2

Now that I upgraded my webapp to Java 8, I'm running into strange classloading problem with an axis webservice that is running in Tomcat 8.

The very first call to the webservice after installing the webapp will cause a RuntimeException and a "No compiler found in your classpath! (you may need to add 'tools.jar')" fault.

All following calls to that webservice work correctly (apparently tools.jar is there after all?).

The behaviour is reproducable by deleting the exploded webapp folder in the deploy directory. After the next tomcat start, the first call will fail again. If the exploded folder is already there, a tomcat restart does not cause the error.

The error did not occur when tomcat/the webapp were still running with Java 7.

Now, that Axis error has a long, long tradition. The solution in earlier times was to copy tools.jar into tomcats lib directory (Aka make tools.jar available in tomcats classpath). Its not there by default, because axis apparently runs with a JRE (even if you start tomcat with a JDK in JAVA_HOME).

Sadly that did not help. I tried several ways to put tools.jar into tomcats classpath:

  • I added it to the libs folder
  • I added it to the classpath via setenv.bat
  • I added it to the endorsed lib folder

None helped, the very first call was still failing.

I then played around with JAVA_HOME and JRE_HOME. Tomcat starts with a JRE whenever possible and only uses a JDK if you use some functions that need a JDK. I tried to get tomcat to use a JDK instad of a JRE, but the error persistet.

So, question time:

  1. Whats the difference in classloading when tomcat explodes a webapp.war compared to the case that the war has already been exploded in an earlier server run?

  2. When starting, tomcat writes the following to the logfile:

Server version: Apache Tomcat/8.0.15
Server built: Nov 2 2014 19:25:20 UTC
Server number: 8.0.15.0
OS Name: Windows Server 2008 R2
OS Version: 6.1
Architecture: x86
JAVA_HOME: C:\Dev\Java\jdk1.8.0_25\jre
JVM Version: 1.8.0_25-b18
JVM Vendor: Oracle Corporation
CATALINA_BASE: C:\Dev\apache-tomcat-8.0.15
CATALINA_HOME: C:\Dev\apache-tomcat-8.0.15
Command line argument: -Djava.util.logging.config.file=c:\Dev\apache-tomcat-8.0.15\conf\logging.properties
Command line argument: -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
Command line argument: -Djava.endorsed.dirs=c:\Dev\apache-tomcat-8.0.15\endorsed
Command line argument: -Dcatalina.base=c:\Dev\apache-tomcat-8.0.15
Command line argument: -Dcatalina.home=c:\Dev\apache-tomcat-8.0.15
Command line argument: -Djava.io.tmpdir=c:\Dev\apache-tomcat-8.0.15\temp

so apparently it got itself a reference to the JRE somehow (although I only specified a JDK, see above). How can I change that?

  1. More ideas?

Edit2: The culprit is the compilation of .jws files that axis does the first time a webservice is called. The existence of those compiled files is the reason the error only happens on the very first call.

Of course that does not explain at all why the first call fails with the 'compiler missing' error even though the necessary compiled files are created and are available for subsequent calls...

Edit: Stacktrace of the axis error as requested. Nothing special compared to all the other topics here that deal with a missing tools.jar:

java.lang.RuntimeException: No compiler found in your classpath! (you may need to add 'tools.jar')
at org.apache.axis.components.compiler.Javac.compile(Javac.java:156)
at org.apache.axis.handlers.JWSHandler.setupService(JWSHandler.java:199)
at org.apache.axis.handlers.JWSHandler.invoke(JWSHandler.java:72)
at org.apache.axis.strategies.InvocationStrategy.visit(InvocationStrategy.java:32)
at org.apache.axis.SimpleChain.doVisiting(SimpleChain.java:118)
at org.apache.axis.SimpleChain.invoke(SimpleChain.java:83)
at org.apache.axis.server.AxisServer.invoke(AxisServer.java:249) at org.apache.axis.transport.http.QSMethodHandler.invokeEndpointFromGet(QSMethodHandler.java:129)
at org.apache.axis.transport.http.QSMethodHandler.invoke(QSMethodHandler.java:94)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.apache.axis.transport.http.AxisServlet.processQuery(AxisServlet.java:1226)
at org.apache.axis.transport.http.AxisServlet.doGet(AxisServlet.java:249)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:618)
at org.apache.axis.transport.http.AxisServletBase.service(AxisServletBase.java:327)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:725)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:610)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:537)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1085)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:658)
at org.apache.coyote.http11.Http11AprProtocol$Http11ConnectionHandler.process(Http11AprProtocol.java:277)
at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.doRun(AprEndpoint.java:2407)
at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.run(AprEndpoint.java:2396)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)

John
  • 21
  • 1
  • 4

3 Answers3

2

The actual message in the stack trace I get is the one by the original poster but preceded by this one.

Looks like the compiler is returning a different value than expected but the compilation still gets done and an exception is thrown. This is from http://www.docjar.org/html/api/org/apache/axis/components/compiler/Javac.java.html

