In the past few days, Jetty (9.2) was used to deploy a company's web project. The project was originally deployed on the Tomcat server. Everything is normal, but after it is deployed to jetty, an error is reported. The key error message is"Java. Lang. noclassdeffounderror: cocould not initialize class org. Apache. tomcat. JDBC. Pool. datasource"
The project uses the Tomcat JDBC connection pool where there are two jar package tomcat-jdbc.jar and tomcat-juli.jar, which are the maven dependency of the former, and the latter is also the log abstraction layer of Tomcat, the Tomcat server comes with this jar in the bin directory of atat_home. the error is determined based on the exception information due to loading and initializing Org. apache. tomcat. JDBC. pool. datasource. To make it easier to analyze the problem, I created a simple web project that only relies on these two jar packages. deploy to the same jetty server. An error is returned."Java. util. serviceconfigurationerror: org. Apache. Juli. Logging. Log: provider org. Eclipse. Jetty. Apache. jsp. julilog not a subtype"
View Source CodeOrg. Eclipse. Jetty. Apache. jsp. julilog is clearly org. Apache. Juli. Logging. LogBut why is this error reported?Java. Lang. noclassdeffounderrorAndJava. util. serviceconfigurationerrorIt can be determined that the problem is caused by class loading. According to the understanding of class loading, the class objects loaded by different class loaders for the same class are different. therefore, it may be because the jetty server uses different classloaders to load two tired instances, and the inheritance relationship does not exist. viewJava. util. serviceconfigurationerrorRelated API documentation found that this error occurred when serviceloader loaded the service provider. view the source code of serviceloader and find such a section.
Private s nextservice () {If (! Hasnextservice () throw new nosuchelementexception (); string Cn = nextname; nextname = NULL; Class <?> C = NULL; try {c = Class. forname (CN, false, loader);} catch (classnotfoundexception X) {fail (service, "provider" + CN + "not found");} If (! Service. isassignablefrom (c) {fail (service, "provider" + CN + "not a subtype"); // the error message I encountered is exactly here .} try {s p = service. cast (C. newinstance (); providers. put (CN, P); Return P;} catch (throwable X) {fail (service, "provider" + CN + "cocould not be instantiated", x );} throw new error (); // This cannot happen}
To verify my speculation, I wrote a small program to test the code. The main code is as follows:
Public class main {public static void main (string [] ARGs) throws classnotfoundexception {final classloader systemclassloader = classloader. getsystemclassloader (); Final customclassloader = new customclassloader ("target/classes", "Custom loader", systemclassloader ); // The path of the first parameter is the compiling path of the class. // use the system class loader to load child // because child depends on parent, the system class loader automatically loads parent, this behavior is the same class as jetty's webappclasscloader <?> Child = Class. forname ("child", true, systemclassloader); // the class of the parent class is directly returned if it is not loaded again because it has been previously loaded. <?> Parent = Class. forname ("child", true, systemclassloader); // use a user-defined loader to load the child. // the User-Defined loader first tries to load the child, use the parent loader class after failure <?> Customchild = Class. forname ("child", true, customclassloader); Class <?> Customparent = Class. forname ("parent", true, customclassloader); // do not reload the two classes that are also loaded using the system loader. The inheritance relationship is normal. out. println ("parent. isassignablefrom (child) = "+ parent. isassignablefrom (child); // true // The Child loaded by the user-defined loader is not the child subclass of the parent loaded by the system class loader. out. println ("parent. isassignablefrom (customchild) = "+ parent. isassignablefrom (customchild); // false // The two classes loaded by the user-defined loader are also used, and the integration relationship is normal. out. println ("customparent. isassignablefrom (customchild) = "+ customparent. isassignablefrom (customchild); // true }}
In this Code, Child is a subclass of parent. this simple test proves my speculation. view the document and find that the JSP Engine of jettty in version 9.2 uses tomcat. in jetty's lib, we can find the following jar
Org. Eclipse. Jetty. apache-jsp-9.2.3.v20140905.jar
Org.eclipse.jetty.orbit.org. Eclipse. jdt. core-3.8.2.v20130121.jar
Org. mortbay. Jasper. apache-el-8.0.9.M3.jar
Org. mortbay. Jasper. apache-jsp-8.0.9.M3.jar
The two jar files starting with org. mortbay are Apache's JSP implementation classes, includingOrg. Apache. Juli. LoggingThis package, Org. Eclipse. Jetty. apache-jsp-9.2.3.v20140905.jarThis jar provides the specific implementation of logging, and finally loads through serviceloader. The problem lies here, because my project contains tomcat-juli.jar which also containsOrg. Apache. Juli. LoggingThis package.
I checked jetty's document about class loading and found jetty loaded classes for each deployed Web application using a separate webappclassloader instance. generally, the user-defined class loader is delegated to the parent loader first (generally the system class loader, which can be passed through classloader. getsystemclassloader (), and then try to load the class by yourself. however, jetty's webappclassloader is the opposite. In addition to system and server classes (What are system and server classes that can be viewed in the document), jetty will first try to load it by itself before entrusting the parent loader.
According to this behavior, we can basically confirm that the server will use its own class loader to load the above class (Org. apache. juli. logging. log and Its Implementation Org. eclipse. jetty. apache. JSP. julilog ),It will be used during application deploymentWebappclassloader loads org. Apache. Juli. Logging. log in the application Lib. The loading process is like this. The webappclassloader instance LoadsOrg. Apache. tomcat. JDBC. Pool. datasource,It depends on Org. apache. juli. logging. log, first try to load by yourself, so it will be loaded to this class from the application Lib, and when trying to load its implementation, it will find that the application lib does not exist, and then delegate it to the parent class loader, that is, the jetty server loader is successfully loaded to Org. eclipse. jetty. apache. JSP. in this way, two different loader instances are used to load the subclass and parent classes. According to the previous test results, the inheritance relationship between the two classes is invalid. therefore, an error occurs.
The cause of the problem is clear. How can this problem be solved?
Solution 1: In Maven configuration, change the tomcat-Juli dependency scope to provided, and the jetty server has provided. in this way, the webappclassloader tries to load Org. eclipse. jetty. apache. JSP. julilog will fail, and then delegate the parent class loader, so Org. apache. juli. logging. log and Its Implementation Org. eclipse. jetty. apache. JSP. the two julilog classes are loaded by the same loader.
Solution 2: change the priority of the webappclassloader parent class loader to give priority to the parent class loader. For specific configuration methods, refer to the document. The goal is to callSetparentloaderpriority (true)
The two solutions can also solve the problems in the original project. But why is the error information of the simple Web project used for testing different from that of the original project?
Return to the error message of the original project,"Java. Lang. noclassdeffounderror: cocould not initialize class org. Apache. tomcat. JDBC. Pool. datasource"This error is caused by the failure to initialize the class during loading.Org. Apache. tomcat. JDBC. Pool. datasource.Private Static final log = logfactory. getlog (datasource. Class );Only this code needs to be initialized after the class is loaded. Tracking this statement will eventually enter the one mentioned above.NextserviceMethod, so the root cause error is still described above.
So far, the problem has been successfully explained and solved.
Summary:
Knowledge points involved in this article
1. class loading mechanism of Java Virtual Machine
2. assumerviceprovider Loading Mechanism
3. Java class initialization process
4. jetty Server Configuration
Handling jetty class Loading Problems