| HTTPServletHandler.java |
/*
* $Id: HTTPServletHandler.java,v 1.75 2012/03/15 21:07:39 agoubard Exp $
*
* See the COPYRIGHT file for redistribution and use restrictions.
*/
package org.xins.common.servlet.container;
import java.io.File;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.servlet.ServletException;
import org.apache.log4j.LogManager;
import org.apache.log4j.PropertyConfigurator;
import org.apache.log4j.helpers.NullEnumeration;
import org.xins.common.Library;
import org.xins.common.Log;
/**
* HTTP server used to invoke the XINS servlet.
*
* @version $Revision: 1.75 $ $Date: 2012/03/15 21:07:39 $
* @author <a href="mailto:anthony.goubard@japplis.com">Anthony Goubard</a>
* @author <a href="mailto:ernst@ernstdehaan.com">Ernst de Haan</a>
*/
public class HTTPServletHandler {
/**
* The default port number is 8080.
*/
public static final int DEFAULT_PORT_NUMBER = 8080;
/**
* The web server.
*/
private ServerSocket _serverSocket;
/**
* The thread that waits for connections from the client.
*/
private SocketAcceptor _acceptor;
/**
* Flag indicating if the server should wait for other connections or stop.
*/
private boolean _running;
/**
* Mapping between the path and the servlet.
*/
private Map<String, LocalServletHandler> _servlets = new HashMap<String, LocalServletHandler>();
/**
* Creates a new HTTPSevletHandler with no Servlet. Use the addServlet
* methods to add the WAR files or the Servlets.
*
* @param port
* The port of the servlet server.
*
* @param daemon
* <code>true</code> if the thread listening to connection should be a
* daemon thread, <code>false</code> otherwise.
*
* @throws IOException
* if the servlet container cannot be started.
*/
public HTTPServletHandler(int port, boolean daemon) throws IOException {
// Configure log4j if not already done.
Enumeration appenders = LogManager.getLoggerRepository().getRootLogger().getAllAppenders();
if (appenders instanceof NullEnumeration) {
configureLoggerFallback();
}
// Start the HTTP server.
startServer(port, daemon);
}
/**
* Creates a new <code>HTTPServletHandler</code>. This servlet handler
* starts a web server on port 8080 and wait for calls from the
* <code>XINSServiceCaller</code>.
*
* <p>Note that all the libraries used by this WAR file should already be
* in the classpath.
*
* @param warFile
* the war file of the application to deploy, cannot be
* <code>null</code>.
*
* @throws ServletException
* if the servlet cannot be initialized.
*
* @throws IOException
* if the servlet container cannot be started.
*/
public HTTPServletHandler(File warFile)
throws ServletException, IOException {
this(DEFAULT_PORT_NUMBER, true);
addWAR(warFile, "/");
}
/**
* Creates a new <code>HTTPSevletHandler</code>. This servlet handler
* starts a web server on the specified port and waits for calls from the XINSServiceCaller.
* Note that all the libraries used by this WAR file should already be in
* the classpath.
*
* @param warFile
* the war file of the application to deploy, cannot be
* <code>null</code>.
*
* @param port
* The port of the servlet server.
*
* @param daemon
* <code>true</code> if the thread listening to connection should be a
* daemon thread, <code>false</code> otherwise.
*
* @throws ServletException
* if the servlet cannot be initialized.
*
* @throws IOException
* if the servlet container cannot be started.
*/
public HTTPServletHandler(File warFile, int port, boolean daemon)
throws ServletException, IOException {
this(port, daemon);
addWAR(warFile, "/");
}
/**
* Creates a new HTTPSevletHandler. This Servlet handler starts a web server
* and wait for calls from the XINSServiceCaller.
*
* @param servletClassName
* The name of the servlet's class to load, cannot be <code>null</code>.
*
* @throws ServletException
* if the servlet cannot be initialized.
*
* @throws IOException
* if the servlet container cannot be started.
*/
public HTTPServletHandler(String servletClassName) throws ServletException, IOException {
this(DEFAULT_PORT_NUMBER, true);
addServlet(servletClassName, "/");
}
/**
* Creates a new HTTPSevletHandler. This Servlet handler starts a web server
* and wait for calls from the XINSServiceCaller.
*
* @param servletClassName
* The name of the servlet's class to load, cannot be <code>null</code>.
*
* @param port
* The port of the servlet server.
*
* @param daemon
* <code>true</code> if the thread listening to connection should be a
* daemon thread, <code>false</code> otherwise.
*
* @throws ServletException
* if the servlet cannot be initialized.
*
* @throws IOException
* if the servlet container cannot be started.
*/
public HTTPServletHandler(String servletClassName, int port, boolean daemon) throws ServletException, IOException {
this(port, daemon);
addServlet(servletClassName, "/");
}
/**
* Initializes the logging subsystem with fallback default settings.
*/
private static void configureLoggerFallback() {
Properties settings = new Properties();
settings.setProperty("log4j.rootLogger", "ALL, console");
settings.setProperty("log4j.appender.console", "org.apache.log4j.ConsoleAppender");
settings.setProperty("log4j.appender.console.layout", "org.apache.log4j.PatternLayout");
settings.setProperty("log4j.appender.console.layout.ConversionPattern", "%6c{1} %-6p %x %m%n");
settings.setProperty("log4j.logger.org.xins.", "INFO");
PropertyConfigurator.configure(settings);
}
/**
* Adds a WAR file to the server.
* The servlet with the virtual path "/" will be the default one.
* Note that all the libraries used by this WAR file should already be in
* the classpath.
*
* @param warFile
* The war file of the application to deploy, cannot be <code>null</code>.
*
* @param virtualPath
* The virtual path of the HTTP server that links to this WAR file, cannot be <code>null</code>.
*
* @throws ServletException
* if the servlet cannot be initialized.
*/
public void addWAR(File warFile, String virtualPath) throws ServletException {
LocalServletHandler servlet = new LocalServletHandler(warFile);
if (! virtualPath.endsWith("/")) {
virtualPath += '/';
}
_servlets.put(virtualPath, servlet);
}
/**
* Adds a new servlet.
* The servlet with the virtual path "/" will be the default one.
*
* @param servletClassName
* The name of the servlet's class to load, cannot be <code>null</code>.
*
* @param virtualPath
* The virtual path of the HTTP server that links to this WAR file, cannot be <code>null</code>.
*
* @throws ServletException
* if the servlet cannot be initialized.
*/
public void addServlet(String servletClassName, String virtualPath) throws ServletException{
LocalServletHandler servlet = new LocalServletHandler(servletClassName);
if (! virtualPath.endsWith("/")) {
virtualPath += '/';
}
_servlets.put(virtualPath, servlet);
}
/**
* Remove a servlet from the server.
*
* @param virtualPath
* The virtual path of the servlet to remove, cannot be <code>null</code>.
*/
public void removeServlet(String virtualPath) {
if (! virtualPath.endsWith("/")) {
virtualPath += '/';
}
LocalServletHandler servlet = (LocalServletHandler) _servlets.get(virtualPath);
servlet.close();
_servlets.remove(virtualPath);
}
/**
* Starts the web server.
*
* @param port
* the port of the servlet server.
*
* @param daemon
* <code>true</code> if the thread listening to connection should be a
* daemon thread, <code>false</code> otherwise.
*
* @throws IOException
* if the web server cannot be started.
*/
public void startServer(int port, boolean daemon) throws IOException {
// Create the server socket
_serverSocket = new ServerSocket(port, 5);
_running = true;
_acceptor = new SocketAcceptor(daemon);
_acceptor.start();
}
/**
* Returns the port the server is accepting connections on.
*
* @return
* the server socket, e.g. <code>8080</code>.
*
* @throws IllegalStateException
* if the port cannot be determined, for example because the server is
* not started.
*
* @since XINS 1.5.0
*/
public int getPort() throws IllegalStateException {
int port;
try {
port = _serverSocket.getLocalPort();
} catch (NullPointerException exception) {
port = -1;
}
if (port < 0) {
throw new IllegalStateException("Unable to determine port.");
}
return port;
}
/**
* Disposes the servlet and stops the web server.
*/
public void close() {
_running = false;
for (LocalServletHandler servlet : _servlets.values()) {
servlet.close();
}
try {
_serverSocket.close();
} catch (IOException ioe) {
Log.log_1502(ioe);
}
}
/**
* Thread waiting for connection from the client.
*/
private class SocketAcceptor extends Thread {
/**
* Create the thread.
*
* @param daemon
* <code>true</code> if the server should be a daemon thread,$
* <code>false</code> otherwise.
*/
public SocketAcceptor(boolean daemon) {
setDaemon(daemon);
setName("XINS " + Library.getVersion() + " Servlet container.");
}
/**
* Executes the thread.
*/
public void run() {
Log.log_1500(_serverSocket.getLocalPort());
try {
while (_running) {
// Wait for a connection
Socket clientSocket = _serverSocket.accept();
HTTPQueryHandler queryHandler = new HTTPQueryHandler(clientSocket, _servlets);
queryHandler.start();
}
} catch (SocketException ie) {
// fall through
} catch (IOException ioe) {
Log.log_1501(ioe);
}
}
}
}