Why is Tomcat looking for jaxb jars in the wrong places?

Viewed 43

I'm running Tomcat 9.0.64 in Spring Boot 2.7.1. I'm including 3 war files as classpath resources and adding them to Tomcat at runtime:

@Bean
public TomcatServletWebServerFactory servletContainerFactory() {
    return new TomcatServletWebServerFactory() {

        @Override
        protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
            new File(tomcat.getServer().getCatalinaBase(), "webapps").mkdirs();

            try {
                tomcat.addWebapp("artemis-plugin", new ClassPathResource("artemis-plugin.war")
                        .getFile().toString());
                tomcat.addWebapp("activemq-branding", new ClassPathResource("activemq-branding.war")
                        .getFile().toString());
                tomcat.addWebapp("artemis-console", new ClassPathResource("artemis-console.war")
                        .getFile().toString());
            }
            catch (IOException e) {
                throw new RuntimeException(e);
            }
            return super.getTomcatWebServer(tomcat);
        }
    };
}

At startup, I'm getting this in the log:

Failed to scan [file:/Users/jeremyross/.m2/repository/com/sun/xml/bind/jaxb-core/2.3.0/jaxb-api.jar] from classloader hierarchy
Failed to scan [file:/Users/jeremyross/.m2/repository/com/sun/xml/bind/jaxb-impl/2.3.0/jaxb-core.jar] from classloader hierarchy

Here's an example stack trace:

java.io.IOException: java.lang.reflect.InvocationTargetException
    at org.apache.tomcat.util.compat.Jre9Compat.jarFileNewInstance(Jre9Compat.java:209) ~[tomcat-embed-core-9.0.64.jar:9.0.64]
    at org.apache.tomcat.util.scan.JarFileUrlJar.<init>(JarFileUrlJar.java:65) ~[tomcat-embed-core-9.0.64.jar:9.0.64]
    at org.apache.tomcat.util.scan.JarFactory.newInstance(JarFactory.java:49) ~[tomcat-embed-core-9.0.64.jar:9.0.64]
    at org.apache.tomcat.util.scan.StandardJarScanner.process(StandardJarScanner.java:383) ~[tomcat-embed-core-9.0.64.jar:9.0.64]
    at org.apache.tomcat.util.scan.StandardJarScanner.processURLs(StandardJarScanner.java:318) ~[tomcat-embed-core-9.0.64.jar:9.0.64]
    at org.apache.tomcat.util.scan.StandardJarScanner.doScanClassPath(StandardJarScanner.java:282) ~[tomcat-embed-core-9.0.64.jar:9.0.64]
    at org.apache.tomcat.util.scan.StandardJarScanner.scan(StandardJarScanner.java:233) ~[tomcat-embed-core-9.0.64.jar:9.0.64]
    at org.apache.catalina.startup.ContextConfig.processJarsForWebFragments(ContextConfig.java:2136) ~[tomcat-embed-core-9.0.64.jar:9.0.64]
    at org.apache.catalina.startup.ContextConfig.webConfig(ContextConfig.java:1289) ~[tomcat-embed-core-9.0.64.jar:9.0.64]
    at org.apache.catalina.startup.ContextConfig.configureStart(ContextConfig.java:986) ~[tomcat-embed-core-9.0.64.jar:9.0.64]
    at org.apache.catalina.startup.ContextConfig.lifecycleEvent(ContextConfig.java:303) ~[tomcat-embed-core-9.0.64.jar:9.0.64]
    at org.apache.catalina.util.LifecycleBase.fireLifecycleEvent(LifecycleBase.java:123) ~[tomcat-embed-core-9.0.64.jar:9.0.64]
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5135) ~[tomcat-embed-core-9.0.64.jar:9.0.64]
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183) ~[tomcat-embed-core-9.0.64.jar:9.0.64]
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1396) ~[tomcat-embed-core-9.0.64.jar:9.0.64]
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1386) ~[tomcat-embed-core-9.0.64.jar:9.0.64]
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[na:na]
    at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75) ~[tomcat-embed-core-9.0.64.jar:9.0.64]
    at java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:145) ~[na:na]
    at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:919) ~[tomcat-embed-core-9.0.64.jar:9.0.64]
    at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:835) ~[tomcat-embed-core-9.0.64.jar:9.0.64]
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183) ~[tomcat-embed-core-9.0.64.jar:9.0.64]
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1396) ~[tomcat-embed-core-9.0.64.jar:9.0.64]
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1386) ~[tomcat-embed-core-9.0.64.jar:9.0.64]
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[na:na]
    at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75) ~[tomcat-embed-core-9.0.64.jar:9.0.64]
    at java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:145) ~[na:na]
    at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:919) ~[tomcat-embed-core-9.0.64.jar:9.0.64]
    at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:263) ~[tomcat-embed-core-9.0.64.jar:9.0.64]
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183) ~[tomcat-embed-core-9.0.64.jar:9.0.64]
    at org.apache.catalina.core.StandardService.startInternal(StandardService.java:432) ~[tomcat-embed-core-9.0.64.jar:9.0.64]
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183) ~[tomcat-embed-core-9.0.64.jar:9.0.64]
    at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:930) ~[tomcat-embed-core-9.0.64.jar:9.0.64]
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183) ~[tomcat-embed-core-9.0.64.jar:9.0.64]
    at org.apache.catalina.startup.Tomcat.start(Tomcat.java:486) ~[tomcat-embed-core-9.0.64.jar:9.0.64]
    at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.initialize(TomcatWebServer.java:123) ~[spring-boot-2.7.1.jar:2.7.1]
    at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.<init>(TomcatWebServer.java:104) ~[spring-boot-2.7.1.jar:2.7.1]
    at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getTomcatWebServer(TomcatServletWebServerFactory.java:479) ~[spring-boot-2.7.1.jar:2.7.1]
    at solutions.elevation.harvest.IntegrationApplication$1.getTomcatWebServer(IntegrationApplication.java:43) ~[classes/:na]
    at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getWebServer(TomcatServletWebServerFactory.java:211) ~[spring-boot-2.7.1.jar:2.7.1]
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:184) ~[spring-boot-2.7.1.jar:2.7.1]
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:162) ~[spring-boot-2.7.1.jar:2.7.1]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:577) ~[spring-context-5.3.21.jar:5.3.21]
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147) ~[spring-boot-2.7.1.jar:2.7.1]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:734) ~[spring-boot-2.7.1.jar:2.7.1]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408) ~[spring-boot-2.7.1.jar:2.7.1]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:308) ~[spring-boot-2.7.1.jar:2.7.1]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306) ~[spring-boot-2.7.1.jar:2.7.1]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295) ~[spring-boot-2.7.1.jar:2.7.1]
    at solutions.elevation.harvest.IntegrationApplication.main(IntegrationApplication.java:21) ~[classes/:na]
