package com.pugh.sockso.web; import com.pugh.sockso.Constants; import com.pugh.sockso.Properties; import com.pugh.sockso.PropertiesListener; import com.google.inject.Inject; import com.google.inject.Injector; import com.google.inject.Singleton; import org.apache.log4j.Logger; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.List; import javax.swing.JOptionPane; import joptsimple.OptionSet; /** * * A basic HTTP server. * */ @Singleton public class HttpServer extends Thread implements Server, PropertiesListener { public static final int DEFAULT_PORT = 4444; private final Injector injector; private final Properties p; private final List<ServerThread> threads; private int port = DEFAULT_PORT; private ServerSocket ss; private static Logger log = Logger.getLogger(Server.class); /** * Creates a new instance of a http server. If the ip address given is null, * then the server will try and work it out for itself. * * @param injector * @param p app properties * */ @Inject public HttpServer( final Injector injector, final Properties p ) { this.injector = injector; this.p = p; threads = new ArrayList<ServerThread>(); } /** * starts the web server, optionally binding to a specific ip (if this * is null then we'll try and work it out ourselves) * * @param options * */ @Override public void start( final OptionSet options, final int port ) { this.port = port; p.addPropertiesListener( this ); start(); } /** * the server thread. opens a socket for listening and then waits for * requests. * */ @Override public void run() { try { ss = getServerSocket( port ); log.info( "Listening on " + port ); while ( true ) { handleRequest( ss.accept()); } } catch ( final IOException e ) { // the socket being closed "shouldn't" be an error... i think? if ( !e.getMessage().equals("Socket closed") ) log.error(e); } } /** * a connection from a client has been received, so we need to * create a thread to handle it. this thread *should* call back * to us when it's done. * * @param client * */ protected void handleRequest( final Socket client ) { final ServerThread st = injector.getInstance( ServerThread.class ); threads.add( st ); st.setClientSocket( client ); st.start(); } /** * shuts down the server, asks any threads that are currently still running * to finish * */ @Override public void shutdown() { log.info( "Shutting Down " + threads.size() + " Thread(s)" ); for ( final ServerThread thread : threads ) thread.shutdown(); if ( ss != null ) try { log.info("Closing Listening Socket"); ss.close(); } catch ( final Exception e ) { log.error(e); } log.info("Shutdown Complete"); } /** * called by threads when they complete * * @param thread the thread that has completed */ @Override public void requestComplete( final ServerThread thread ) { //log.debug( "Thread Complete Signal Received" ); threads.remove(thread); } /** * returns the ip address the server is bound to and the port that we're * listening on * * @return ip:port combo * * @deprecated * */ @Override public String getHost() { return p.get( Constants.SERVER_HOST ) + ":" + getPort(); } /** * returns the port the server is currently listening on * * @return the port number * */ @Override public int getPort() { return port; } /** * handles properties being saved, maybe things have changed and we need to * do some adjustment * * @param p the new properties object * */ @Override public void propertiesSaved( final Properties p ) { try { final int newPort = Integer.parseInt(p.get(Constants.SERVER_PORT, "4444")); if (newPort != port) JOptionPane.showMessageDialog( null, "You need to restart Sockso for this change to take effect" ); } catch ( final NumberFormatException e ) { log.warn("The server port is not a number: '" + p.get(Constants.SERVER_PORT) + "'"); } } /** * returns a standard server socket * * @param port * * @return * * @throws java.io.IOException * */ protected ServerSocket getServerSocket( final int port ) throws IOException { return new ServerSocket( port ); } /** * Returns the name of the protocol we're using (eg "http", "https") * * @return * */ @Override public String getProtocol() { return "http"; } }