/*
* Copyright (c) 2008-2017 the original author or authors.
*
* 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.cometd.websocket;
import java.util.Arrays;
import java.util.Map;
import javax.websocket.ContainerProvider;
import javax.websocket.WebSocketContainer;
import org.cometd.bayeux.server.BayeuxServer;
import org.cometd.client.BayeuxClient;
import org.cometd.client.transport.ClientTransport;
import org.cometd.client.transport.LongPollingTransport;
import org.cometd.server.BayeuxServerImpl;
import org.cometd.server.CometDServlet;
import org.cometd.server.transport.JSONTransport;
import org.cometd.websocket.server.JettyWebSocketTransport;
import org.cometd.websocket.server.WebSocketTransport;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer;
import org.eclipse.jetty.websocket.server.WebSocketUpgradeFilter;
import org.junit.After;
import org.junit.Rule;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public abstract class ClientServerWebSocketTest {
protected static final String WEBSOCKET_JSR_356 = "JSR 356";
protected static final String WEBSOCKET_JETTY = "JETTY";
@Parameterized.Parameters(name = "{index}: WebSocket implementation: {0}")
public static Iterable<Object[]> data() {
return Arrays.asList(new Object[][]
{
{WEBSOCKET_JSR_356},
{WEBSOCKET_JETTY}
}
);
}
@Rule
public final TestWatcher testName = new TestWatcher() {
@Override
protected void starting(Description description) {
super.starting(description);
System.err.printf("Running %s.%s%n", description.getTestClass().getName(), description.getMethodName());
}
};
protected final String wsTransportType;
protected ServerConnector connector;
protected Server server;
protected ServletContextHandler context;
protected String cometdServletPath;
protected HttpClient httpClient;
protected WebSocketContainer wsClientContainer;
protected WebSocketClient wsClient;
protected String cometdURL;
protected BayeuxServerImpl bayeux;
protected ClientServerWebSocketTest(String wsTransportType) {
this.wsTransportType = wsTransportType;
}
protected void prepareAndStart(Map<String, String> initParams) throws Exception {
prepareAndStart("/cometd", initParams);
}
protected void prepareAndStart(String servletPath, Map<String, String> initParams) throws Exception {
prepareServer(0, servletPath, initParams, true);
prepareClient();
startServer();
startClient();
}
protected void prepareServer(int port, Map<String, String> initParams, boolean eager) throws Exception {
prepareServer(port, "/cometd", initParams, eager);
}
protected void prepareServer(int port, String servletPath, Map<String, String> initParams, boolean eager) throws Exception {
String wsTransportClass;
switch (wsTransportType) {
case WEBSOCKET_JSR_356:
wsTransportClass = WebSocketTransport.class.getName();
break;
case WEBSOCKET_JETTY:
wsTransportClass = JettyWebSocketTransport.class.getName();
break;
default:
throw new IllegalArgumentException();
}
prepareServer(port, servletPath, initParams, eager, wsTransportClass);
}
protected void prepareServer(int port, String servletPath, Map<String, String> initParams, boolean eager, String wsTransportClass) throws Exception {
QueuedThreadPool serverThreads = new QueuedThreadPool();
serverThreads.setName("server");
server = new Server(serverThreads);
connector = new ServerConnector(server);
connector.setPort(port);
server.addConnector(connector);
context = new ServletContextHandler(server, "/", true, false);
switch (wsTransportType) {
case WEBSOCKET_JSR_356:
WebSocketServerContainerInitializer.configureContext(context);
break;
case WEBSOCKET_JETTY:
WebSocketUpgradeFilter.configureContext(context);
break;
default:
throw new IllegalArgumentException();
}
// CometD servlet
cometdServletPath = servletPath;
if (cometdServletPath.endsWith("/*")) {
cometdServletPath = cometdServletPath.substring(0, cometdServletPath.length() - 2);
}
String cometdURLMapping = cometdServletPath;
if (!cometdURLMapping.endsWith("/*")) {
cometdURLMapping = cometdURLMapping + "/*";
}
ServletHolder cometdServletHolder = new ServletHolder(CometDServlet.class);
String transports = wsTransportClass + "," + JSONTransport.class.getName();
cometdServletHolder.setInitParameter("transports", transports);
cometdServletHolder.setInitParameter("timeout", "10000");
cometdServletHolder.setInitParameter("ws.cometdURLMapping", cometdURLMapping);
if (eager) {
cometdServletHolder.setInitOrder(1);
}
if (initParams != null) {
for (Map.Entry<String, String> entry : initParams.entrySet()) {
cometdServletHolder.setInitParameter(entry.getKey(), entry.getValue());
}
}
context.addServlet(cometdServletHolder, cometdURLMapping);
}
protected void prepareClient() {
httpClient = new HttpClient();
switch (wsTransportType) {
case WEBSOCKET_JSR_356:
wsClientContainer = ContainerProvider.getWebSocketContainer();
httpClient.addBean(wsClientContainer, true);
break;
case WEBSOCKET_JETTY:
QueuedThreadPool wsThreadPool = new QueuedThreadPool();
wsThreadPool.setName("client");
wsClient = new WebSocketClient();
wsClient.setExecutor(wsThreadPool);
httpClient.addBean(wsClient);
break;
default:
throw new IllegalArgumentException();
}
}
protected void startServer() throws Exception {
server.start();
int port = connector.getLocalPort();
cometdURL = "http://localhost:" + port + cometdServletPath;
bayeux = (BayeuxServerImpl)context.getServletContext().getAttribute(BayeuxServer.ATTRIBUTE);
}
protected void startClient() throws Exception {
httpClient.start();
}
protected BayeuxClient newBayeuxClient() {
return new BayeuxClient(cometdURL, newWebSocketTransport(null));
}
protected ClientTransport newLongPollingTransport(Map<String, Object> options) {
return new LongPollingTransport(options, httpClient);
}
protected ClientTransport newWebSocketTransport(Map<String, Object> options) {
return newWebSocketTransport(null, options);
}
protected ClientTransport newWebSocketTransport(String url, Map<String, Object> options) {
ClientTransport result;
switch (wsTransportType) {
case WEBSOCKET_JSR_356:
result = new org.cometd.websocket.client.WebSocketTransport(url, options, null, wsClientContainer);
break;
case WEBSOCKET_JETTY:
result = new org.cometd.websocket.client.JettyWebSocketTransport(url, options, null, wsClient);
break;
default:
throw new IllegalArgumentException();
}
return result;
}
protected void disconnectBayeuxClient(BayeuxClient client) {
client.disconnect(1000);
}
@After
public void stopAndDispose() throws Exception {
stopClient();
stopServer();
}
protected void stopServer() throws Exception {
server.stop();
server.join();
}
protected void stopClient() throws Exception {
httpClient.stop();
}
}