Caused by: java.lang.reflect.InvocationTargetException: null
    at java.base/jdk.internal.reflect.GeneratedConstructorAccessor41.newInstance(Unknown Source) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:na]
    at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499) ~[na:na]
    at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480) ~[na:na]
    at org.apache.tomcat.util.compat.Jre9Compat.jarFileNewInstance(Jre9Compat.java:206) ~[tomcat-embed-core-9.0.64.jar:9.0.64]
    ... 49 common frames omitted
Caused by: java.nio.file.NoSuchFileException: /Users/jeremyross/.m2/repository/com/sun/xml/bind/jaxb-core/2.3.0/jaxb-api.jar
    at java.base/sun.nio.fs.UnixException.translateToIOException(UnixException.java:92) ~[na:na]
    at java.base/sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:106) ~[na:na]
    at java.base/sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:111) ~[na:na]
    at java.base/sun.nio.fs.UnixFileAttributeViews$Basic.readAttributes(UnixFileAttributeViews.java:55) ~[na:na]
    at java.base/sun.nio.fs.UnixFileSystemProvider.readAttributes(UnixFileSystemProvider.java:148) ~[na:na]
    at java.base/java.nio.file.Files.readAttributes(Files.java:1851) ~[na:na]
    at java.base/java.util.zip.ZipFile$Source.get(ZipFile.java:1264) ~[na:na]
    at java.base/java.util.zip.ZipFile$CleanableResource.<init>(ZipFile.java:709) ~[na:na]
    at java.base/java.util.zip.ZipFile.<init>(ZipFile.java:243) ~[na:na]
    at java.base/java.util.zip.ZipFile.<init>(ZipFile.java:172) ~[na:na]
    at java.base/java.util.jar.JarFile.<init>(JarFile.java:347) ~[na:na]
    ... 54 common frames omitted

Why is it looking for jaxb-api.jar in the jaxb-core directory? Same for jaxb-core.jar in the jaxb-impl directory.

1 Answers

TL;DR you need to set the scanManifest attribute of the StandardJarScanner to false.

As of Tomcat 9.0.0-M5, by default the StandardJarScanner scans the manifest for additional classpath entries and tries to scan any JARs it finds. This was a fix for Bug 59226.

As of Tomcat 9.0.0-M11, this behaviour was made configurable via the scanManifest attribute. This was a fix for Bug 59961.

The jaxb-core.jar file contains a classpath reference to jaxb-api.jar hence Tomcat looks for it in the same directory as the jaxb-core.jar file.

The jaxb-impl.jar file contains a classpath reference to jaxb-core.jar hence Tomcat looks for it in the same directory as the jaxb-impl.jar file.

Related