package edu.harvard.econcs.turkserver.server;
import java.util.Map;
import javax.servlet.GenericServlet;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.lang.ArrayUtils;
import org.cometd.annotation.AnnotationCometdServlet;
import org.cometd.bayeux.server.BayeuxServer;
import org.cometd.bayeux.server.BayeuxServer.BayeuxServerListener;
import org.cometd.server.BayeuxServerImpl;
import org.cometd.server.CometdServlet;
import org.cometd.server.DefaultSecurityPolicy;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.bio.SocketConnector;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.ResourceCollection;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.inject.name.Named;
import edu.harvard.econcs.turkserver.config.TSConfig;
@Singleton
public class JettyCometD {
public static final String ATTRIBUTE = "edu.harvard.econcs.turkserver.jettycometd";
private int initOrder = 1;
protected final Server server;
protected final ContextHandlerCollection contexts;
protected final ServletContextHandler context;
protected final CometdServlet cometdServlet;
@Inject
JettyCometD(
@Named(TSConfig.SERVER_RESOURCES) Resource[] resources,
Configuration conf
) {
int httpPort = conf.getInt(TSConfig.SERVER_HTTPPORT);
server = new Server();
QueuedThreadPool qtp = new QueuedThreadPool();
qtp.setMinThreads(5);
qtp.setMaxThreads(200);
server.setThreadPool(qtp);
server.setGracefulShutdown(1000);
server.setStopAtShutdown(true);
SelectChannelConnector connector = new SelectChannelConnector();
connector.setPort(httpPort);
connector.setMaxIdleTime(120000);
connector.setLowResourcesMaxIdleTime(60000);
connector.setLowResourcesConnections(20000);
connector.setAcceptQueueSize(5000);
server.addConnector(connector);
SocketConnector bconnector = new SocketConnector();
bconnector.setPort(httpPort+1);
server.addConnector(bconnector);
contexts = new ContextHandlerCollection();
server.setHandler(contexts);
// Base files servlet
context = new ServletContextHandler(contexts, "/" ,ServletContextHandler.SESSIONS);
context.setBaseResource(new ResourceCollection(resources));
// context.setAliases(true);
// Default servlet
ServletHolder dftServlet = context.addServlet(DefaultServlet.class, "/");
dftServlet.setInitOrder(initOrder++);
// Cometd servlet
cometdServlet = new AnnotationCometdServlet();
ServletHolder comet = new ServletHolder(cometdServlet);
context.addServlet(comet, "/cometd/*");
comet.setInitParameter("timeout", "20000");
comet.setInitParameter("interval", "100");
comet.setInitParameter("maxInterval", "10000");
comet.setInitParameter("multiFrameInterval", "5000");
comet.setInitParameter("logLevel", "2");
// for registering serialization and deserialization
comet.setInitParameter("jsonContext", org.cometd.server.JettyJSONContextServer.class.getCanonicalName());
comet.setInitParameter("transports", org.cometd.websocket.server.WebSocketTransport.class.getCanonicalName());
// comet.setInitParameter("transports","org.cometd.server.transport.LongPollingTransport");
comet.setInitOrder(initOrder++);
context.setAttribute(ATTRIBUTE, this);
}
/**
* Add optional additional servlets before starting server
* @param servletPaths
*/
@Inject(optional=true)
void addCustomServlets(
@Named(TSConfig.SERVER_EXTRA_SERVLETS)
Map<Class<? extends GenericServlet>, String> servletPaths) {
for( Map.Entry<Class<? extends GenericServlet>, String> e : servletPaths.entrySet() ) {
addServlet(e.getKey(), e.getValue());
}
}
/**
* Injects custom handlers for the server.
* @param handlerPaths
*/
@Inject(optional=true)
void addCustomHandlers(
@Named(TSConfig.SERVER_CUSTOM_HANDLERS)
Map<Handler, String> handlerPaths
) {
for( Map.Entry<Handler, String> e : handlerPaths.entrySet() ) {
addCustomHandler(e.getKey(), e.getValue());
}
}
public ServletHolder addServlet(Class<? extends GenericServlet> servletClass, String pathSpec) {
ServletHolder holder = context.addServlet(servletClass, pathSpec);
holder.setInitOrder(initOrder++);
return holder;
}
/**
* Add custom handlers to the server.
* For advanced users that have custom dynamic content.
* @param handler
*/
public void addCustomHandler(Handler handler, String contextPath) {
// Additional custom handlers
ContextHandler ch = new ContextHandler(contextPath);
ch.setHandler(handler);
// Add this to the beginning of the collection of handlers
Handler[] handlers = contexts.getHandlers();
contexts.setHandlers((Handler[]) ArrayUtils.addAll(new Handler[] {ch}, handlers));
}
BayeuxServer getBayeux() {
return cometdServlet.getBayeux();
}
/**
* Start Jetty server and configure bayeux server
* @throws Exception
*/
Server start(SessionServer sessions) throws Exception {
context.setAttribute(SessionServer.ATTRIBUTE, sessions);
server.start();
BayeuxServerImpl bayeux = cometdServlet.getBayeux();
bayeux.setSecurityPolicy(new DefaultSecurityPolicy());
bayeux.addListener(new BayeuxServerListener() {
});
return server;
}
}