136             if (modern) {
137                 int compilationResult = 
138                     ((Integer)compile.invoke(compiler, new Object[] 
139                         {
140                             toStringArray(fillArguments
141                                           (new ArrayList()))})).intValue();
142 
143                 result = (compilationResult == 0);        
144                 log.debug("Compilation Returned: " 
145                           + Integer.toString(compilationResult));
146             }
147             else {
148                 Boolean ok = 
149                     (Boolean)compile.invoke(compiler, new Object[] 
150                         {toStringArray(fillArguments(new ArrayList()))});
151         
152                 result = ok.booleanValue();
153             }
154         } catch (Exception cnfe){
155             log.error(Messages.getMessage("noCompiler00"), cnfe);
156             throw new RuntimeException(Messages.getMessage("noCompiler00"));


5-Apr-2015 20:16:42 ERROR 160663 [http-nio-10470-exec-10] org.apache.axis.components.compiler.Javac.compile(Javac.java:155) - No compiler found in your classpath!  (you may need to add 'tools.jar')
java.lang.ClassCastException: com.sun.tools.javac.main.Main$Result cannot be cast to java.lang.Integer
        at org.apache.axis.components.compiler.Javac.compile(Javac.java:137)
        at org.apache.axis.handlers.JWSHandler.setupService(JWSHandler.java:199)
        at org.apache.axis.handlers.JWSHandler.generateWSDL(JWSHandler.java:294)
        at org.apache.axis.strategies.WSDLGenStrategy.visit(WSDLGenStrategy.java:33)
        at org.apache.axis.SimpleChain.doVisiting(SimpleChain.java:118)
        at org.apache.axis.SimpleChain.generateWSDL(SimpleChain.java:104)
        at org.apache.axis.server.AxisServer.generateWSDL(AxisServer.java:454)
        at org.apache.axis.transport.http.QSWSDLHandler.invoke(QSWSDLHandler.java:68)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:497)
        at org.apache.axis.transport.http.AxisServlet.processQuery(AxisServlet.java:1226)
        at org.apache.axis.transport.http.AxisServlet.doGet(AxisServlet.java:249)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:618)
        at org.apache.axis.transport.http.AxisServletBase.service(AxisServletBase.java:327)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:725)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:516)
        at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1086)
        at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:659)
        at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:223)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1558)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1515)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:745)

Update

I have changed the code in axis-1_4\src\org\apache\axis\components\compiler\Javac.java to the below as the original code is expecting an integer for pre JDK 8 compilers. JDK 8 is returning an enum. I have only done this on my local copy and re-built and works fine.

// Call the compile() method

 Method compile = c.getMethod("compile",

                                     new Class [] { String[].class });

 if (modern) {

   log.info("Using modified axis for jdk 8 compiler");  
   Object compilationResult =  (compile.invoke(compiler, new Object[] 

                    {
                        toStringArray(fillArguments
                                      (new ArrayList()))}));

   result = ("OK".equalsIgnoreCase(compilationResult.toString()));        
  log.debug("Compilation Returned: "+ compilationResult);
        }
1
  1. Whats the difference in classloading

No difference.

Maybe some files are created in the work directory for your web application on the first run.

  1. When starting, tomcat writes the following to the logfile:

JAVA_HOME: C:\Dev\Java\jdk1.8.0_25\jre

That message is the value of System.getProperty("java.home"), as logged by VersionLoggerListener.

That is how Java launcher (java.exe) works. It has nothing to do with Tomcat itself.

A simple Java program:

public class Test {
  public static void main(String[] a){
    System.out.println("java.home: "  + System.getProperty("java.home"));
    System.out.println("java.class.path: "  + System.getProperty("java.class.path"));
    System.out.println("sun.boot.class.path: "  + System.getProperty("sun.boot.class.path"));
  }
}

Running it with JDK prints the path of JDK's JRE, not of JDK. The boot classpath lists JRE libraries only (no tools.jar there).

Note that the system properties can also be inspected via jconsole, jvisualvm.

  1. More ideas?

Add tools.jar to a CLASSPATH explicitly.

  • If you are launching Tomcat from a *.bat file, the CLASSPATH variable can be set in setenv.bat.
  • If you are launching it as a service, use service configuration application (tomcat8w.exe) to configure the classpath.
  • If you are launching it from within an IDE, configure java settings in the IDE.
  • I added it to the libs folder

I would expect that to work. Maybe some other JDK jars are needed besides tools.jar.

  • I added it to the classpath via setenv.
  • I added it to the endorsed lib folder

The above two points work only if you are using the standard *.bat files to launch Tomcat. If you are launching it as a service, or from within an IDE the service / IDE settings matter. The setenv.bat file is not used by them.

It also depends on how Axis is detecting this issue. I hope that it tries to load some class (and not tries to find a specific jar file). More details on the original message? (What component logs it? A stacktrace? Source code?)

Konstantin Kolinko
  • 3,854
  • 1
  • 13
  • 21
  • > Add tools.jar to a CLASSPATH explicitly. – John Jan 19 '15 at 11:39
  • Thanks for your answer. Regarding the classpath, I had already tried everything you mentioned, did not work. Your idea that files were created on the first run is correct. Axis did compile the webservice.jws file into a java class to run (and it did that even though the missing compiler error was thrown). I guess I will continue to search in that direction - axis behaves strange... – John Jan 19 '15 at 11:50
  • I was having a general problem with a JAR file I had added to the classpath via setenv.bat not loading for me when I moved my application to JDK1.8+Tomcat 8. On JDK1.7+Tomcat 7, this worked fine for me even while I was running Tomcat as a Windows service. Adding the jar to the Classpath registry entry instead solved the problem with the later line-up. Your comments above prompted me to try this, so thanks! – John Rix Apr 22 '15 at 11:46
0

I've had the same problem, and after hours of trying to solve it and re-configurate my classpath hundred of times...it seems I've found the solution, at least one that suits my situation.

The error you mention, it appears to go away if you change the JDK version from 1.8 to 1.7!!

daniegarcia254
  • 1,157
  • 4
  • 17
  • 36