/**
* The MIT License
* Copyright (c) 2010 Tad Glines
*
* Contributors: Ovea.com, Mycila.com
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.waveprotocol.box.server.rpc;
import com.google.inject.Inject;
import com.glines.socketio.server.AnnotationTransportHandlerProvider;
import com.glines.socketio.server.ClasspathTransportDiscovery;
import com.glines.socketio.server.ServletBasedSocketIOConfig;
import com.glines.socketio.server.SocketIOConfig;
import com.glines.socketio.server.SocketIOInbound;
import com.glines.socketio.server.SocketIOServlet;
import com.glines.socketio.server.SocketIOSessionManager;
import com.glines.socketio.server.Transport;
import com.glines.socketio.server.TransportDiscovery;
import com.glines.socketio.server.TransportHandlerProvider;
import com.glines.socketio.server.TransportInitializationException;
import com.glines.socketio.server.TransportType;
import com.glines.socketio.server.transport.FlashSocketTransport;
import com.glines.socketio.server.transport.HTMLFileTransport;
import com.glines.socketio.server.transport.JSONPPollingTransport;
import com.glines.socketio.server.transport.XHRMultipartTransport;
import com.glines.socketio.server.transport.XHRPollingTransport;
import com.glines.socketio.server.transport.jetty.JettyWebSocketTransport;
import com.glines.socketio.util.IO;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public abstract class AbstractWaveSocketIOServlet extends HttpServlet {
private static final Logger LOGGER = Logger.getLogger(SocketIOServlet.class.getName());
private static final long serialVersionUID = 2L;
private final SocketIOSessionManager sessionManager = new SocketIOSessionManager();
private final TransportHandlerProvider transportHandlerProvider = new AnnotationTransportHandlerProvider();
private SocketIOConfig config;
private final Transport[] transports;
public AbstractWaveSocketIOServlet(Transport...transports) {
this.transports = transports;
}
@Override
public void init() throws ServletException {
config = new ServletBasedSocketIOConfig(getServletConfig(), "socketio");
// lazy load available transport handlers
transportHandlerProvider.init();
if (LOGGER.isLoggable(Level.INFO))
LOGGER.log(Level.INFO, "Transport handlers loaded: " + transportHandlerProvider.listAll());
// lazily load available transports
//TransportDiscovery transportDiscovery = new ClasspathTransportDiscovery();
for (Transport transport : transports) {
if (transportHandlerProvider.isSupported(transport.getType())) {
transport.setTransportHandlerProvider(transportHandlerProvider);
config.addTransport(transport);
} else {
LOGGER.log(Level.WARNING, "Transport " + transport.getType() + " ignored since not supported by any TransportHandler");
}
}
// initialize them
for (Transport t : config.getTransports()) {
try {
t.init(getServletConfig());
} catch (TransportInitializationException e) {
config.removeTransport(t.getType());
LOGGER.log(Level.WARNING, "Transport " + t.getType() + " disabled. Initialization failed: " + e.getMessage());
}
}
if (LOGGER.isLoggable(Level.INFO))
LOGGER.log(Level.INFO, "Transports loaded: " + config.getTransports());
}
@Override
public void destroy() {
for (Transport t : config.getTransports()) {
t.destroy();
}
super.destroy();
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
serve(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
serve(req, resp);
}
/**
* Returns an instance of SocketIOInbound or null if the connection is to be denied.
* The value of cookies and protocols may be null.
*/
protected abstract SocketIOInbound doSocketIOConnect(HttpServletRequest request);
private void serve(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String path = request.getPathInfo();
if (path == null || path.length() == 0 || "/".equals(path)) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Missing SocketIO transport");
return;
}
if (path.startsWith("/")) path = path.substring(1);
String[] parts = path.split("/");
Transport transport = config.getTransport(TransportType.from(parts[0]));
if (transport == null) {
if ("GET".equals(request.getMethod()) && "socket.io.js".equals(parts[0])) {
response.setContentType("text/javascript");
InputStream is = this.getClass().getClassLoader().getResourceAsStream("com/glines/socketio/socket.io.js");
OutputStream os = response.getOutputStream();
IO.copy(is, os);
return;
}else if ("GET".equals(request.getMethod()) && "WebSocketMain.swf".equals(parts[0])) {
response.setContentType("application/x-shockwave-flash");
InputStream is = this.getClass().getClassLoader().getResourceAsStream("com/glines/socketio/WebSocketMain.swf");
OutputStream os = response.getOutputStream();
IO.copy(is, os);
return;
} else {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Unknown SocketIO transport: " + parts[0]);
return;
}
}
if (LOGGER.isLoggable(Level.FINE))
LOGGER.log(Level.FINE, "Handling request from " + request.getRemoteHost() + ":" + request.getRemotePort() + " with transport: " + transport.getType());
transport.handle(request, response, new Transport.InboundFactory() {
@Override
public SocketIOInbound getInbound(HttpServletRequest request) {
return AbstractWaveSocketIOServlet.this.doSocketIOConnect(request);
}
}, sessionManager);
}
}