Packaging Applications"
RebeccaLai (talk | contribs) m |
|||
(23 intermediate revisions by one other user not shown) | |||
Line 1: | Line 1: | ||
{{ZKDevelopersReferencePageHeader}} | {{ZKDevelopersReferencePageHeader}} | ||
__TOC__ | __TOC__ | ||
+ | |||
+ | =Package Single Version in WAR= | ||
+ | ZK doesn't support running 2 different versions in a single web application (WAR). Mixing Compact Edition/official version with evaluation version is not allowed. Please be sure you package only one single version of ZK JAR into a WAR. | ||
+ | |||
+ | For example, if you include '''zul:9.6.0''' and '''zkex:9.6.3''', then you will see the following messages when starting a server: | ||
+ | <syntaxhighlight> | ||
+ | 2023-03-02 09:21:44 [INFO ] ConfigParser:116 - Ignore jar:file:/Users/yourName/.m2/repository/org/zkoss/zk/zkex/9.6.3/zkex-9.6.3.jar!/metainfo/zk/config.xml | ||
+ | Cause: ZK version must be 9.6.3 or later, not 9.6.0 | ||
+ | ... | ||
+ | Your ZK binary is being altered and may not work as expected. Please contact us at info@zkoss.org for assistance. | ||
+ | </syntaxhighlight> | ||
=Package ZK As a Shared Library = | =Package ZK As a Shared Library = | ||
− | This is a not-recommended practice. Since ZK stores its | + | This is a not-recommended practice. Since ZK stores its configurations (parsed from <code>zk.xml</code>) as a static object, all your zk applications in the same application server will have the same configurations. If you change a configuration (library property), it will affect all zk applications. |
= Package as EAR = | = Package as EAR = | ||
+ | An EAR file contains one or more JAR and WAR. | ||
==Notice for Form Binding == | ==Notice for Form Binding == | ||
+ | {{versionSince | 9.6.3}} | ||
+ | |||
+ | If you package multiple WAR files and EJB as an EAR file and use form binding on an entity bean packaged in EJB jar, you need to arrange zkbind jar in the following way, or you might encounter the error: | ||
+ | |||
+ | <code>java.lang.NoClassDefFoundError: org/zkoss/bind/proxy/FormProxyObject</code> | ||
+ | |||
+ | # put <code>zkbind-api.jar</code> and <code>javassist.jar</code> in a shared library | ||
+ | # package <code>zkbind-impl.jar</code> in a WAR. | ||
+ | # make sure a WAR doesn't contain <code>zkbind-api.jar</code> | ||
+ | |||
+ | Check [https://github.com/hawkchen/ear-demo this EAR demo project] for details. | ||
+ | |||
+ | '''myapp.ear''' | ||
+ | <syntaxhighlight> | ||
+ | lib/javassist.jar | ||
+ | lib/zkbind-api.jar | ||
+ | lib/zk.jar (and its dependent jar) | ||
+ | |||
+ | myapp-ejb.jar (MyEntity.java) | ||
+ | |||
+ | myapp.war/WEB-INF/lib/zkbind-impl.jar | ||
+ | myapp.war/WEB-INF/lib/other ZK jars (but NO zkbind-api.jar here) | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | If you don't use form binding or don't use form binding on a bean in an EJB jar, then you don't need above arrangement. | ||
+ | |||
+ | === Underlying Details === | ||
+ | In enterprise application server, each module has its own class loader like: | ||
+ | [[File:HierachicalClassLoader.jpg|center]] | ||
+ | |||
+ | When ZK creates a proxy object based on EJB class with javassist, it uses the target object's class loader. In this case, it's EJB's class loader. | ||
+ | |||
+ | '''The related code in javassist:3.28.0-GA''' | ||
+ | <syntaxhighlight line lang='java' line highlight=''> | ||
+ | public class ProxyFactory { | ||
+ | ... | ||
+ | protected ClassLoader getClassLoader0() { | ||
+ | ClassLoader loader = null; | ||
+ | if (superClass != null && !superClass.getName().equals("java.lang.Object")) | ||
+ | loader = superClass.getClassLoader(); | ||
+ | </syntaxhighlight> | ||
+ | * Line 6: <code>superClass</code> is the EJB bean to be proxied. | ||
+ | |||
+ | Since parent class loader cannot know the classed loaded by its child class loader, EarClassLoader doesn't know the classes loaded by WebappClassLoader. So it will throw <code>java.lang.NoClassDefFoundError: org/zkoss/bind/proxy/FormProxyObject</code> because zkbind.jar is in a WAR. | ||
+ | |||
+ | Hence, that's the reason you need to put zkbind-api.jar in <code>/lib</code> (out of WAR) in order to be loaded by EarLibClassLoader. | ||
+ | |||
+ | |||
+ | (Related issues: [https://tracker.zkoss.org/browse/ZK-2932 ZK-2932], [https://tracker.zkoss.org/browse/ZK-3135 ZK-3135] , [https://tracker.zkoss.org/browse/ZK-4554 ZK-4554], [https://tracker.zkoss.org/browse/ZK-5256 ZK-5256]) | ||
+ | |||
+ | ==== Class Loader Hierarchy in Glassfish 6==== | ||
+ | '''WAR''' | ||
+ | |||
+ | WebappClassLoader (load classes under WEB-INF) ➡️<br> | ||
+ | EarClassLoader ➡️<br> | ||
+ | org.glassfish.internal.api.DelegatingClassLoader@6144db94 ➡️<br> | ||
+ | EarLibClassLoader (load jar in lib) ➡️<br> | ||
+ | org.glassfish.internal.api.DelegatingClassLoader@76bcc8a2 ➡️<br> | ||
+ | java.net.URLClassLoader@42561fba ➡️<br> | ||
+ | APIClassLoader ➡️<br> | ||
+ | jdk.internal.loader.ClassLoaders$PlatformClassLoader@80937 | ||
+ | '''EJB''' | ||
+ | EarClassLoader ➡️<br> | ||
+ | org.glassfish.internal.api.DelegatingClassLoader@6144db94 ➡️<br> | ||
+ | EarLibClassLoader ➡️<br> | ||
+ | org.glassfish.internal.api.DelegatingClassLoader@76bcc8a2 ➡️<br> | ||
+ | java.net.URLClassLoader@42561fba ➡️<br> | ||
+ | APIClassLoader ➡️<br> | ||
+ | jdk.internal.loader.ClassLoaders$PlatformClassLoader@80937 | ||
{{ZKDevelopersReferencePageFooter}} | {{ZKDevelopersReferencePageFooter}} |
Latest revision as of 10:02, 2 February 2024
Package Single Version in WAR
ZK doesn't support running 2 different versions in a single web application (WAR). Mixing Compact Edition/official version with evaluation version is not allowed. Please be sure you package only one single version of ZK JAR into a WAR.
For example, if you include zul:9.6.0 and zkex:9.6.3, then you will see the following messages when starting a server:
2023-03-02 09:21:44 [INFO ] ConfigParser:116 - Ignore jar:file:/Users/yourName/.m2/repository/org/zkoss/zk/zkex/9.6.3/zkex-9.6.3.jar!/metainfo/zk/config.xml
Cause: ZK version must be 9.6.3 or later, not 9.6.0
...
Your ZK binary is being altered and may not work as expected. Please contact us at info@zkoss.org for assistance.
This is a not-recommended practice. Since ZK stores its configurations (parsed from zk.xml
) as a static object, all your zk applications in the same application server will have the same configurations. If you change a configuration (library property), it will affect all zk applications.
Package as EAR
An EAR file contains one or more JAR and WAR.
Notice for Form Binding
Since 9.6.3
If you package multiple WAR files and EJB as an EAR file and use form binding on an entity bean packaged in EJB jar, you need to arrange zkbind jar in the following way, or you might encounter the error:
java.lang.NoClassDefFoundError: org/zkoss/bind/proxy/FormProxyObject
- put
zkbind-api.jar
andjavassist.jar
in a shared library - package
zkbind-impl.jar
in a WAR. - make sure a WAR doesn't contain
zkbind-api.jar
Check this EAR demo project for details.
myapp.ear
lib/javassist.jar
lib/zkbind-api.jar
lib/zk.jar (and its dependent jar)
myapp-ejb.jar (MyEntity.java)
myapp.war/WEB-INF/lib/zkbind-impl.jar
myapp.war/WEB-INF/lib/other ZK jars (but NO zkbind-api.jar here)
If you don't use form binding or don't use form binding on a bean in an EJB jar, then you don't need above arrangement.
Underlying Details
In enterprise application server, each module has its own class loader like:
When ZK creates a proxy object based on EJB class with javassist, it uses the target object's class loader. In this case, it's EJB's class loader.
The related code in javassist:3.28.0-GA
1 public class ProxyFactory {
2 ...
3 protected ClassLoader getClassLoader0() {
4 ClassLoader loader = null;
5 if (superClass != null && !superClass.getName().equals("java.lang.Object"))
6 loader = superClass.getClassLoader();
- Line 6:
superClass
is the EJB bean to be proxied.
Since parent class loader cannot know the classed loaded by its child class loader, EarClassLoader doesn't know the classes loaded by WebappClassLoader. So it will throw java.lang.NoClassDefFoundError: org/zkoss/bind/proxy/FormProxyObject
because zkbind.jar is in a WAR.
Hence, that's the reason you need to put zkbind-api.jar in /lib
(out of WAR) in order to be loaded by EarLibClassLoader.
(Related issues: ZK-2932, ZK-3135 , ZK-4554, ZK-5256)
Class Loader Hierarchy in Glassfish 6
WAR
WebappClassLoader (load classes under WEB-INF) ➡️
EarClassLoader ➡️
org.glassfish.internal.api.DelegatingClassLoader@6144db94 ➡️
EarLibClassLoader (load jar in lib) ➡️
org.glassfish.internal.api.DelegatingClassLoader@76bcc8a2 ➡️
java.net.URLClassLoader@42561fba ➡️
APIClassLoader ➡️
jdk.internal.loader.ClassLoaders$PlatformClassLoader@80937
EJB
EarClassLoader ➡️
org.glassfish.internal.api.DelegatingClassLoader@6144db94 ➡️
EarLibClassLoader ➡️
org.glassfish.internal.api.DelegatingClassLoader@76bcc8a2 ➡️
java.net.URLClassLoader@42561fba ➡️
APIClassLoader ➡️
jdk.internal.loader.ClassLoaders$PlatformClassLoader@80937