Bug 58658 - 7.0.66 running with Java 6 fails unless unneeded "tomcat7-websocket.jar" is removed from lib/
Summary: 7.0.66 running with Java 6 fails unless unneeded "tomcat7-websocket.jar" is r...
Status: RESOLVED FIXED
Alias: None
Product: Tomcat 7
Classification: Unclassified
Component: Catalina (show other bugs)
Version: trunk
Hardware: PC All
: P2 normal (vote)
Target Milestone: ---
Assignee: Tomcat Developers Mailing List
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2015-11-26 16:17 UTC by Konstantin Kolinko
Modified: 2015-12-28 10:36 UTC (History)
1 user (show)



Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Konstantin Kolinko 2015-11-26 16:17:12 UTC
Running 7.0.66 release candidate (smoke-testing) I see an issue that I think is caused by r1715984 change in build.xml:

-           source="${compile.source}"
-           target="${compile.target}"
+           source="1.7"
+           target="1.7"

To reproduce:
1. Unpack apache-tomcat-7.0.66.zip
2. Set JAVA_HOME = Java 6
3. Start Tomcat
4. Deployment of every web application fails, including the ROOT one.


INFO: Deploying web application directory REDACTED\apache-tomcat-7.0.66\webapps\ROOT
26.11.2015 18:54:53 org.apache.catalina.core.ContainerBase addChildInternal
SEVERE: ContainerBase.addChild: start:
org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[]]
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:154)
    at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:901)
    at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:877)
    at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:652)
    at org.apache.catalina.startup.HostConfig.deployDirectory(HostConfig.java:1263)
    at org.apache.catalina.startup.HostConfig$DeployDirectory.run(HostConfig.java:1975)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:439)
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
    at java.util.concurrent.FutureTask.run(FutureTask.java:138)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)
    at java.lang.Thread.run(Thread.java:662)
Caused by: java.lang.UnsupportedClassVersionError: org/apache/tomcat/websocket/server/WsSci : Unsupported major.minor version 51.0
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClassCond(ClassLoader.java:631)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:615)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:141)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:283)
    at java.net.URLClassLoader.access$000(URLClassLoader.java:58)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:197)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:249)
    at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1842)
    at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1705)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:249)
    at org.apache.catalina.startup.WebappServiceLoader.loadServices(WebappServiceLoader.java:192)
    at org.apache.catalina.startup.WebappServiceLoader.load(WebappServiceLoader.java:157)
    at org.apache.catalina.startup.ContextConfig.processServletContainerInitializers(ContextConfig.java:1577)
    at org.apache.catalina.startup.ContextConfig.webConfig(ContextConfig.java:1281)
    at org.apache.catalina.startup.ContextConfig.configureStart(ContextConfig.java:889)
    at org.apache.catalina.startup.ContextConfig.lifecycleEvent(ContextConfig.java:386)
    at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:117)
    at org.apache.catalina.util.LifecycleBase.fireLifecycleEvent(LifecycleBase.java:90)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5460)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    ... 11 more
]]]

Solution
---------
The workaround is to remove the following two files from lib directory:
tomcat7-websocket.jar
websocket-api.jar

I guess that removing tomcat7-websocket.jar alone is enough, but I only tested with removing both of them.

It is known that websocket API (JSR356) requires Java 7, so there is no use for those jar files when running with Java 6.

Good news:
1. The examples web app runs successfully without those files. (I feared that it won't start).
2. JSR356 examples successfully show their HTML pages. Attempting to open a websocket connection fails immediately, as expected.

Thoughts
---------
1. Starting Tomcat 7.0.65 with Java 6 logs a nice warning:

26.11.2015 19:12:19 org.apache.tomcat.websocket.server.WsSci onStartup
INFO: JSR 356 WebSocket (Java WebSocket 1.1) support is not available
when running on Java 6. To suppress this message, run Tomcat on Java 7,
remove the WebSocket JARs from $CATALINA_HOME/lib or add the WebSocket JARs
to the tomcat.util.scan.DefaultJarScanner.jarsToSkip property in
$CATALINA_BASE/conf/catalina.properties. Note that the deprecated Tomcat 7
WebSocket API will be available. 

The above warning message is no longer written by 7.0.66. Tomcat 7.0.66 just fails.

2. An update to installation instructions (RUNNING.txt) is needed.

Currently it says to unpack the binary archive, but with Java 6 that is no longer enough.

3. An update to Windows installer is needed.

If I remember correctly, there is no option to omit those jar files when installing Tomcat.
Comment 1 Mark Thomas 2015-11-27 20:30:27 UTC
I've reverted r1715984 and refactored the code that required 1.7 so it compiles with 1.6. The fix will be in 7.0.67 onwards.
Comment 2 Christopher Schultz 2015-11-30 17:47:30 UTC
(In reply to Mark Thomas from comment #1)
> I've reverted r1715984 and refactored the code that required 1.7 so it
> compiles with 1.6. The fix will be in 7.0.67 onwards.

It's not an issue of compiling with Java 1.6, but an issue of class file versions. You can still use 1.7 source features and still have target="1.6". I'm not sure the re-factoring was necessary (but I haven't read the diff, yet).
Comment 3 imgx64+bz 2015-12-28 08:46:02 UTC
This is fixed when running standalone Tomcat 7.0.67, but it still breaks when running Tomcat embedded in Spring Boot. See this Spring Boot issue for more info: [1]

To reproduce:
1- Download and unzip this Spring Boot Starter template: [2]
2- Edit pom.xml and add `<tomcat.version>7.0.67</tomcat.version>` inside `<properties>` element.
3- run `mvn package` in the unzipped directory (You either need to install Maven, or run `./mvnw package` instead which will automatically download Maven).
4- Make sure you're using Java 6 and run `java -jar target/demo-0.0.1-SNAPSHOT.jar'
5- This exception is thrown:

java.lang.NoClassDefFoundError: java/nio/charset/StandardCharsets
        at org.apache.tomcat.websocket.WsWebSocketContainer.<clinit>(WsWebSocketContainer.java:112) ~[tomcat-embed-websocket-7.0.67.jar!/:7.0.67]
        at org.apache.tomcat.websocket.server.WsSci.init(WsSci.java:154) ~[tomcat-embed-websocket-7.0.67.jar!/:7.0.67]
        at org.apache.tomcat.websocket.server.WsContextListener.contextInitialized(WsContextListener.java:39) ~[tomcat-embed-websocket-7.0.67.jar!/:7.0.67]
        at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:5077) [tomcat-embed-core-7.0.67.jar!/:7.0.67]
        at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5591) [tomcat-embed-core-7.0.67.jar!/:7.0.67]
        at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150) [tomcat-embed-core-7.0.67.jar!/:7.0.67]
        at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1574) [tomcat-embed-core-7.0.67.jar!/:7.0.67]
        at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1564) [tomcat-embed-core-7.0.67.jar!/:7.0.67]
        at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303) [na:1.6.0_65]
        at java.util.concurrent.FutureTask.run(FutureTask.java:138) [na:1.6.0_65]
        at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895) [na:1.6.0_65]
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918) [na:1.6.0_65]
        at java.lang.Thread.run(Thread.java:695) [na:1.6.0_65]
