Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fix for #195 [runtime] Add support for Tomcat WebSocket

  • Loading branch information...
commit 380b5715af327270a0033bb06018bd73d71e8334 1 parent a26ae4c
@jfarcand jfarcand authored
Showing with 608 additions and 104 deletions.
  1. +132 −0 modules/acceptance-tests/pom.xml
  2. 0  modules/{cpr → acceptance-tests}/src/test/java/org/atmosphere/tests/AbstractHttpAtmosphereHandler.java
  3. 0  modules/{cpr → acceptance-tests}/src/test/java/org/atmosphere/tests/AtmosphereServletTest.java
  4. +16 −16 modules/{cpr → acceptance-tests}/src/test/java/org/atmosphere/tests/BaseTest.java
  5. 0  modules/{cpr → acceptance-tests}/src/test/java/org/atmosphere/tests/BlockingIOCometSupportTest.java
  6. 0  modules/{cpr → acceptance-tests}/src/test/java/org/atmosphere/tests/BroadcasterLifecycleTest.java
  7. 0  modules/{cpr → acceptance-tests}/src/test/java/org/atmosphere/tests/BroadcasterScopeTest.java
  8. 0  modules/{cpr → acceptance-tests}/src/test/java/org/atmosphere/tests/ConcurrentBroadcastTest.java
  9. 0  modules/{cpr → acceptance-tests}/src/test/java/org/atmosphere/tests/GrizzlyCometSupportTest.java
  10. 0  modules/{cpr → acceptance-tests}/src/test/java/org/atmosphere/tests/Jetty6CometSupportTest.java
  11. 0  modules/{cpr → acceptance-tests}/src/test/java/org/atmosphere/tests/Jetty7CometSupportTest.java
  12. +0 −1  modules/{cpr → acceptance-tests}/src/test/java/org/atmosphere/tests/MeteorTest.java
  13. +5 −0 modules/{cpr → acceptance-tests}/src/test/java/org/atmosphere/tests/TomcatCometSupportTest.java
  14. 0  modules/{cpr → acceptance-tests}/src/test/resources/logback-test.xml
  15. +2 −1  modules/compat-tomcat7/pom.xml
  16. +34 −55 modules/cpr/pom.xml
  17. +2 −2 modules/cpr/src/main/java/org/atmosphere/container/JettyWebSocketHandler.java
  18. +173 −0 modules/cpr/src/main/java/org/atmosphere/container/Tomcat7AsyncSupportWithWebSocket.java
  19. +92 −0 modules/cpr/src/main/java/org/atmosphere/container/TomcatWebSocketHandler.java
  20. +108 −0 modules/cpr/src/main/java/org/atmosphere/container/version/TomcatWebSocket.java
  21. +6 −2 modules/cpr/src/main/java/org/atmosphere/cpr/AtmosphereRequest.java
  22. +5 −0 modules/cpr/src/main/java/org/atmosphere/cpr/DefaultAsyncSupportResolver.java
  23. +29 −24 modules/cpr/src/main/java/org/atmosphere/websocket/WebSocketProcessor.java
  24. +1 −1  modules/cpr/src/test/java/org/atmosphere/cpr/DefaultBroadcasterFactoryTest.java
  25. +1 −0  modules/pom.xml
  26. +2 −2 pom.xml
