Problem
In a project, We use a stub jar file generated by Axis2. It always throw: java.lang.NoSuchFieldError: localClientNameTracker
While in another project, everything is ok using same jar file. No exception is thrown.
CLUE
- Guess it's because environment, but has no idea about the cause.
- Clean the project and re-add all the lib in Eclipse, the issue disappear
- If we put axis1 before axis2 on classpath, the issue disappear but the project broken
- If we put axis2 before axis1 on classpath, the project ok but the issue occur
Suspect
It seem because of Axis1 and axis2 jar file conflict, we need dig into detail to find out which jar cause this conflict.
diagnosis
- Search the internet, try to find the Axis1 and Axis2 jar conflict, do not any any hit
- Search the internet, try to understanding class loader
-
- prints a trace of the class loading process on the console in Eclipse . From an article on IBM java programming dynamics series, part1 Java classes and class loadin g.
- In Eclipse, we append Eclipse console to the File , Run Configurations -> Common -> Standard Input and Output
- Use Beyond compare to compare the two trace file, one is success trace, the other is fail trace
Cause
- localClientNameTracker is a field in a parent class called ServerReuqest (x.jar)
- There is happened another jar file(y.jar) which has same class with exactly same class name and package name, while they are different version.
- When add y.jar file before x.jar into classpath, then JVM search y.jar first and load ServerRequest from y.jar file. Then NoSuchFieldError occur
- When add x.jar file before y.jar into classpath, then JVM search x.jar first and load ServerRequest from x.jar file, everything is OK.
Solution1
According to problem solving policy : Try to find another ways which achieve same target but do not this problem. So our first solution is remove two version of same classes. Only keep a latest version of ServerRequest
Solution2
How about keep two version of same jar file. According to Managing Component Dependencies Using ClassLoaders
We can let each jar file have one classloader, see the diagram
The policy of our class loader
- Each Jar file has own class loader, JarClassLoader, which extended from URLClassLoader and load class from a single jar file. If can not load from jar, delegate to AppJarClassLoader.
- Add a Customer class loader (AppJarClassLoader ) to boot class path as System class loader, which iterate each JarClassLoader. If can not load from jars, delegate to the parent.
- If no parent delegation, NoClassDefError may be occur.
postmortem
ClassLoader
- Startup (Main class is loaded by bootstrap class loader, In Sun JVM, Default loaded by AppClassLoader)
The Java virtual machine starts up by creating an initial class, which is specified in an implementation-dependent manner, using the bootstrap class loader (§5.3.1) . The Java virtual machine then links the initial class, initializes it, and invokes its
public
class methodvoid
main(String[])
. The invocation of this method drives all further execution. Execution of the Java virtual machine instructions constituting themain
method may cause linking (and consequently creation) of additional classes and interfaces, as well as invocation of additional methods.
- Namespaces(runtime package )
At run time, a class or interface is determined not by its name alone, but by a pair: its fully qualified name and its defining class loader. Each such class or interface belongs to a single runtime package . The runtime package of a class or interface is determined by the package name and defining class loader of the class or interface.
- Same Loader (L load c, if c reference d, then L will load d)
Creation of a class or interface C denoted by the name N consists of the construction in the method area of the Java virtual machine (§3.5.4) of an
implementation-specific internal representation of C. Class or interface creation is triggered by another class or interface D, which references C through its runtime constant pool. Class or interface creation may also be triggered by D invoking methods in certain Java class libraries (§3.12) such as reflection.
The Java virtual machine uses one of three procedures to create class or interface C denoted by N:
If N denotes a nonarray class or an interface, one of the two following methods is used to load and thereby create C :
If D was defined by the bootstrap class loader, then the bootstrap class loader initiates loading of C (§5.3.1).
If D was defined by a user-defined class loader, then that same user-defined class loader initiates loading of C (§5.3.2).
Otherwise N denotes an array class. An array class is created directly by the Java virtual machine (§5.3.3), not by a class loader. However, the defining class loader of D is used in the process of creating array class C.
ClassNotFoundException NoClassDefFoundError
ClassNotFoundException
but no definition for the class with the specified name could be found.
NoClassDefFoundError
is another common exception thrown by the class loader during the loading phase. The JVM specification definesNoClassDefFoundError
as followsEssentially, this means that a
NoClassDefFoundError
is thrown as a result of a unsuccessful implicit class load.
Java
-verbose:class
Tomcat Classloader
Finding a way out of ClassLoader , the author said we have at least 3 choices:
the system (also referred to as the application ) classloader, the current classloader, and the current thread context classloader. The question above refers to the latter. Which classloader is the right one?
If you choose the system classloader
As soon as you move your code into an Enterprise JavaBean, a Web application, or a Java Web Start application, things are guaranteed to break.
So let's digg into Tomcat to see how a web concainter using customized class loader.
So each webapp has own class loader. Tomcat implements it by controlling the App entries: Filter, Listener, Servlet Listener Each StandardContext represent an App
Filter
Each StandardWrapper is an individual servlet definition. StandardWrapperValue is a pipeline implements the default basic behavior for the StandardWrapper
Servlet
WebAppClassLoader
It's very important in web class loader, it verfiy the jar file, if it's servlet jar, discard this jar file. So all the interface javax.servlet is loaded from parent class loader while all the implementation is loaded from App class loader
OSGI
We do not need define own class loader, Other choice is OSGI
http://www.slideshare.net/stephanj/intro-to-osgi