Caused by: java.lang.ClassNotFoundException: java.nio.charset.StandardCharsets
        at java.net.URLClassLoader$1.run(URLClassLoader.java:202) ~[na:1.6.0_65]
        at java.security.AccessController.doPrivileged(Native Method) ~[na:1.6.0_65]
        at java.net.URLClassLoader.findClass(URLClassLoader.java:190) ~[na:1.6.0_65]
        at java.lang.ClassLoader.loadClass(ClassLoader.java:306) ~[na:1.6.0_65]
        at org.springframework.boot.loader.LaunchedURLClassLoader.doLoadClass(LaunchedURLClassLoader.java:178) ~[demo-0.0.1-SNAPSHOT.jar!/:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.LaunchedURLClassLoader.loadClass(LaunchedURLClassLoader.java:142) ~[demo-0.0.1-SNAPSHOT.jar!/:0.0.1-SNAPSHOT]
        at java.lang.ClassLoader.loadClass(ClassLoader.java:247) ~[na:1.6.0_65]
        ... 13 common frames omitted


[1] (https://github.com/spring-projects/spring-boot/issues/4846 )
[2] (http://start.spring.io/starter.zip?name=demo&groupId=com.example&artifactId=demo&version=0.0.1-SNAPSHOT&description=Demo+project+for+Spring+Boot&packageName=com.example&type=maven-project&packaging=jar&javaVersion=1.6&language=java&bootVersion=1.3.2.BUILD-SNAPSHOT&dependencies=web )
Comment 4 Mark Thomas 2015-12-28 09:34:17 UTC
There are multiple reasons this is not the same issue:

- the stack trace is different
- the GitHub issue makes it clear that a different set of Tomcat versions is affected.

The Spring Boot team appear to have mis-read my comment, particularly the question mark at the end of the first sentence and the entire of the final sentence.

I've taken a quick look at the demo and this is clearly a Spring Boot bug. I'll comment on the Spring Boot issue.
Comment 5 imgx64+bz 2015-12-28 10:28:14 UTC
I dug into the Tomcat 7 code, and I managed to "fix" the Spring Boot bug by copying StandardCharsets.ISO_8859_1 into WsWebSocketContainer and changing the static intializers. Is this an appropriate solution?

Note that StandardCharsets.ISO_8859_1 is used elsewhere in WsWebSocketContainer, but only the static initializers are causing problems.

Index: java/org/apache/tomcat/websocket/WsWebSocketContainer.java
===================================================================
--- java/org/apache/tomcat/websocket/WsWebSocketContainer.java	(revision 1721884)
+++ java/org/apache/tomcat/websocket/WsWebSocketContainer.java	(working copy)
@@ -29,6 +29,7 @@
 import java.nio.ByteBuffer;
 import java.nio.channels.AsynchronousChannelGroup;
 import java.nio.channels.AsynchronousSocketChannel;
+import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
 import java.security.KeyStore;
 import java.util.ArrayList;
@@ -109,10 +110,12 @@
     private static final Random random = new Random();
     private static final byte[] crlf = new byte[] {13, 10};
 
-    private static final byte[] GET_BYTES = "GET ".getBytes(StandardCharsets.ISO_8859_1);
-    private static final byte[] ROOT_URI_BYTES = "/".getBytes(StandardCharsets.ISO_8859_1);
+    private static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
+
+    private static final byte[] GET_BYTES = "GET ".getBytes(ISO_8859_1);
+    private static final byte[] ROOT_URI_BYTES = "/".getBytes(ISO_8859_1);
     private static final byte[] HTTP_VERSION_BYTES =
-            " HTTP/1.1\r\n".getBytes(StandardCharsets.ISO_8859_1);
+            " HTTP/1.1\r\n".getBytes(ISO_8859_1);
 
     private volatile AsynchronousChannelGroup asynchronousChannelGroup = null;
     private final Object asynchronousChannelGroupLock = new Object();
Comment 6 Mark Thomas 2015-12-28 10:36:55 UTC
This issue is fixed. Please don't use it to discuss what is an unrelated issue in Spring Boot. If you want input from the Tomcat devs on possible options for fixing the Spring Boot issue, then the Tomcat dev list is the place to ask.