View
132 modules/acceptance-tests/pom.xml
@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <groupId>org.atmosphere</groupId>
+ <artifactId>atmosphere-project</artifactId>
+ <version>0.9-SNAPSHOT</version>
+ <relativePath>../../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.atmosphere</groupId>
+ <artifactId>acceptance-tests-runtime</artifactId>
+ <packaging>jar</packaging>
+ <version>0.9-SNAPSHOT</version>
+ <name>acceptance-tests-runtime</name>
+ <url>https://github.com/Atmosphere/atmosphere</url>
+ <build>
+ <defaultGoal>install</defaultGoal>
+ <testResources>
+ <testResource>
+ <directory>src/test/resources</directory>
+ </testResource>
+ </testResources>
+ </build>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-servlet_3.0_spec</artifactId>
+ <version>1.0</version>
+ <scope>test</scope>
+ <optional>true</optional>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-continuation</artifactId>
+ <version>${jetty7-version}</version>
+ <scope>test</scope>
+ <optional>true</optional>
+ <exclusions>
+ <exclusion>
+ <groupId>javax.servlet</groupId>
+ <artifactId>servlet-api</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-servlet</artifactId>
+ <version>${jetty7-version}</version>
+ <scope>test</scope>
+ <optional>true</optional>
+ <exclusions>
+ <exclusion>
+ <groupId>javax.servlet</groupId>
+ <artifactId>servlet-api</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>com.sun.grizzly</groupId>
+ <artifactId>grizzly-websockets</artifactId>
+ <version>${grizzly-version}</version>
+ <scope>test</scope>
+ <optional>true</optional>
+ </dependency>
+ <dependency>
+ <groupId>com.sun.grizzly</groupId>
+ <artifactId>grizzly-http</artifactId>
+ <version>${grizzly-version}</version>
+ <scope>test</scope>
+ <optional>true</optional>
+ </dependency>
+ <dependency>
+ <groupId>com.sun.grizzly</groupId>
+ <artifactId>grizzly-http-servlet</artifactId>
+ <version>${grizzly-version}</version>
+ <scope>test</scope>
+ <optional>true</optional>
+ </dependency>
+ <dependency>
+ <groupId>com.sun.grizzly</groupId>
+ <artifactId>grizzly-comet</artifactId>
+ <version>${grizzly-version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.sun.grizzly</groupId>
+ <artifactId>grizzly-compat</artifactId>
+ <version>${grizzly-version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.tomcat</groupId>
+ <artifactId>catalina</artifactId>
+ <version>${tomcat-version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.tomcat</groupId>
+ <artifactId>coyote</artifactId>
+ <version>${tomcat-version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.8.5</version>
+ <type>jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.atmosphere</groupId>
+ <artifactId>atmosphere-runtime</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>org.apache.tomcat</groupId>
+ <artifactId>tomcat-catalina</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.tomcat</groupId>
+ <artifactId>tomcat-coyote</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.tomcat</groupId>
+ <artifactId>tomcat-util</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ </dependencies>
+</project>
+
View
0  ...mosphere/tests/AbstractHttpAtmosphereHandler.java → ...mosphere/tests/AbstractHttpAtmosphereHandler.java
File renamed without changes
View
0  ...a/org/atmosphere/tests/AtmosphereServletTest.java → ...a/org/atmosphere/tests/AtmosphereServletTest.java
File renamed without changes
View
32 .../src/test/java/org/atmosphere/tests/BaseTest.java → .../src/test/java/org/atmosphere/tests/BaseTest.java
@@ -216,7 +216,7 @@ public void onStateChange(AtmosphereResourceEvent event) throws IOException {
latch.countDown();
}
}
- }, BroadcasterFactory.getDefault().get(DefaultBroadcaster.class, "suspend"));
+ }, BroadcasterFactory.getDefault().get(DefaultBroadcaster.class, "suspend"));
AsyncHttpClient c = new AsyncHttpClient();
try {
@@ -268,7 +268,7 @@ public void onStateChange(AtmosphereResourceEvent event) throws IOException {
latch.countDown();
}
}
- }, BroadcasterFactory.getDefault().get(DefaultBroadcaster.class, "suspend"));
+ }, BroadcasterFactory.getDefault().get(DefaultBroadcaster.class, "suspend"));
AsyncHttpClient c = new AsyncHttpClient();
try {
@@ -320,7 +320,7 @@ public void onStateChange(AtmosphereResourceEvent event) throws IOException {
latch.countDown();
}
}
- }, BroadcasterFactory.getDefault().get(DefaultBroadcaster.class, "suspend"));
+ }, BroadcasterFactory.getDefault().get(DefaultBroadcaster.class, "suspend"));
AsyncHttpClient c = new AsyncHttpClient();
try {
@@ -392,7 +392,7 @@ public void onStateChange(AtmosphereResourceEvent event) throws IOException {
latch.countDown();
}
}
- }, BroadcasterFactory.getDefault().get(DefaultBroadcaster.class, "suspend"));
+ }, BroadcasterFactory.getDefault().get(DefaultBroadcaster.class, "suspend"));
AsyncHttpClient c = new AsyncHttpClient();
try {
@@ -474,7 +474,7 @@ public void onStateChange(AtmosphereResourceEvent event) throws IOException {
latch.countDown();
}
}
- }, BroadcasterFactory.getDefault().get(DefaultBroadcaster.class, "suspend"));
+ }, BroadcasterFactory.getDefault().get(DefaultBroadcaster.class, "suspend"));
AsyncHttpClient c = new AsyncHttpClient();
try {
@@ -550,7 +550,7 @@ public void onStateChange(AtmosphereResourceEvent event) throws IOException {
latch.countDown();
}
}
- }, BroadcasterFactory.getDefault().get(DefaultBroadcaster.class, "suspend"));
+ }, BroadcasterFactory.getDefault().get(DefaultBroadcaster.class, "suspend"));
AsyncHttpClient c = new AsyncHttpClient();
try {
@@ -623,7 +623,7 @@ public void onStateChange(AtmosphereResourceEvent event) throws IOException {
latch.countDown();
}
}
- }, BroadcasterFactory.getDefault().get(DefaultBroadcaster.class, "suspend"));
+ }, BroadcasterFactory.getDefault().get(DefaultBroadcaster.class, "suspend"));
AsyncHttpClient c = new AsyncHttpClient();
try {
@@ -704,7 +704,7 @@ public void onStateChange(AtmosphereResourceEvent event) throws IOException {
latch.countDown();
}
}
- }, BroadcasterFactory.getDefault().get(DefaultBroadcaster.class, "suspend"));
+ }, BroadcasterFactory.getDefault().get(DefaultBroadcaster.class, "suspend"));
AsyncHttpClient c = new AsyncHttpClient();
try {
@@ -784,7 +784,7 @@ public void onStateChange(AtmosphereResourceEvent event) throws IOException {
latch.countDown();
}
}
- }, BroadcasterFactory.getDefault().get(DefaultBroadcaster.class, "suspend"));
+ }, BroadcasterFactory.getDefault().get(DefaultBroadcaster.class, "suspend"));
AsyncHttpClient c = new AsyncHttpClient();
try {
@@ -858,7 +858,7 @@ public void onStateChange(AtmosphereResourceEvent event) throws IOException {
event.getResource().resume();
latch.countDown();
}
- }, BroadcasterFactory.getDefault().get(DefaultBroadcaster.class, "suspend"));
+ }, BroadcasterFactory.getDefault().get(DefaultBroadcaster.class, "suspend"));
AsyncHttpClient c = new AsyncHttpClient();
try {
@@ -939,7 +939,7 @@ public void onStateChange(AtmosphereResourceEvent event) throws IOException {
latch.countDown();
}
}
- }, BroadcasterFactory.getDefault().get(DefaultBroadcaster.class, "suspend"));
+ }, BroadcasterFactory.getDefault().get(DefaultBroadcaster.class, "suspend"));
AsyncHttpClient c = new AsyncHttpClient();
try {
@@ -1006,7 +1006,7 @@ public void onStateChange(AtmosphereResourceEvent event) throws IOException {
latch.countDown();
}
}
- }, BroadcasterFactory.getDefault().get(DefaultBroadcaster.class, "suspend"));
+ }, BroadcasterFactory.getDefault().get(DefaultBroadcaster.class, "suspend"));
AsyncHttpClient c = new AsyncHttpClient();
try {
@@ -1065,7 +1065,7 @@ public void onStateChange(AtmosphereResourceEvent event) throws IOException {
latch.countDown();
}
}
- }, BroadcasterFactory.getDefault().get(DefaultBroadcaster.class, "suspend"));
+ }, BroadcasterFactory.getDefault().get(DefaultBroadcaster.class, "suspend"));
AsyncHttpClient c = new AsyncHttpClient();
try {
@@ -1132,7 +1132,7 @@ public void onStateChange(AtmosphereResourceEvent event) throws IOException {
assertEquals(event.getMessage(), "broadcastOnResume");
}
}
- }, BroadcasterFactory.getDefault().get(DefaultBroadcaster.class, "suspend"));
+ }, BroadcasterFactory.getDefault().get(DefaultBroadcaster.class, "suspend"));
AsyncHttpClient c = new AsyncHttpClient();
try {
@@ -1209,7 +1209,7 @@ public void onStateChange(AtmosphereResourceEvent event) throws IOException {
}
}
}
- }, BroadcasterFactory.getDefault().get(DefaultBroadcaster.class, "suspend"));
+ }, BroadcasterFactory.getDefault().get(DefaultBroadcaster.class, "suspend"));
AsyncHttpClient c = new AsyncHttpClient();
try {
@@ -1276,7 +1276,7 @@ public void onStateChange(AtmosphereResourceEvent event) throws IOException {
}
}
}
- }, BroadcasterFactory.getDefault().get(DefaultBroadcaster.class, "suspend"));
+ }, BroadcasterFactory.getDefault().get(DefaultBroadcaster.class, "suspend"));
AsyncHttpClient c = new AsyncHttpClient();
try {
View
0  .../atmosphere/tests/BlockingIOCometSupportTest.java → .../atmosphere/tests/BlockingIOCometSupportTest.java
File renamed without changes
View
0  ...rg/atmosphere/tests/BroadcasterLifecycleTest.java → ...rg/atmosphere/tests/BroadcasterLifecycleTest.java
File renamed without changes
View
0  ...va/org/atmosphere/tests/BroadcasterScopeTest.java → ...va/org/atmosphere/tests/BroadcasterScopeTest.java
File renamed without changes
View
0  ...org/atmosphere/tests/ConcurrentBroadcastTest.java → ...org/atmosphere/tests/ConcurrentBroadcastTest.java
File renamed without changes
View
0  ...org/atmosphere/tests/GrizzlyCometSupportTest.java → ...org/atmosphere/tests/GrizzlyCometSupportTest.java
File renamed without changes
View
0  .../org/atmosphere/tests/Jetty6CometSupportTest.java → .../org/atmosphere/tests/Jetty6CometSupportTest.java
File renamed without changes
View
0  .../org/atmosphere/tests/Jetty7CometSupportTest.java → .../org/atmosphere/tests/Jetty7CometSupportTest.java
File renamed without changes
View
1  ...rc/test/java/org/atmosphere/tests/MeteorTest.java → ...rc/test/java/org/atmosphere/tests/MeteorTest.java
@@ -35,7 +35,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.annotations.AfterMethod;
-import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import javax.servlet.http.HttpServlet;
View
5 .../org/atmosphere/tests/TomcatCometSupportTest.java → .../org/atmosphere/tests/TomcatCometSupportTest.java
@@ -85,6 +85,7 @@ public void init(final ServletConfig sc) throws ServletException {
public void startServer() throws Exception {
System.setProperty("org.atmosphere.useNative", "true");
+ try {
int port = TestHelper.getEnvVariable("ATMOSPHERE_HTTP_PORT", findFreePort());
urlTarget = "http://127.0.0.1:" + port + "/invoke";
@@ -114,7 +115,11 @@ public void startServer() throws Exception {
embedded.addEngine(engine);
embedded.addConnector(connector);
embedded.start();
+
atmoServlet = (AtmosphereServlet) w.getServlet();
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
}
View
0  modules/cpr/src/test/resources/logback-test.xml → ...eptance-tests/src/test/resources/logback-test.xml
File renamed without changes
View
3  modules/compat-tomcat7/pom.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.atmosphere</groupId>
<artifactId>atmosphere-project</artifactId>
View
89 modules/cpr/pom.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.atmosphere</groupId>
<artifactId>atmosphere-project</artifactId>
@@ -30,7 +31,7 @@
<testResources>
<testResource>
- <directory>src/test/resources</directory>
+ <directory>../acceptance-tests/src/test/resources</directory>
</testResource>
</testResources>
@@ -69,32 +70,6 @@
<optional>true</optional>
</dependency>
<dependency>
- <groupId>org.apache.tomcat</groupId>
- <artifactId>coyote</artifactId>
- <version>${tomcat-version}</version>
- <scope>provided</scope>
- <optional>true</optional>
- <exclusions>
- <exclusion>
- <groupId>org.apache.tomcat</groupId>
- <artifactId>servlet-api</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
- <groupId>org.apache.tomcat</groupId>
- <artifactId>catalina</artifactId>
- <version>${tomcat-version}</version>
- <scope>provided</scope>
- <optional>true</optional>
- <exclusions>
- <exclusion>
- <groupId>org.apache.tomcat</groupId>
- <artifactId>servlet-api</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-continuation</artifactId>
<version>${jetty7-version}</version>
@@ -108,19 +83,6 @@
</exclusions>
</dependency>
<dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-servlet</artifactId>
- <version>${jetty7-version}</version>
- <scope>test</scope>
- <optional>true</optional>
- <exclusions>
- <exclusion>
- <groupId>javax.servlet</groupId>
- <artifactId>servlet-api</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
<groupId>com.sun.grizzly</groupId>
<artifactId>grizzly-websockets</artifactId>
<version>${grizzly-version}</version>
@@ -142,20 +104,6 @@
</dependency>
<dependency>
<groupId>com.sun.grizzly</groupId>
- <artifactId>grizzly-http</artifactId>
- <version>${grizzly-version}</version>
- <scope>test</scope>
- <optional>true</optional>
- </dependency>
- <dependency>
- <groupId>com.sun.grizzly</groupId>
- <artifactId>grizzly-http-servlet</artifactId>
- <version>${grizzly-version}</version>
- <scope>test</scope>
- <optional>true</optional>
- </dependency>
- <dependency>
- <groupId>com.sun.grizzly</groupId>
<artifactId>grizzly-comet</artifactId>
<version>${grizzly-version}</version>
<scope>provided</scope>
@@ -182,6 +130,37 @@
<version>${project.version}</version>
</dependency>
<dependency>
+ <groupId>org.atmosphere</groupId>
+ <artifactId>atmosphere-compat-tomcat7</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.tomcat</groupId>
+ <artifactId>tomcat-catalina</artifactId>
+ <version>${tomcat7-version}</version>
+ <scope>provided</scope>
+ <optional>true</optional>
+ <exclusions>
+ <exclusion>
+ <groupId>org.apache.tomcat</groupId>
+ <artifactId>servlet-api</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.tomcat</groupId>
+ <artifactId>tomcat-coyote</artifactId>
+ <version>${tomcat7-version}</version>
+ <scope>provided</scope>
+ <optional>true</optional>
+ <exclusions>
+ <exclusion>
+ <groupId>org.apache.tomcat</groupId>
+ <artifactId>servlet-api</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.8.5</version>
View
4 modules/cpr/src/main/java/org/atmosphere/container/JettyWebSocketHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2011 Jeanfrancois Arcand
+ * Copyright 2012 Jeanfrancois Arcand
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
@@ -48,7 +48,7 @@
private WebSocketProcessor webSocketProcessor;
private final AtmosphereRequest request;
private final AtmosphereFramework framework;
- private WebSocketProtocol webSocketProtocol;
+ private final WebSocketProtocol webSocketProtocol;
public JettyWebSocketHandler(AtmosphereRequest request, AtmosphereFramework framework, WebSocketProtocol webSocketProtocol) {
this.request = request;
View
173 modules/cpr/src/main/java/org/atmosphere/container/Tomcat7AsyncSupportWithWebSocket.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2012 Jeanfrancois Arcand
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.atmosphere.container;
+
+import org.apache.catalina.connector.RequestFacade;
+import org.apache.catalina.websocket.StreamInbound;
+import org.atmosphere.cpr.AtmosphereConfig;
+import org.atmosphere.cpr.AtmosphereFramework;
+import org.atmosphere.cpr.AtmosphereRequest;
+import org.atmosphere.cpr.AtmosphereResponse;
+import org.atmosphere.websocket.WebSocket;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+
+public class Tomcat7AsyncSupportWithWebSocket extends Servlet30CometSupport {
+ private static final Logger logger = LoggerFactory.getLogger(Tomcat7AsyncSupportWithWebSocket.class);
+ private static final long serialVersionUID = 1L;
+ private static byte[] WS_ACCEPT;
+
+ private MessageDigest sha1Helper;
+
+ public Tomcat7AsyncSupportWithWebSocket(AtmosphereConfig config) {
+ super(config);
+ }
+
+ @Override
+ public void init(ServletConfig sc) throws ServletException {
+ try {
+ WS_ACCEPT = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11".getBytes("ISO_8859_1");
+ sha1Helper = MessageDigest.getInstance("SHA1");
+ } catch (Exception e) {
+ throw new ServletException(e);
+ }
+ }
+
+ @Override
+ public AtmosphereFramework.Action service(AtmosphereRequest req, AtmosphereResponse res) throws IOException, ServletException {
+ // First, handshake
+ if (req.getAttribute(WebSocket.WEBSOCKET_SUSPEND) == null) {
+ // Information required to send the server handshake message
+ String key;
+ String subProtocol = null;
+ List<String> extensions = Collections.emptyList();
+
+ if (!headerContainsToken(req, "upgrade", "websocket")) {
+ return super.service(req, res);
+ }
+
+ if (!headerContainsToken(req, "connection", "upgrade")) {
+ return super.service(req, res);
+ }
+
+ if (!headerContainsToken(req, "sec-websocket-version", "13")) {
+ res.setStatus(426);
+ res.setHeader("Sec-WebSocket-Version", "13");
+ return super.service(req, res);
+ }
+
+ key = req.getHeader("Sec-WebSocket-Key");
+ if (key == null) {
+ return super.service(req, res);
+ }
+
+ // If we got this far, all is good. Accept the connection.
+ res.setHeader("upgrade", "websocket");
+ res.setHeader("connection", "upgrade");
+ res.setHeader("Sec-WebSocket-Accept", getWebSocketAccept(key));
+
+ if (subProtocol != null) {
+ res.setHeader("Sec-WebSocket-Protocol", subProtocol);
+ }
+
+ if (!extensions.isEmpty()) {
+ // TODO
+ }
+
+ RequestFacade facade = (RequestFacade) req.wrappedRequest();
+ StreamInbound inbound = new TomcatWebSocketHandler(AtmosphereRequest.loadInMemory(req), config.framework(), config.framework().getWebSocketProtocol());
+ facade.doUpgrade(inbound);
+ }
+
+ AtmosphereFramework.Action action = suspended(req, res);
+ if (action.type == AtmosphereFramework.Action.TYPE.SUSPEND) {
+ logger.debug("Suspending resonse: {}", res);
+ } else if (action.type == AtmosphereFramework.Action.TYPE.RESUME) {
+ logger.debug("Resume resonse: {}", res);
+ req.setAttribute(WebSocket.WEBSOCKET_RESUME, true);
+ }
+ return action;
+ }
+
+ /*
+ * This only works for tokens. Quoted strings need more sophisticated
+ * parsing.
+ */
+
+ private boolean headerContainsToken(HttpServletRequest req,
+ String headerName, String target) {
+ Enumeration<String> headers = req.getHeaders(headerName);
+ while (headers.hasMoreElements()) {
+ String header = headers.nextElement();
+ String[] tokens = header.split(",");
+ for (String token : tokens) {
+ if (target.equalsIgnoreCase(token.trim())) {
+ return true;
+ }
+ }
+ }
+ return true;
+ }
+
+
+ /*
+ * This only works for tokens. Quoted strings need more sophisticated
+ * parsing.
+ */
+ private List<String> getTokensFromHeader(HttpServletRequest req,
+ String headerName) {
+ List<String> result = new ArrayList<String>();
+
+ Enumeration<String> headers = req.getHeaders(headerName);
+ while (headers.hasMoreElements()) {
+ String header = headers.nextElement();
+ String[] tokens = header.split(",");
+ for (String token : tokens) {
+ result.add(token.trim());
+ }
+ }
+ return result;
+ }
+
+
+ private String getWebSocketAccept(String key) {
+ synchronized (sha1Helper) {
+ sha1Helper.reset();
+ try {
+ sha1Helper.update(key.getBytes("ISO_8859_1"));
+ } catch (UnsupportedEncodingException e) {
+ }
+ return org.apache.catalina.util.Base64.encode(sha1Helper.digest(WS_ACCEPT));
+ }
+ }
+
+ @Override
+ public boolean supportWebSocket() {
+ return true;
+ }
+}
+
View
92 modules/cpr/src/main/java/org/atmosphere/container/TomcatWebSocketHandler.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2012 Jeanfrancois Arcand
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.atmosphere.container;
+
+import org.apache.catalina.websocket.MessageInbound;
+import org.apache.catalina.websocket.WsOutbound;
+import org.atmosphere.container.version.TomcatWebSocket;
+import org.atmosphere.cpr.AtmosphereFramework;
+import org.atmosphere.cpr.AtmosphereRequest;
+import org.atmosphere.websocket.WebSocketEventListener;
+import org.atmosphere.websocket.WebSocketProcessor;
+import org.atmosphere.websocket.WebSocketProtocol;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+
+import static org.atmosphere.websocket.WebSocketEventListener.WebSocketEvent.TYPE.CLOSE;
+import static org.atmosphere.websocket.WebSocketEventListener.WebSocketEvent.TYPE.CONNECT;
+import static org.atmosphere.websocket.WebSocketEventListener.WebSocketEvent.TYPE.MESSAGE;
+
+public class TomcatWebSocketHandler extends MessageInbound {
+
+ private static final Logger logger = LoggerFactory.getLogger(JettyWebSocketHandler.class);
+
+ private WebSocketProcessor webSocketProcessor;
+ private final AtmosphereRequest request;
+ private final AtmosphereFramework framework;
+ private final WebSocketProtocol webSocketProtocol;
+
+ public TomcatWebSocketHandler(AtmosphereRequest request, AtmosphereFramework framework, WebSocketProtocol webSocketProtocol) {
+ this.request = request;
+ this.framework = framework;
+ this.webSocketProtocol = webSocketProtocol;
+ }
+
+ @Override
+ protected void onOpen(WsOutbound outbound) {
+ logger.trace("WebSocket.onOpen.");
+ try {
+ webSocketProcessor = new WebSocketProcessor(framework, new TomcatWebSocket(outbound, framework.getAtmosphereConfig()), webSocketProtocol);
+ webSocketProcessor.dispatch(request);
+ webSocketProcessor.notifyListener(new WebSocketEventListener.WebSocketEvent("", CONNECT, webSocketProcessor.webSocket()));
+ } catch (Exception e) {
+ logger.warn("failed to connect to web socket", e);
+ }
+ }
+
+ @Override
+ protected void onClose(int closeCode) {
+ request.destroy();
+ if (webSocketProcessor == null) return;
+
+ webSocketProcessor.notifyListener(new WebSocketEventListener.WebSocketEvent("", CLOSE, webSocketProcessor.webSocket()));
+ webSocketProcessor.close(closeCode);
+ }
+
+ @Override
+ protected void onBinaryMessage(ByteBuffer message) throws IOException {
+ logger.trace("WebSocket.onMessage (bytes)");
+ webSocketProcessor.invokeWebSocketProtocol(message.array(), 0, message.array().length);
+ try {
+ webSocketProcessor.notifyListener(new WebSocketEventListener.WebSocketEvent(new String(message.array(), "UTF-8"), MESSAGE, webSocketProcessor.webSocket()));
+ } catch (UnsupportedEncodingException e) {
+ logger.warn("UnsupportedEncodingException", e);
+
+ }
+ }
+
+ @Override
+ protected void onTextMessage(CharBuffer message) throws IOException {
+ logger.trace("WebSocket.onMessage");
+ webSocketProcessor.invokeWebSocketProtocol(message.toString());
+ webSocketProcessor.notifyListener(new WebSocketEventListener.WebSocketEvent(message.toString(), MESSAGE, webSocketProcessor.webSocket()));
+ }
+}
View
108 modules/cpr/src/main/java/org/atmosphere/container/version/TomcatWebSocket.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2012 Jeanfrancois Arcand
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.atmosphere.container.version;
+
+import org.apache.catalina.websocket.WsOutbound;
+import org.atmosphere.cpr.AtmosphereConfig;
+import org.atmosphere.websocket.WebSocketAdapter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Tomcat WebSocket Support
+ *
+ * @author Jeanfrancois Arcand
+ */
+public class TomcatWebSocket extends WebSocketAdapter {
+
+ private final WsOutbound outbound;
+ private static final Logger logger = LoggerFactory.getLogger(TomcatWebSocket.class);
+ private final AtmosphereConfig config;
+ private final AtomicBoolean firstWrite = new AtomicBoolean(false);
+
+ public TomcatWebSocket(WsOutbound outbound, AtmosphereConfig config) {
+ this.outbound = outbound;
+ this.config = config;
+ }
+
+ @Override
+ public void redirect(String location) throws IOException {
+ logger.error("WebSocket Redirect not supported");
+ }
+
+ @Override
+ public void writeError(int errorCode, String message) throws IOException {
+ if (!firstWrite.get()) {
+ logger.debug("The WebSocket handshake succeeded but the dispatched URI failed {}:{}. " +
+ "The WebSocket connection is still open and client can continue sending messages.", message, errorCode);
+ } else {
+ logger.debug("{} {}", errorCode, message);
+ }
+ }
+
+ @Override
+ public void write(String data) throws IOException {
+ firstWrite.set(true);
+ logger.trace("WebSocket.write()");
+ outbound.writeTextMessage(CharBuffer.wrap(data));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void write(byte[] data) throws IOException {
+ firstWrite.set(true);
+ logger.trace("WebSocket.write()");
+ outbound.writeTextMessage(CharBuffer.wrap(new String(data)));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void write(byte[] data, int offset, int length) throws IOException {
+ firstWrite.set(true);
+ logger.trace("WebSocket.write()");
+ outbound.writeTextMessage(CharBuffer.wrap(new String(data, offset, length)));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void close() throws IOException {
+ logger.trace("WebSocket.close()");
+ outbound.close(1005, ByteBuffer.wrap(new byte[0]));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void flush() throws IOException {
+ }
+
+ @Override
+ public String toString() {
+ return outbound.toString();
+ }
+}
View
8 modules/cpr/src/main/java/org/atmosphere/cpr/AtmosphereRequest.java
@@ -313,6 +313,10 @@ public String getHeader(String s) {
return getHeader(s, true);
}
+ public HttpServletRequest wrappedRequest() {
+ return b.request;
+ }
+
public String getHeader(String s, boolean checkCase) {
if ("content-type".equalsIgnoreCase(s)) {
@@ -1424,7 +1428,6 @@ public final static AtmosphereRequest loadInMemory(HttpServletRequest request) {
isWrapped = true;
} else {
b = new Builder();
- b.request(request);
}
b.servletPath(request.getServletPath())
@@ -1436,7 +1439,8 @@ public final static AtmosphereRequest loadInMemory(HttpServletRequest request) {
.serverName(request.getServerName())
.serverPort(request.getServerPort())
.destroyable(false)
- .session(new FakeHttpSession(request.getSession(true)));
+ .session(new FakeHttpSession(request.getSession(true)))
+ .request(new NoOpsRequest());
Enumeration<String> e = request.getHeaderNames();
String s;
View
5 modules/cpr/src/main/java/org/atmosphere/cpr/DefaultAsyncSupportResolver.java
@@ -64,6 +64,7 @@
import org.atmosphere.container.NettyCometSupport;
import org.atmosphere.container.Servlet30AsyncSupportWithWebSocket;
import org.atmosphere.container.Servlet30CometSupport;
+import org.atmosphere.container.Tomcat7AsyncSupportWithWebSocket;
import org.atmosphere.container.Tomcat7CometSupport;
import org.atmosphere.container.TomcatCometSupport;
import org.atmosphere.container.WebLogicCometSupport;
@@ -85,6 +86,7 @@
public final static String SERVLET_30 = "javax.servlet.AsyncListener";
public final static String GLASSFISH_V2 = "com.sun.enterprise.web.PEWebContainer";
public final static String TOMCAT_7 = "org.apache.catalina.comet.CometFilterChain";
+ public final static String TOMCAT_WEBSOCKET = "org.apache.catalina.websocket.WebSocketServlet";
public final static String TOMCAT = "org.apache.coyote.http11.Http11NioProcessor";
public final static String JBOSS_5 = "org.jboss.";
public final static String JETTY = "org.mortbay.util.ajax.Continuation";
@@ -163,6 +165,9 @@ protected boolean testClassExists(final String testClass) {
public List<Class<? extends AsyncSupport>> detectWebSocketPresent() {
List l = new LinkedList<Class<? extends AsyncSupport>>() {
{
+ if (testClassExists(TOMCAT_WEBSOCKET))
+ add(Tomcat7AsyncSupportWithWebSocket.class);
+
if (testClassExists(JETTY_8))
add(JettyAsyncSupportWithWebSocket.class);
View
53 modules/cpr/src/main/java/org/atmosphere/websocket/WebSocketProcessor.java
@@ -183,35 +183,40 @@ public WebSocket webSocket() {
public void close(int closeCode) {
logger.debug("WebSocket closed with {}", closeCode);
AtmosphereResourceImpl resource = (AtmosphereResourceImpl) webSocket.resource();
- AtmosphereRequest r = resource.getRequest(false);
- AtmosphereResponse s = resource.getResponse(false);
- try {
- webSocketProtocol.onClose(webSocket);
-
- if (resource != null && resource.isInScope()) {
- AsynchronousProcessor.AsynchronousProcessorHook h = (AsynchronousProcessor.AsynchronousProcessorHook)
- r.getAttribute(ASYNCHRONOUS_HOOK);
- if (h != null) {
- if (closeCode == 1000) {
- h.timedOut();
+
+ if (resource == null) {
+ logger.warn("Unable to retrieve AtmosphereResource for {}", webSocket);
+ } else {
+ AtmosphereRequest r = resource.getRequest(false);
+ AtmosphereResponse s = resource.getResponse(false);
+ try {
+ webSocketProtocol.onClose(webSocket);
+
+ if (resource != null && resource.isInScope()) {
+ AsynchronousProcessor.AsynchronousProcessorHook h = (AsynchronousProcessor.AsynchronousProcessorHook)
+ r.getAttribute(ASYNCHRONOUS_HOOK);
+ if (h != null) {
+ if (closeCode == 1000) {
+ h.timedOut();
+ } else {
+ h.closed();
+ }
} else {
- h.closed();
+ logger.warn("AsynchronousProcessor.AsynchronousProcessorHook was null");
}
- } else {
- logger.warn("AsynchronousProcessor.AsynchronousProcessorHook was null");
}
- }
- } finally {
- if (r != null) {
- r.destroy();
- }
+ } finally {
+ if (r != null) {
+ r.destroy();
+ }
- if (s != null) {
- s.destroy();
- }
+ if (s != null) {
+ s.destroy();
+ }
- if (webSocket != null) {
- WebSocketAdapter.class.cast(webSocket).setAtmosphereResource(null);
+ if (webSocket != null) {
+ WebSocketAdapter.class.cast(webSocket).setAtmosphereResource(null);
+ }
}
}
asyncExecutor.shutdown();
View
2  modules/cpr/src/test/java/org/atmosphere/cpr/DefaultBroadcasterFactoryTest.java
@@ -20,7 +20,7 @@
import org.testng.annotations.Test;
/**
- * Unit tests for the {@link DefaultBroadcasterFactory}.
+ * Unit tests for the {@link org.atmosphere.cpr.DefaultBroadcasterFactory}.
*
* @author Jason Burgess
*/
View
1  modules/pom.xml
@@ -21,6 +21,7 @@
<module>compat-jbossweb</module>
<module>compat-weblogic</module>
<module>cpr</module>
+ <module>acceptance-tests</module>
<module>annotations</module>
<module>jersey</module>
<module>jquery</module>
View
4 pom.xml
@@ -472,8 +472,8 @@
<jboss-version>2.1.1.GA</jboss-version>
<grizzly10-version>1.0.31</grizzly10-version>
<jetty7-version>7.6.0.v20120127</jetty7-version>
- <tomcat-version>6.0.18</tomcat-version>
- <tomcat7-version>7.0.26</tomcat7-version>
+ <tomcat-version>6.0.35</tomcat-version>
+ <tomcat7-version>7.0.27</tomcat7-version>
<jgroups-version>2.8.0.GA</jgroups-version>
<common-logging-version>1.1.1</common-logging-version>
<scala.version>2.9.1</scala.version>
Please sign in to comment.
Something went wrong with that request. Please try again.