summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--WebContent/WEB-INF/web.xml16
-rw-r--r--client/src/com/vaadin/client/communication/AtmospherePushConnection.java2
-rw-r--r--push/ivy.xml6
-rw-r--r--server/ivy.xml6
-rw-r--r--server/src/com/vaadin/server/ServletPortletHelper.java20
-rw-r--r--server/src/com/vaadin/server/communication/JSR356WebsocketInitializer.java203
-rw-r--r--server/src/com/vaadin/server/communication/PushAtmosphereHandler.java120
-rw-r--r--server/src/com/vaadin/server/communication/PushHandler.java75
-rw-r--r--server/src/com/vaadin/server/communication/PushRequestHandler.java102
-rw-r--r--server/tests/src/com/vaadin/server/MockServletContext.java327
-rw-r--r--server/tests/src/com/vaadin/tests/server/ClassesSerializableTest.java2
-rw-r--r--uitest/src/com/vaadin/tests/integration/ServletIntegrationJSR356WebsocketUITest.java32
-rw-r--r--uitest/src/com/vaadin/tests/tb3/ServletIntegrationTests.java65
13 files changed, 903 insertions, 73 deletions
diff --git a/WebContent/WEB-INF/web.xml b/WebContent/WEB-INF/web.xml
index ef60364202..51ac907843 100644
--- a/WebContent/WEB-INF/web.xml
+++ b/WebContent/WEB-INF/web.xml
@@ -87,6 +87,17 @@
</servlet>
<servlet>
+ <servlet-name>VaadinApplicationRunnerWithJSR356</servlet-name>
+ <servlet-class>com.vaadin.launcher.ApplicationRunnerServlet</servlet-class>
+ <!-- Force web sockets to use JSR 356 standard -->
+ <init-param>
+ <param-name>org.atmosphere.cpr.asyncSupport</param-name>
+ <param-value>org.atmosphere.container.JSR356AsyncSupport</param-value>
+ </init-param>
+ <async-supported>true</async-supported>
+ </servlet>
+
+ <servlet>
<!-- This servlet is a separate instance for the sole purpose of
testing #12446 (com.vaadin.tests.components.ui.TimeoutRedirectResetsOnActivity)
because it modifies the VaadinService timeout parameters -->
@@ -149,6 +160,11 @@
</servlet-mapping>
<servlet-mapping>
+ <servlet-name>VaadinApplicationRunnerWithJSR356</servlet-name>
+ <url-pattern>/run-jsr356/*</url-pattern>
+ </servlet-mapping>
+
+ <servlet-mapping>
<servlet-name>IntegrationTest</servlet-name>
<url-pattern>/integration/*</url-pattern>
</servlet-mapping>
diff --git a/client/src/com/vaadin/client/communication/AtmospherePushConnection.java b/client/src/com/vaadin/client/communication/AtmospherePushConnection.java
index e544c91d0f..cd989d7ea4 100644
--- a/client/src/com/vaadin/client/communication/AtmospherePushConnection.java
+++ b/client/src/com/vaadin/client/communication/AtmospherePushConnection.java
@@ -197,7 +197,7 @@ public class AtmospherePushConnection implements PushConnection {
private void connect() {
String baseUrl = connection
.translateVaadinUri(ApplicationConstants.APP_PROTOCOL_PREFIX
- + ApplicationConstants.PUSH_PATH + '/');
+ + ApplicationConstants.PUSH_PATH);
String extraParams = UIConstants.UI_ID_PARAMETER + "="
+ connection.getConfiguration().getUIId();
diff --git a/push/ivy.xml b/push/ivy.xml
index 605f5d1a05..8827d92bc0 100644
--- a/push/ivy.xml
+++ b/push/ivy.xml
@@ -29,9 +29,9 @@
<dependencies>
<!-- API DEPENDENCIES -->
- <!--Servlet API version 2.4 -->
- <dependency org="javax.servlet" name="servlet-api"
- rev="2.4" conf="build-provided,ide,test -> default" />
+ <!-- @WebListener is used for JSR356 websockets so we need to compile with servlet 3 API -->
+ <dependency org="javax.servlet" name="javax.servlet-api"
+ rev="3.0.1" conf="build-provided,ide,test -> default" />
<!-- Atmosphere -->
<dependency org="com.vaadin.external.atmosphere"
diff --git a/server/ivy.xml b/server/ivy.xml
index b30e6a72ef..e9bc8b818d 100644
--- a/server/ivy.xml
+++ b/server/ivy.xml
@@ -26,9 +26,9 @@
<dependency org="com.liferay.portal" name="portal-service"
rev="6.0.2" conf="build-provided,ide -> default" />
- <!--Servlet API version 2.4 -->
- <dependency org="javax.servlet" name="servlet-api"
- rev="2.4" conf="build-provided,ide,test -> default" />
+ <!--Servlet API version 3.0 -->
+ <dependency org="javax.servlet" name="javax.servlet-api"
+ rev="3.0.1" conf="build-provided,ide,test -> default" />
<!--Portlet API version 2.0 (JSR-286) -->
<dependency org="javax.portlet" name="portlet-api"
diff --git a/server/src/com/vaadin/server/ServletPortletHelper.java b/server/src/com/vaadin/server/ServletPortletHelper.java
index 197d9fe416..33ecf16e17 100644
--- a/server/src/com/vaadin/server/ServletPortletHelper.java
+++ b/server/src/com/vaadin/server/ServletPortletHelper.java
@@ -102,6 +102,24 @@ public class ServletPortletHelper implements Serializable {
return false;
}
+ private static boolean isPathInfo(VaadinRequest request, String string) {
+ String pathInfo = request.getPathInfo();
+
+ if (pathInfo == null) {
+ return false;
+ }
+
+ if (!string.startsWith("/")) {
+ string = '/' + string;
+ }
+
+ if (pathInfo.equals(string)) {
+ return true;
+ }
+
+ return false;
+ }
+
public static boolean isFileUploadRequest(VaadinRequest request) {
return hasPathPrefix(request, UPLOAD_URL_PREFIX);
}
@@ -124,7 +142,7 @@ public class ServletPortletHelper implements Serializable {
}
public static boolean isPushRequest(VaadinRequest request) {
- return hasPathPrefix(request, ApplicationConstants.PUSH_PATH + '/');
+ return isPathInfo(request, ApplicationConstants.PUSH_PATH);
}
public static void initDefaultUIProvider(VaadinSession session,
diff --git a/server/src/com/vaadin/server/communication/JSR356WebsocketInitializer.java b/server/src/com/vaadin/server/communication/JSR356WebsocketInitializer.java
new file mode 100644
index 0000000000..fd61f6180f
--- /dev/null
+++ b/server/src/com/vaadin/server/communication/JSR356WebsocketInitializer.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2000-2014 Vaadin Ltd.
+ *
+ * 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 com.vaadin.server.communication;
+
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+import javax.servlet.ServletRegistration;
+import javax.servlet.annotation.WebListener;
+
+import org.atmosphere.cpr.AtmosphereFramework;
+
+import com.vaadin.server.VaadinServlet;
+
+/**
+ * Initializer class for JSR 356 websockets.
+ * <p>
+ * Websocket specification says that initialization of websocket end points
+ * should be done in the servlet context initialization phase. Some servers
+ * implement this strictly so that end points cannot be registered after the
+ * context initialization phase.
+ * <p>
+ * Note that {@link WebListener} is Servlet 3.0 API so this will not be run for
+ * older servers (unless added to web.xml), but these servers do not support JSR
+ * 356 websockets either.
+ *
+ * @since
+ * @author Vaadin Ltd
+ */
+@WebListener
+public class JSR356WebsocketInitializer implements ServletContextListener {
+
+ private static boolean atmosphereAvailable = false;
+ static {
+ try {
+ org.atmosphere.util.Version.getRawVersion();
+ atmosphereAvailable = true;
+ } catch (NoClassDefFoundError e) {
+ }
+ }
+
+ /**
+ * "ServletConfig" which only provides information from a
+ * {@link ServletRegistration} and its {@link ServletContext}
+ */
+ public static class FakeServletConfig implements ServletConfig {
+
+ private ServletRegistration servletRegistration;
+ private ServletContext servletContext;
+
+ public FakeServletConfig(ServletRegistration servletRegistration,
+ ServletContext servletContext) {
+ this.servletContext = servletContext;
+ this.servletRegistration = servletRegistration;
+ }
+
+ @Override
+ public String getServletName() {
+ return servletRegistration.getName();
+ }
+
+ @Override
+ public ServletContext getServletContext() {
+ return servletContext;
+ }
+
+ @Override
+ public String getInitParameter(String name) {
+ return servletRegistration.getInitParameter(name);
+ }
+
+ @Override
+ public Enumeration<String> getInitParameterNames() {
+ return Collections.enumeration(servletRegistration
+ .getInitParameters().keySet());
+ }
+
+ }
+
+ @Override
+ public void contextInitialized(ServletContextEvent sce) {
+ ServletContext servletContext = sce.getServletContext();
+ if (servletContext.getMajorVersion() < 3) {
+ return;
+ }
+
+ if (!atmosphereAvailable) {
+ return;
+ }
+
+ Map<String, ? extends ServletRegistration> regs = servletContext
+ .getServletRegistrations();
+ for (String servletName : regs.keySet()) {
+ ServletRegistration servletRegistration = regs.get(servletName);
+
+ if (isVaadinServlet(servletRegistration)) {
+ try {
+ initAtmosphereForVaadinServlet(servletRegistration,
+ servletContext);
+ } catch (Exception e) {
+ getLogger().log(
+ Level.WARNING,
+ "Failed to initialize Atmosphere for "
+ + servletName, e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Initializes Atmosphere for use with the given Vaadin servlet
+ * <p>
+ * For JSR 356 websockets to work properly, the initialization must be done
+ * in the servlet context initialization phase.
+ *
+ * @param servletRegistration
+ * The servlet registration info for the servlet
+ * @param servletContext
+ */
+ public static void initAtmosphereForVaadinServlet(
+ ServletRegistration servletRegistration,
+ ServletContext servletContext) {
+ String servletName = servletRegistration.getName();
+ String attributeName = getAttributeName(servletName);
+
+ if (servletContext.getAttribute(attributeName) != null) {
+ // Already initialized
+ getLogger().warning("Atmosphere already initialized");
+ return;
+ }
+ getLogger().finer("Creating AtmosphereFramework for " + servletName);
+ AtmosphereFramework framework = PushRequestHandler
+ .initAtmosphere(new FakeServletConfig(servletRegistration,
+ servletContext));
+ servletContext.setAttribute(attributeName, framework);
+ getLogger().finer("Created AtmosphereFramework for " + servletName);
+
+ }
+
+ /**
+ * Returns the name of the attribute in the servlet context where the
+ * pre-initialized Atmosphere object is stored
+ *
+ * @param servletName
+ * The name of the servlet
+ * @return The attribute name which contains the initialized Atmosphere
+ * object
+ */
+ public static String getAttributeName(String servletName) {
+ return JSR356WebsocketInitializer.class.getName() + "." + servletName;
+ }
+
+ /**
+ * Tries to determine if the given servlet registration refers to a Vaadin
+ * servlet.
+ *
+ * @param servletRegistration
+ * The servlet registration info for the servlet
+ * @return false if the servlet is definitely not a Vaadin servlet, true
+ * otherwise
+ */
+ protected boolean isVaadinServlet(ServletRegistration servletRegistration) {
+ try {
+ Class<?> servletClass = Class.forName(servletRegistration
+ .getClassName());
+ return VaadinServlet.class.isAssignableFrom(servletClass);
+ } catch (Exception e) {
+ // This will fail in OSGi environments, assume everything is a
+ // VaadinServlet
+ return true;
+ }
+ }
+
+ private static final Logger getLogger() {
+ return Logger.getLogger(JSR356WebsocketInitializer.class.getName());
+ }
+
+ @Override
+ public void contextDestroyed(ServletContextEvent sce) {
+ // Nothing to do here
+ }
+
+}
diff --git a/server/src/com/vaadin/server/communication/PushAtmosphereHandler.java b/server/src/com/vaadin/server/communication/PushAtmosphereHandler.java
new file mode 100644
index 0000000000..0e94eaa75f
--- /dev/null
+++ b/server/src/com/vaadin/server/communication/PushAtmosphereHandler.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2000-2014 Vaadin Ltd.
+ *
+ * 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 com.vaadin.server.communication;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.atmosphere.cpr.AtmosphereRequest;
+import org.atmosphere.cpr.AtmosphereResource;
+import org.atmosphere.cpr.AtmosphereResourceEvent;
+import org.atmosphere.cpr.AtmosphereResourceEventListenerAdapter;
+import org.atmosphere.handler.AbstractReflectorAtmosphereHandler;
+
+/**
+ * Handles Atmosphere requests and forwards them to logical methods in
+ * {@link PushHandler}
+ *
+ * @since
+ * @author Vaadin Ltd
+ */
+public class PushAtmosphereHandler extends AbstractReflectorAtmosphereHandler
+ implements Serializable {
+
+ private PushHandler pushHandler = null;
+
+ public void setPushHandler(PushHandler pushHandler) {
+ this.pushHandler = pushHandler;
+ }
+
+ private static final Logger getLogger() {
+ return Logger.getLogger(PushAtmosphereHandler.class.getName());
+ }
+
+ @Override
+ public void onStateChange(AtmosphereResourceEvent event) throws IOException {
+ super.onStateChange(event);
+ if (pushHandler == null) {
+ getLogger()
+ .warning(
+ "AtmosphereHandler.onStateChange called before PushHandler has been set. This should really not happen");
+ return;
+ }
+
+ if (event.isCancelled() || event.isResumedOnTimeout()) {
+ pushHandler.connectionLost(event);
+ }
+ }
+
+ @Override
+ public void onRequest(AtmosphereResource resource) {
+ if (pushHandler == null) {
+ getLogger()
+ .warning(
+ "AtmosphereHandler.onRequest called before PushHandler has been set. This should really not happen");
+ return;
+ }
+
+ AtmosphereRequest req = resource.getRequest();
+
+ if (req.getMethod().equalsIgnoreCase("GET")) {
+ onConnect(resource);
+ } else if (req.getMethod().equalsIgnoreCase("POST")) {
+ onMessage(resource);
+ }
+ }
+
+ /**
+ * Called when the client sends a message through the push channel
+ *
+ * @param resource
+ */
+ private void onMessage(AtmosphereResource resource) {
+ pushHandler.onMessage(resource);
+ }
+
+ /**
+ * Called when the client sends the first request (to establish a push
+ * connection)
+ *
+ * @param resource
+ */
+ private void onConnect(AtmosphereResource resource) {
+ resource.addEventListener(new AtmosphereResourceListener());
+
+ pushHandler.onConnect(resource);
+ }
+
+ private class AtmosphereResourceListener extends
+ AtmosphereResourceEventListenerAdapter implements Serializable {
+
+ @Override
+ public void onDisconnect(AtmosphereResourceEvent event) {
+ // Log event on trace level
+ super.onDisconnect(event);
+ pushHandler.connectionLost(event);
+ }
+
+ @Override
+ public void onThrowable(AtmosphereResourceEvent event) {
+ getLogger().log(Level.SEVERE, "Exception in push connection",
+ event.throwable());
+ pushHandler.connectionLost(event);
+ }
+ }
+}
diff --git a/server/src/com/vaadin/server/communication/PushHandler.java b/server/src/com/vaadin/server/communication/PushHandler.java
index ea937d279e..552f2e9e03 100644
--- a/server/src/com/vaadin/server/communication/PushHandler.java
+++ b/server/src/com/vaadin/server/communication/PushHandler.java
@@ -22,14 +22,11 @@ import java.util.Collection;
import java.util.logging.Level;
import java.util.logging.Logger;
-import org.atmosphere.cpr.AtmosphereHandler;
import org.atmosphere.cpr.AtmosphereRequest;
import org.atmosphere.cpr.AtmosphereResource;
import org.atmosphere.cpr.AtmosphereResource.TRANSPORT;
import org.atmosphere.cpr.AtmosphereResourceEvent;
-import org.atmosphere.cpr.AtmosphereResourceEventListenerAdapter;
import org.atmosphere.cpr.AtmosphereResourceImpl;
-import org.atmosphere.handler.AbstractReflectorAtmosphereHandler;
import com.vaadin.server.ErrorEvent;
import com.vaadin.server.ErrorHandler;
@@ -50,37 +47,13 @@ import com.vaadin.ui.UI;
import elemental.json.JsonException;
/**
- * Establishes bidirectional ("push") communication channels
+ * Handles incoming push connections and messages and dispatches them to the
+ * correct {@link UI}/ {@link AtmospherePushConnection}
*
* @author Vaadin Ltd
* @since 7.1
*/
-public class PushHandler extends AtmosphereResourceEventListenerAdapter {
-
- AtmosphereHandler handler = new AbstractReflectorAtmosphereHandler() {
-
- @Override
- public void onStateChange(AtmosphereResourceEvent event)
- throws IOException {
- super.onStateChange(event);
- if (event.isCancelled() || event.isResumedOnTimeout()) {
- connectionLost(event);
- }
- }
-
- @Override
- public void onRequest(AtmosphereResource resource) {
- AtmosphereRequest req = resource.getRequest();
-
- if (req.getMethod().equalsIgnoreCase("GET")) {
- callWithUi(resource, establishCallback, false);
- } else if (req.getMethod().equalsIgnoreCase("POST")) {
- callWithUi(resource, receiveCallback,
- resource.transport() == TRANSPORT.WEBSOCKET);
- }
- }
-
- };
+public class PushHandler {
/**
* Callback interface used internally to process an event with the
@@ -103,8 +76,6 @@ public class PushHandler extends AtmosphereResourceEventListenerAdapter {
"New push connection for resource {0} with transport {1}",
new Object[] { resource.uuid(), resource.transport() });
- resource.addEventListener(PushHandler.this);
-
resource.getResponse().setContentType("text/plain; charset=UTF-8");
VaadinSession session = ui.getSession();
@@ -325,21 +296,7 @@ public class PushHandler extends AtmosphereResourceEventListenerAdapter {
}
}
- @Override
- public void onDisconnect(AtmosphereResourceEvent event) {
- // Log event on trace level
- super.onDisconnect(event);
- connectionLost(event);
- }
-
- @Override
- public void onThrowable(AtmosphereResourceEvent event) {
- getLogger().log(Level.SEVERE, "Exception in push connection",
- event.throwable());
- connectionLost(event);
- }
-
- private void connectionLost(AtmosphereResourceEvent event) {
+ void connectionLost(AtmosphereResourceEvent event) {
// We don't want to use callWithUi here, as it assumes there's a client
// request active and does requestStart and requestEnd among other
// things.
@@ -506,4 +463,28 @@ public class PushHandler extends AtmosphereResourceEventListenerAdapter {
private static final Logger getLogger() {
return Logger.getLogger(PushHandler.class.getName());
}
+
+ /**
+ * Called when a new push connection is requested to be opened by the client
+ *
+ * @since
+ * @param resource
+ * The related atmosphere resources
+ */
+ void onConnect(AtmosphereResource resource) {
+ callWithUi(resource, establishCallback, false);
+ }
+
+ /**
+ * Called when a message is received through the push connection
+ *
+ * @since
+ * @param resource
+ * The related atmosphere resources
+ */
+ void onMessage(AtmosphereResource resource) {
+ callWithUi(resource, receiveCallback,
+ resource.transport() == TRANSPORT.WEBSOCKET);
+ }
+
}
diff --git a/server/src/com/vaadin/server/communication/PushRequestHandler.java b/server/src/com/vaadin/server/communication/PushRequestHandler.java
index 74165a4988..0e3ec300b4 100644
--- a/server/src/com/vaadin/server/communication/PushRequestHandler.java
+++ b/server/src/com/vaadin/server/communication/PushRequestHandler.java
@@ -17,6 +17,8 @@
package com.vaadin.server.communication;
import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
@@ -25,6 +27,8 @@ import org.atmosphere.cache.UUIDBroadcasterCache;
import org.atmosphere.client.TrackMessageSizeInterceptor;
import org.atmosphere.cpr.ApplicationConfig;
import org.atmosphere.cpr.AtmosphereFramework;
+import org.atmosphere.cpr.AtmosphereFramework.AtmosphereHandlerWrapper;
+import org.atmosphere.cpr.AtmosphereHandler;
import org.atmosphere.cpr.AtmosphereInterceptor;
import org.atmosphere.cpr.AtmosphereRequest;
import org.atmosphere.cpr.AtmosphereResponse;
@@ -48,7 +52,8 @@ import com.vaadin.shared.communication.PushConstants;
/**
* Handles requests to open a push (bidirectional) communication channel between
* the client and the server. After the initial request, communication through
- * the push channel is managed by {@link PushHandler}.
+ * the push channel is managed by {@link PushAtmosphereHandler} and
+ * {@link PushHandler}
*
* @author Vaadin Ltd
* @since 7.1
@@ -62,10 +67,85 @@ public class PushRequestHandler implements RequestHandler,
public PushRequestHandler(VaadinServletService service)
throws ServiceException {
+ service.addServiceDestroyListener(new ServiceDestroyListener() {
+ @Override
+ public void serviceDestroy(ServiceDestroyEvent event) {
+ destroy();
+ }
+ });
+
final ServletConfig vaadinServletConfig = service.getServlet()
.getServletConfig();
- atmosphere = new AtmosphereFramework() {
+ pushHandler = new PushHandler(service);
+
+ atmosphere = getPreInitializedAtmosphere(vaadinServletConfig);
+ if (atmosphere == null) {
+ // Not initialized by JSR356WebsocketInitializer
+ getLogger().fine(
+ "Initializing Atmosphere for servlet "
+ + vaadinServletConfig.getServletName());
+ try {
+ atmosphere = initAtmosphere(vaadinServletConfig);
+ } catch (Exception e) {
+ getLogger().log(
+ Level.WARNING,
+ "Failed to initialize Atmosphere for "
+ + service.getServlet().getServletName()
+ + ". Push will not work.", e);
+ return;
+ }
+ } else {
+ getLogger().fine(
+ "Using pre-initialized Atmosphere for servlet "
+ + vaadinServletConfig.getServletName());
+ }
+
+ for (AtmosphereHandlerWrapper handlerWrapper : atmosphere
+ .getAtmosphereHandlers().values()) {
+ AtmosphereHandler handler = handlerWrapper.atmosphereHandler;
+ if (handler instanceof PushAtmosphereHandler) {
+ // Map the (possibly pre-initialized) handler to the actual push
+ // handler
+ ((PushAtmosphereHandler) handler).setPushHandler(pushHandler);
+ }
+
+ }
+ }
+
+ private static final Logger getLogger() {
+ return Logger.getLogger(PushRequestHandler.class.getName());
+ }
+
+ /**
+ * Returns an AtmosphereFramework instance which was initialized in the
+ * servlet context init phase by {@link JSR356WebsocketInitializer}, if such
+ * exists
+ */
+ private AtmosphereFramework getPreInitializedAtmosphere(
+ ServletConfig vaadinServletConfig) {
+ String attributeName = JSR356WebsocketInitializer
+ .getAttributeName(vaadinServletConfig.getServletName());
+ Object framework = vaadinServletConfig.getServletContext()
+ .getAttribute(attributeName);
+ if (framework != null && framework instanceof AtmosphereFramework) {
+ return (AtmosphereFramework) framework;
+ }
+
+ return null;
+ }
+
+ /**
+ * Initializes Atmosphere for the given ServletConfiguration
+ *
+ * @since
+ * @param vaadinServletConfig
+ * The servlet configuration for the servlet which should have
+ * Atmosphere support
+ */
+ static AtmosphereFramework initAtmosphere(
+ final ServletConfig vaadinServletConfig) {
+ AtmosphereFramework atmosphere = new AtmosphereFramework() {
@Override
protected void analytics() {
// Overridden to disable version number check
@@ -81,15 +161,7 @@ public class PushRequestHandler implements RequestHandler,
}
};
- service.addServiceDestroyListener(new ServiceDestroyListener() {
- @Override
- public void serviceDestroy(ServiceDestroyEvent event) {
- destroy();
- }
- });
-
- pushHandler = new PushHandler(service);
- atmosphere.addAtmosphereHandler("/*", pushHandler.handler);
+ atmosphere.addAtmosphereHandler("/*", new PushAtmosphereHandler());
atmosphere.addInitParameter(ApplicationConfig.BROADCASTER_CACHE,
UUIDBroadcasterCache.class.getName());
atmosphere.addInitParameter(ApplicationConfig.ANNOTATION_PROCESSOR,
@@ -131,8 +203,9 @@ public class PushRequestHandler implements RequestHandler,
trackMessageSize.configure(atmosphere.getAtmosphereConfig());
atmosphere.interceptor(trackMessageSize);
} catch (ServletException e) {
- throw new ServiceException("Atmosphere init failed", e);
+ throw new RuntimeException("Atmosphere init failed", e);
}
+ return atmosphere;
}
@Override
@@ -144,6 +217,11 @@ public class PushRequestHandler implements RequestHandler,
}
if (request instanceof VaadinServletRequest) {
+ if (atmosphere == null) {
+ response.sendError(500,
+ "Atmosphere initialization failed. No push available.");
+ return true;
+ }
try {
atmosphere.doCometSupport(AtmosphereRequest
.wrap((VaadinServletRequest) request),
diff --git a/server/tests/src/com/vaadin/server/MockServletContext.java b/server/tests/src/com/vaadin/server/MockServletContext.java
index 031c83ae90..45ec700c40 100644
--- a/server/tests/src/com/vaadin/server/MockServletContext.java
+++ b/server/tests/src/com/vaadin/server/MockServletContext.java
@@ -24,12 +24,21 @@ import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import java.util.Enumeration;
+import java.util.EventListener;
+import java.util.Map;
import java.util.Set;
+import javax.servlet.Filter;
+import javax.servlet.FilterRegistration;
import javax.servlet.RequestDispatcher;
import javax.servlet.Servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
+import javax.servlet.ServletRegistration;
+import javax.servlet.ServletRegistration.Dynamic;
+import javax.servlet.SessionCookieConfig;
+import javax.servlet.SessionTrackingMode;
+import javax.servlet.descriptor.JspConfigDescriptor;
/**
*
@@ -56,7 +65,7 @@ public class MockServletContext implements ServletContext {
*/
@Override
public int getMajorVersion() {
- return 2;
+ return 3;
}
/*
@@ -66,7 +75,7 @@ public class MockServletContext implements ServletContext {
*/
@Override
public int getMinorVersion() {
- return 4;
+ return 0;
}
/*
@@ -153,8 +162,7 @@ public class MockServletContext implements ServletContext {
*/
@Override
public Enumeration getServlets() {
- // TODO Auto-generated method stub
- return null;
+ return Collections.enumeration(Collections.EMPTY_SET);
}
/*
@@ -301,4 +309,315 @@ public class MockServletContext implements ServletContext {
return null;
}
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.ServletContext#getContextPath()
+ */
+ @Override
+ public String getContextPath() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.ServletContext#getEffectiveMajorVersion()
+ */
+ @Override
+ public int getEffectiveMajorVersion() {
+ return 3;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.ServletContext#getEffectiveMinorVersion()
+ */
+ @Override
+ public int getEffectiveMinorVersion() {
+ return 0;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.ServletContext#setInitParameter(java.lang.String,
+ * java.lang.String)
+ */
+ @Override
+ public boolean setInitParameter(String name, String value) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.ServletContext#addServlet(java.lang.String,
+ * java.lang.String)
+ */
+ @Override
+ public Dynamic addServlet(String servletName, String className) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.ServletContext#addServlet(java.lang.String,
+ * javax.servlet.Servlet)
+ */
+ @Override
+ public Dynamic addServlet(String servletName, Servlet servlet) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.ServletContext#addServlet(java.lang.String,
+ * java.lang.Class)
+ */
+ @Override
+ public Dynamic addServlet(String servletName,
+ Class<? extends Servlet> servletClass) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.ServletContext#createServlet(java.lang.Class)
+ */
+ @Override
+ public <T extends Servlet> T createServlet(Class<T> clazz)
+ throws ServletException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * javax.servlet.ServletContext#getServletRegistration(java.lang.String)
+ */
+ @Override
+ public ServletRegistration getServletRegistration(String servletName) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.ServletContext#getServletRegistrations()
+ */
+ @Override
+ public Map<String, ? extends ServletRegistration> getServletRegistrations() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.ServletContext#addFilter(java.lang.String,
+ * java.lang.String)
+ */
+ @Override
+ public javax.servlet.FilterRegistration.Dynamic addFilter(
+ String filterName, String className) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.ServletContext#addFilter(java.lang.String,
+ * javax.servlet.Filter)
+ */
+ @Override
+ public javax.servlet.FilterRegistration.Dynamic addFilter(
+ String filterName, Filter filter) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.ServletContext#addFilter(java.lang.String,
+ * java.lang.Class)
+ */
+ @Override
+ public javax.servlet.FilterRegistration.Dynamic addFilter(
+ String filterName, Class<? extends Filter> filterClass) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.ServletContext#createFilter(java.lang.Class)
+ */
+ @Override
+ public <T extends Filter> T createFilter(Class<T> clazz)
+ throws ServletException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.ServletContext#getFilterRegistration(java.lang.String)
+ */
+ @Override
+ public FilterRegistration getFilterRegistration(String filterName) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.ServletContext#getFilterRegistrations()
+ */
+ @Override
+ public Map<String, ? extends FilterRegistration> getFilterRegistrations() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.ServletContext#getSessionCookieConfig()
+ */
+ @Override
+ public SessionCookieConfig getSessionCookieConfig() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.ServletContext#setSessionTrackingModes(java.util.Set)
+ */
+ @Override
+ public void setSessionTrackingModes(
+ Set<SessionTrackingMode> sessionTrackingModes) {
+ // TODO Auto-generated method stub
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.ServletContext#getDefaultSessionTrackingModes()
+ */
+ @Override
+ public Set<SessionTrackingMode> getDefaultSessionTrackingModes() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.ServletContext#getEffectiveSessionTrackingModes()
+ */
+ @Override
+ public Set<SessionTrackingMode> getEffectiveSessionTrackingModes() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.ServletContext#addListener(java.lang.String)
+ */
+ @Override
+ public void addListener(String className) {
+ // TODO Auto-generated method stub
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.ServletContext#addListener(java.util.EventListener)
+ */
+ @Override
+ public <T extends EventListener> void addListener(T t) {
+ // TODO Auto-generated method stub
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.ServletContext#addListener(java.lang.Class)
+ */
+ @Override
+ public void addListener(Class<? extends EventListener> listenerClass) {
+ // TODO Auto-generated method stub
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.ServletContext#createListener(java.lang.Class)
+ */
+ @Override
+ public <T extends EventListener> T createListener(Class<T> clazz)
+ throws ServletException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.ServletContext#getJspConfigDescriptor()
+ */
+ @Override
+ public JspConfigDescriptor getJspConfigDescriptor() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.ServletContext#getClassLoader()
+ */
+ @Override
+ public ClassLoader getClassLoader() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.servlet.ServletContext#declareRoles(java.lang.String[])
+ */
+ @Override
+ public void declareRoles(String... roleNames) {
+ // TODO Auto-generated method stub
+
+ }
+
}
diff --git a/server/tests/src/com/vaadin/tests/server/ClassesSerializableTest.java b/server/tests/src/com/vaadin/tests/server/ClassesSerializableTest.java
index 0de8fd6aab..9603032ce5 100644
--- a/server/tests/src/com/vaadin/tests/server/ClassesSerializableTest.java
+++ b/server/tests/src/com/vaadin/tests/server/ClassesSerializableTest.java
@@ -16,7 +16,6 @@ import java.util.jar.JarFile;
import junit.framework.TestCase;
-import org.junit.Ignore;
import org.junit.Test;
public class ClassesSerializableTest extends TestCase {
@@ -80,6 +79,7 @@ public class ClassesSerializableTest extends TestCase {
"com\\.vaadin\\.external\\..*", //
"com\\.vaadin\\.util\\.WeakValueMap.*", //
"com\\.vaadin\\.themes\\.valoutil\\.BodyStyleName", //
+ "com\\.vaadin\\.server\\.communication\\.JSR356WebsocketInitializer.*", //
};
/**
diff --git a/uitest/src/com/vaadin/tests/integration/ServletIntegrationJSR356WebsocketUITest.java b/uitest/src/com/vaadin/tests/integration/ServletIntegrationJSR356WebsocketUITest.java
new file mode 100644
index 0000000000..f118d38158
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/integration/ServletIntegrationJSR356WebsocketUITest.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2000-2014 Vaadin Ltd.
+ *
+ * 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 com.vaadin.tests.integration;
+
+public class ServletIntegrationJSR356WebsocketUITest extends
+ AbstractServletIntegrationTest {
+ // Uses the test method declared in the super class
+
+ @Override
+ protected String getDeploymentPath(Class<?> uiClass) {
+ return super.getDeploymentPath(uiClass)
+ .replace("/run/", "/run-jsr356/");
+ }
+
+ @Override
+ protected Class<?> getUIClass() {
+ return ServletIntegrationWebsocketUI.class;
+ }
+}
diff --git a/uitest/src/com/vaadin/tests/tb3/ServletIntegrationTests.java b/uitest/src/com/vaadin/tests/tb3/ServletIntegrationTests.java
index 77c5676215..2f97ce27c3 100644
--- a/uitest/src/com/vaadin/tests/tb3/ServletIntegrationTests.java
+++ b/uitest/src/com/vaadin/tests/tb3/ServletIntegrationTests.java
@@ -17,21 +17,84 @@
package com.vaadin.tests.tb3;
import java.io.IOException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
import org.junit.runner.RunWith;
import org.junit.runners.model.InitializationError;
import com.vaadin.tests.integration.AbstractServletIntegrationTest;
+import com.vaadin.tests.integration.ServletIntegrationJSR356WebsocketUITest;
+import com.vaadin.tests.integration.ServletIntegrationWebsocketUITest;
import com.vaadin.tests.tb3.ServletIntegrationTests.ServletIntegrationTestSuite;
@RunWith(ServletIntegrationTestSuite.class)
public class ServletIntegrationTests {
+ public static Set<String> notJSR356Compatible = new HashSet<String>();
+ public static Set<String> notWebsocketCompatible = new HashSet<String>();
+ static {
+ notJSR356Compatible.add("jboss4");
+ notJSR356Compatible.add("jboss5");
+ notJSR356Compatible.add("jboss6");
+ notJSR356Compatible.add("jbosseap6");
+ notJSR356Compatible.add("jboss7");
+
+ notJSR356Compatible.add("jetty7");
+ notJSR356Compatible.add("jetty8");
+
+ notJSR356Compatible.add("glassfish3");
+
+ notJSR356Compatible.add("tomcat6");
+ notJSR356Compatible.add("tomcat7");
+ notJSR356Compatible.add("tomcat7apacheproxy");
+ notJSR356Compatible.add("weblogic10");
+ notJSR356Compatible.add("osgi"); // Karaf 3, Jetty 8
+
+ notWebsocketCompatible.add("glassfish2");
+ // In theory GF3 could work but in reality broken
+ notWebsocketCompatible.add("glassfish3");
+ notWebsocketCompatible.add("jboss4");
+ notWebsocketCompatible.add("jboss5");
+ notWebsocketCompatible.add("jboss6");
+ notWebsocketCompatible.add("jboss7");
+ notWebsocketCompatible.add("jbosseap6");
+ notWebsocketCompatible.add("jetty5");
+ notWebsocketCompatible.add("jetty6");
+ notWebsocketCompatible.add("tomcat5");
+ notWebsocketCompatible.add("tomcat6");
+ notWebsocketCompatible.add("tomcat7apacheproxy");
+ notWebsocketCompatible.add("weblogic10");
+ }
+
public static class ServletIntegrationTestSuite extends TB3TestSuite {
public ServletIntegrationTestSuite(Class<?> klass)
throws InitializationError, IOException {
super(klass, AbstractServletIntegrationTest.class,
- "com.vaadin.tests.integration", new String[] {});
+ "com.vaadin.tests.integration", new String[] {},
+ new ServletTestLocator());
+ }
+ }
+
+ public static class ServletTestLocator extends TB3TestLocator {
+ @Override
+ protected <T> List<Class<? extends T>> findClasses(Class<T> baseClass,
+ String basePackage, String[] ignoredPackages)
+ throws IOException {
+ List<Class<? extends T>> allClasses = super.findClasses(baseClass,
+ basePackage, ignoredPackages);
+ String serverName = System.getProperty("server-name");
+
+ if (notJSR356Compatible.contains(serverName)) {
+ allClasses
+ .remove(ServletIntegrationJSR356WebsocketUITest.class);
+ }
+
+ if (notWebsocketCompatible.contains(serverName)) {
+ allClasses.remove(ServletIntegrationWebsocketUITest.class);
+ }
+ return allClasses;
}
}
}