TLDR: On Java 9/10, a web app in Tomcat has no access to JAXB even though its reference implementation is present on the class path.
Edit: No, this is not a duplicate of How to resolve java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException in Java 9 - as you can tell by the What I tried section, I already tried the proposed solutions.
The Situation
We have a web app that runs on Tomcat and depends on JAXB. During our migration to Java 9 we opted for adding the JAXB reference implementation as a regular dependency.
Everything worked when launching the app from the IDE with embedded Tomcat, but when running it on a real Tomcat instance, I get this error:
Caused by: java.lang.RuntimeException: javax.xml.bind.JAXBException:
Implementation of JAXB-API has not been found on module path or classpath.
- with linked exception:
[java.lang.ClassNotFoundException: com.sun.xml.internal.bind.v2.ContextFactory]
at [... our-code ...]
Caused by: javax.xml.bind.JAXBException: Implementation of JAXB-API has not been found on module path or classpath.
at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:278) ~[jaxb-api-2.3.0.jar:2.3.0]
at javax.xml.bind.ContextFinder.find(ContextFinder.java:421) ~[jaxb-api-2.3.0.jar:2.3.0]
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:721) ~[jaxb-api-2.3.0.jar:2.3.0]
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:662) ~[jaxb-api-2.3.0.jar:2.3.0]
at [... our-code ...]
Caused by: java.lang.ClassNotFoundException: com.sun.xml.internal.bind.v2.ContextFactory
at jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582) ~[?:?]
at jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:190) ~[?:?]
at java.lang.ClassLoader.loadClass(ClassLoader.java:499) ~[?:?]
at javax.xml.bind.ServiceLoaderUtil.nullSafeLoadClass(ServiceLoaderUtil.java:122) ~[jaxb-api-2.3.0.jar:2.3.0]
at javax.xml.bind.ServiceLoaderUtil.safeLoadClass(ServiceLoaderUtil.java:155) ~[jaxb-api-2.3.0.jar:2.3.0]
at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:276) ~[jaxb-api-2.3.0.jar:2.3.0]
at javax.xml.bind.ContextFinder.find(ContextFinder.java:421) ~[jaxb-api-2.3.0.jar:2.3.0]
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:721) ~[jaxb-api-2.3.0.jar:2.3.0]
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:662) ~[jaxb-api-2.3.0.jar:2.3.0]
at [... our-code ...]
Note:
Implementation of JAXB-API has not been found on module path or classpath.
These are the relevant files in webapps/$app/WEB-INF/lib:
jaxb-api-2.3.0.jar
jaxb-core-2.3.0.jar
jaxb-impl-2.3.0.jar
What is going on here?
What I tried
Adding JARs to Tomca's CLASSPATH
Maybe it helps to add the JARs to Tomcat's class path in setenv.sh?
CLASSPATH=
.../webapps/$app/WEB-INF/lib/jaxb-api-2.3.0.jar:
.../webapps/$app/WEB-INF/lib/jaxb-impl-2.3.0.jar:
.../webapps/$app/WEB-INF/lib/jaxb-core-2.3.0.jar:
.../webapps/$app/WEB-INF/lib/javax.activation-1.2.0.jar
Nope:
Caused by: javax.xml.bind.JAXBException: ClassCastException: attempting to cast
jar:file:.../webapps/$app/WEB-INF/lib/jaxb-api-2.3.0.jar!/javax/xml/bind/JAXBContext.class to
jar:file:.../webapps/$app/WEB-INF/lib/jaxb-api-2.3.0.jar!/javax/xml/bind/JAXBContext.class.
Please make sure that you are specifying the proper ClassLoader.
at javax.xml.bind.ContextFinder.handleClassCastException(ContextFinder.java:157) ~[jaxb-api-2.3.0.jar:2.3.0]
at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:300) ~[jaxb-api-2.3.0.jar:2.3.0]
at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:286) ~[jaxb-api-2.3.0.jar:2.3.0]
at javax.xml.bind.ContextFinder.find(ContextFinder.java:409) ~[jaxb-api-2.3.0.jar:2.3.0]
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:721) ~[jaxb-api-2.3.0.jar:2.3.0]
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:662) ~[jaxb-api-2.3.0.jar:2.3.0]
at de.disy.gis.webmapserver.factory.DefaultWmsRequestFactory.initializeCommandExtractor(DefaultWmsRequestFactory.java:103) ~[cadenza-gis-webmapserver-7.7-SNAPSHOT.jar:7.6]
at de.disy.gis.webmapserver.factory.DefaultWmsRequestFactory.lambda$new$0(DefaultWmsRequestFactory.java:87) ~[cadenza-gis-webmapserver-7.7-SNAPSHOT.jar:7.6]
That's clearly the same class, so apparently it has been loaded by two class loaders. I suspect the system class loader and the app's class loader, but why would loading JAXBContext be delegated to the system class loader once but not always? It almost looks as if the delegation behavior of the app's class loader changes while the program runs.
Adding the module
I don't really want to add java.xml.bind, but I tried it anyways by adding this to catalina.sh:
JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-modules=java.xml.bind"
Doesn't work either, though:
Caused by: java.lang.ClassCastException:
java.xml.bind/com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl
cannot be cast to com.sun.xml.bind.v2.runtime.JAXBContextImpl
at [... our-code ...]
Apart from the different class and stack trace, this is in line with what happened earlier: The class JAXBContextImpl was loaded twice, once from java.xml.bind (must have been the system class loader) and one other time (I assume by the app's loader from the JAR).
Searching for bugs
Searching Tomcat's bug database I found #62559. Could that be the same error?
Adding JAR's to Tomcat's lib
Following advice given on the Tomcat user mailing list, I added the JAXB JARs to Tomcat's CATALINA_BASE/lib directory, but got the same error as in the application's lib folder.
Analysis
First some random facts:
if not given a class loader, JAXBContext::newInstance will use the thread's context class loader when looking for the JAXB implementation - this is the case even if you call newInstance(Class...) (one might mistakenly think it uses the provided class instances' loader)
Tomcat builds a small class loader hierarchy to separate web applications from one another
by not relying on the module java.xml.bind, in Java 9, JAXB classes are not loaded by the bootstrap or system class loader
So here's what happened on Java 8:
we don't pass a class loader to JAXB (oops), so it uses the thread's context class loader
our conjecture is that Tomcat does not explicitly set the context class loader and so it will end up being the same one that loaded Tomcat: the system class loader
that's dandy because the system class loader sees the entire JDK and hence the JAXB implementation included therein
Java 9 enters - the piano stops playing and everybody puts down their scotch:
we added JAXB as a regular dependency and so it is loaded by the web app's class loader
just as on Java 8, JAXB searches the system class loader, though, and that one can't see the app's loader (only the other way around)
JAXB fails to find the implementation and goes belly up
Solution
The solution is to make sure JAXB uses the right class loader. We know of three ways:
call Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader()); but that's not really a good idea
create a context resolver, but that requires JAX-WS and that feels like replacing one evil with another
use the package-accepting variant of JAXBContext::newInstance (Javadoc from Java EE 7) that also takes a class loader and pass the correct loader, although that requires some refactoring
We used the third option and refactored towards the package-accepting variant of JAXBContext::newInstance. Menial work, but fixed the problem.
Note
User curlals provided the critical piece of information, but deleted their answer. I hope it was not because I asked for a few edits. All credit/karma should go to them! #curlals: If you restore and edit your answer, I will accept and upvote it.
Try the following and its dependencies. See a Maven repository for latest version.
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.0.1</version>
</dependency>
It also contains the Java Service Loader descriptors. See Using JAXB in Java 9+
I had this issue using Spring Boot (version 2.2.6) with embedded Tomcat in a specific part of my code where I used a CompletableFuture. The code worked perfectly with Java 8 and related unit test passed in Java 12. The issue appeared only when the application was executed inside Tomcat using Java 11 or 12.
Debugging the problem I discovered the issue was related to the fact that a different ClassLoader is used inside the CompletableFuture's Runner.
// here Thread.currentThread().getContextClassLoader().getClass()
// returns org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader
return CompletableFuture.runAsync(() -> {
// here returns jdk.internal.loader.ClassLoaders$AppClassLoader
});
The second ClassLoader is not able to load the JAXB classes. This behavior seems to be present only with Java 9+, indeed before Java 9 ForkJoinPool.common() returned an Executor with a ClassLoader of your main Thread, but after Java 9 it returns an executor with system ClassLoader.
Since the CompletableFuture.runAsync() method accepts an Executor as second parameter, it is possible to set the desired Executor in the code. Here an example of a possible solution.
First, define a proper ForkJoinWorkerThreadFactory:
public class JaxbForkJoinWorkerThreadFactory implements ForkJoinWorkerThreadFactory {
private final ClassLoader classLoader;
public JaxbForkJoinWorkerThreadFactory() {
classLoader = Thread.currentThread().getContextClassLoader();
}
#Override
public final ForkJoinWorkerThread newThread(ForkJoinPool pool) {
ForkJoinWorkerThread thread = new JaxbForkJoinWorkerThread(pool);
thread.setContextClassLoader(classLoader);
return thread;
}
private static class JaxbForkJoinWorkerThread extends ForkJoinWorkerThread {
private JaxbForkJoinWorkerThread(ForkJoinPool pool) {
super(pool);
}
}
}
Then pass an Executor using that factory to the runAsync() method:
return CompletableFuture.runAsync(() -> {
// now you have the right ClassLoader here
}, getJaxbExecutor());
private ForkJoinPool getJaxbExecutor() {
JaxbForkJoinWorkerThreadFactory threadFactory = new JaxbForkJoinWorkerThreadFactory();
int parallelism = Math.min(0x7fff /* copied from ForkJoinPool.java */, Runtime.getRuntime().availableProcessors());
return new ForkJoinPool(parallelism, threadFactory, null, false);
}
TL;DR
A simple solution that worked for me is just to upgrade the Hibernate version.
I used Hibernate with version of 5.2.10.Final and they rely on JAXB. However, when I replaced undertow with Tomcat, that dependency went missing. I found this issue but none of the answers really solved my issue. When I found that jpa-model-gen was the issue I quickly realized, that it is the onlt Hibernate dependecy only that is looking for JAXB. Updating the hibernate version to a higher one solved my problem.
I too have experienced with the similar issue while using JAXB, i.e
Implementation of JAXB-API has not been found
which occurs randomly and it was harder to reproduce. Fortunately I had found a system environment where the above error is continuous, while other environments it worked smoothly.
Observation
With the extensive research into this issue, I found a classloader issue that causes this problem. Further I noticed,
JAXB implementation is visible to ParallelWebappClassLoader, a classloader present in Tomcat server
Sometimes it is not visible to jdk internal classloaders like AppClassLoader,(Even though it was visible for many cases)
Solution
JAXBContext object is thread-safe (while marshaller/unmarshaller is not) and can be re-used once initiated. Therefore,
I found a thread that works with ParallelWebappClassLoader (i.e that given thread's context class loader is ParallelWebappClassLoader) and created JAXBContext there and stored in a map for later usage
Retrieved the stored JAXBContext whenever necessary (other threads that uses different class loaders) and carried out marshall/unmarshall tasks. That saved the day for me :)
Related
When deploying a Springboot maven project(version 2.3.4.RELEASE) to an external Tomcat container,official guide says you need to mark the "spring-boot-starter-tomcat" dependency as provided ,but actually even if without doing that,the final war package which contains lib like "spring-boot-starter-tomcat","tomcat-embed-core" and "tomcat-embed-websocket" also works fine in tomcat8.5.54 or tomcat 9.0 ,so I am confused about that,"Do we really need set spring-boot-starter-tomcat as provided or not?" ,anyone could explains why?
"Traditional Deployment"
You don't want to have multiple versions of the same classes on the classpath. This can lead to many errors during runtime. For example, if you have a
public class MyServlet implements javax.servlet.Servlet
and you package both MyServlet.class and javax.servlet-api.jar (which contains javax.servlet.Servlet) into your application, you might get an error:
Servlet class MyServlet is not a javax.servlet.Servlet
What happens is: when the application classloader loads MyServlet, it looks for javax.servlet.Servlet in the application first and it finds it in javax.servlet-api.jar. The server compares this class with the one loaded by the server's classloader and concludes that they differ, since classes from different JARs are not equal. If the application is shipped without javax.servlet-api.jar this does not happen: the classloader does not find javax.servlet.Servlet in its own classpath, so it looks in the parent classloader.
Remark: This example can not actually be reproduced on Tomcat. Due probably to many incorrectly packaged applications the WebappClassLoader has an exception to the class loading rule: special classes such as those starting with javax.servlet or org.apache.tomcat are always loaded from the server's classloader (cf. the source code for a list of these exceptions).
TL;DR: due to the remark above, leaving spring-boot-starter-tomcat in the compile scope probably will do no harm, but it is a risk not worth taking.
When I try to build my Enterprise Project in Netbeans with ant, i got this exception:
warning: Supported source version 'RELEASE_6' from annotation
processor
'org.eclipse.persistence.internal.jpa.modelgen.CanonicalModelProcessor'
less than -source '1.8'
Note: Creating static metadata factory ...
An annotation processor threw an uncaught exception.
Consult the following stack trace for details.
java.lang.ClassFormatError: Absent Code attribute in method that is not native or abstract in class file
javax/persistence/PersistenceException
I only have some entityclasses in my ejb module.
This error is reported as a bug in NetBeans (with a resolved status as it turns out to be considered as a "user error"). Below is the main comment that explains the problem and gives the solution:
"ClassFormatError: Absent Code attribute in method..." is always indication that javaee-api-6.0.jar is used for runtime execution. The jar contains only method signatures (method bodies are stripped) and is suitable only for compilation. In future versions of javac there might be better error message.
The problem here is that javaee-api-6.0.jar is on classpath before EclipseLink jars and when EclipseLink annotation processor is started classes from javaee-api-6.0.jar are used instead of classes from EclipseLink. First thing is that javaee-api-6.0.jar should be removed from classpath of EJB project - it should not be needed as EJB project has an Application server selected and the project takes EE 6 APIs from that server.
As I understand the comment, you should try removing any reference to javaee-api-6.0.jar (which contains only "dummy" classes) from the classpath of the project, because the EclipseLink library should provide the correct classes used by the annotation processor.
Also, this question seems to tackle the same issue.
when you use the insert code and automatic bean creation it adds the Java EE 6
API library . I deleted the library and add the Java EE 7 API.
Websphere 8.0.0.11
Hibernate 4.2.21.Final
I have found many questions about this same problem but none of them worked for me.
If I deploy the application in Websphere it works OK.
However we have defined a shared library that contains all the third party libraries (spring, hibernate, javassist, etc) so that our WARs are thinner.
This way during deployment we associate our thin WAR against that Websphere shared library.
The point is that when we deploy the application this way the ClassCastException Hibernate exception _$$_javassist_856 cannot be cast to javassist.util.proxy.Proxy is thrown.
I have checked the loaded jars in the websphere console and can only see one javassist jar (3.18.1-GA) in the classpath.
Why could this be happening?
UPDATE
I have also tried using PARENT_FIRST and PARENT_LAST class loading.
UPDATE 2
I just found out that Websphere is loading its own javassist jar:
URL location = ProxyFactory.class.getProtectionDomain().getCodeSource().getLocation();
logger.info("{}", location);
It prints: file:/opt/IBM/WebSphere/AppServer/plugins/javassist.jar
After trying pretty much everything I found on S.O. without any success I decided to downgrade Hibernate to version 4.1.12.Final. This is the maximum 4.x version compatible with Websphere 8.x.
The problem is that Javassist leaves traces in its generated code. With Javassist on the class path twice, its classes are loaded twice. Two types are however only equal if they have the same name and are loaded by the same class loader. In your case, the generated class resolves its Javassist dependeny to a type that is loaded by your application class loader while your code is casting the instance to the Javassist type that is loaded by the Websphere class loader (or the other way around).
Are you sharing any Hibernate dependencies between applications? Try to not use any shared libraries related to Hibernate in your application to avoid this.
Getting following exception in Jboss 5.0 EAP but it work fine in JBoss 5.1 GA.
we are using POI 3.7 and jars included are
poi-3.7.jar
poi-ooxml-schemas.jar
poi-ooxml.jar
The stack trace is
ERROR [org.apache.catalina.core.ContainerBase.[jboss.ueb].[localhost].[fesbcon-Fig].[Faces Servlet]]
3;13;44.4g3pM (http-0.0.0.0-8280-1) Servlet.service() -For servlet Faces Servlet threu exception
java.lang.NoClassDe-FFoundError: Could not initialize class org.apache.poi.POIXMLDocument
at org.apache.poi.ss.usermodel.HorkbookFactory.create(HorkbookFactory.java:62)
at com.-Ferguson.esb.con-Fig.controller.AssociationsExcelUploadController.submit(Unknoun Source)
at sun.re-Flect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.re-Flect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.re-Flect.DelegatingMethodAccessorImpl.invoke(Delegating?ethodAccessorImpl.java:25)
at java.lang.re-Flect.Method.invoke(Method.java:597)
at org.apache.my-Faces.el.MethodBindingImpl.invoke(MethodBindingImpl.java:132)
at org.apache.my-Faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:61)
Please Advise how to solve this issue in JBoss 5.0 EAP
It looks like you're application is throwing the exception you're seeing because an Apache XMLBeans JAR or class is not present when running under JBoss 5.0. It seems Apache POI is trying to load the class org.apache.xmlbeans.XMLOptions but it cannot find this class.
The message Could not initialize class SomeClass indicates that the JVM has twice tried and failed to load and statically initialize the class SomeClass. In this case, the class in question is org.apache.poi.POIXMLDocument.
Static initialization for a class consists of statically initializing its superclass, assigning values to all static fields and running all static initializer blocks. The POIXMLDocument class has a few static String constants, which won't cause any problem, but no static initializer. It is however a subclass of POIXMLDocumentPart, which is a subclass of Object and which has the following static initialization code:
private static POILogger logger = POILogFactory.getLogger(POIXMLDocumentPart.class);
public static final XmlOptions DEFAULT_XML_OPTIONS;
static {
DEFAULT_XML_OPTIONS = new XmlOptions();
DEFAULT_XML_OPTIONS.setSaveOuter();
DEFAULT_XML_OPTIONS.setUseDefaultNamespace();
DEFAULT_XML_OPTIONS.setSaveAggressiveNamespaces();
}
This static initialization will fail if the JVM cannot load all of the POILogger, POILogFactory and XmlOptions classes.
The POILogger and POILogFactory classes are both imported from the package org.apache.poi.util.POILogFactory, and both classes are contained within poi-3.7.jar, so they're not the problem here. So, by elimination, it seems the XmlOptions class, imported from org.apache.xmlbeans.XmlOptions, must be missing.
I found this XMLOptions class within xbean.jar contained within the lib folder of xmlbeans-2.6.0.zip downloadable from one of the mirrors here.
It seems likely to me that adding this JAR will fix the problem on JBoss 5.0 EAP. However, I'm aware you said your application works fine in JBoss 5.1 GA, which implies to me that JBoss 5.1 GA contains a copy of this JAR whereas 5.0 EAP doesn't. As a result I'm not sure what the best way to fix this problem is. I'd be hesitant to add this XMLBeans JAR to your application as doing so may cause issues when you run it under JBoss 5.1. I don't know whether there's a way of adding extra 'library' JARs to JBoss 5.0, though - perhaps that's worth looking at?
I had the same error running on JBoss 8.2 (WildFly 8.2.0.Final) with Apache POI 3.14
Error:
Caused by: java.lang.NoClassDefFoundError: Could not initialize class org.apache.poi.POIXMLTypeLoader
I fixed this by including the latest xmlbeans-2.6.0 jar (I previously had 2.4.0) into my deployment which is included with the Apache POI 3.14 distribution in the ooxml-lib folder.
Placing the Following jars in classpath will do the trick:
dom4j-1.6.1.jar
poi-3.9-20121203.jar
poi-ooxml-3.9-20121203.jar
poi-ooxml-schemas-3.9-20121203.jar
xmlbeans-2.3.0.jar
By adding only the xbean.jar alone, you can not solve the problem. It will continue complaining class not found. What I did is import not only xbean.jar but also other jar files which are listed under lib folder of xmlbeans-2.5.0
I am playing with Weld-SE. I have a project which has pluggable modules. With one module, weld works. When other module, which uses weld-servlet-core, is put on classpath (with it's deps), CDI stops working, giving errors like "Unable to resolve dependency XY" or (after removing that #Inject) this:
Exception in thread "main" org.jboss.weld.exceptions.DeploymentException: WELD-001417 Enabled interceptor class <class>org.jboss.weld.environment.se.jpa.JpaTransactionInterceptor</class> in jar:file:/mnt/ssd1/data/.m2/repository/org/jboss/jawabot/JawaBot-core/2.0.0-SNAPSHOT/JawaBot-core-2.0.0-SNAPSHOT.jar!/META-INF/beans.xml#11 is neither annotated #Interceptor nor registered through a portable extension
at org.jboss.weld.bootstrap.Validator.validateEnabledInterceptorClasses(Validator.java:466)
...
Although the class has #Interceptor (and the same class works fine with the other module).
When I debug WeldBootstrap#startContainer(Environment environment, Deployment deployment),
deployment.beanDeploymentArchive.beanClasses contains the interceptor
("org.jboss.weld.environment.se.jpa.JpaTransactionInterceptor").
I've checked the dependencies and TattleTale report, they all seem fine.
Any ideas what should I look at / try next?
The project is at http://ondrazizka.googlecode.com/svn/trunk/bots/JawaBot/branches/2.0/ To see the error, run mvn dependency:copy-dependencies java -cp ... org.jboss.jawabot.JawaBotApp Or simply run the web module in NetBeans.
Thanks, Ondra
Update: JIRA: https://issues.jboss.org/browse/WELD-940
Also, I found that it might be caused by a bug in Weld's scanning extension, <weld:scan>.
It was caused by using Weld's scanning extension, <weld:scan>:
An <includes> element contained package wildcard which resulted in matching classes in both "packages" (which is .jar in CDI / Weld's terminology IIUC).
I am not sure if I used it improperly or it's a bug.
See JIRA: https://issues.jboss.org/browse/WELD-940