SimpleServer.java
/*******************************************************************************
* Copyright (c) 2004, 2013 Steve Flasby
* All rights reserved.
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* <ul>
* <li>Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.</li>
* <li>Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.</li>
* </ul>
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
package org.flasby.server;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import org.flasby.server.Reporter.Importance;
import org.flasby.util.Lifecycle.StartFailedException;
/**
* listens on a port and waits for connections.
* Whene a conection arrives it creates a new Instance and runs this
* in a new thread.
* The Instance is given the socket connection to the remote client and
* a Reporter which it can use to report progress and problems.
*
* @author steve
*
*/
public abstract class SimpleServer {
private final int mPort;
final Reporter mReporter;
final ServerSocket ss;
/**
* starts a simple server on any free port. The port can be found using getPort().
*/
protected SimpleServer() {
this(0);
}
/**
* starts a simple server on the specified port.
*
* @param port
*/
protected SimpleServer(int port) {
mReporter = createReporter();
try {
ss = new ServerSocket(port);
mPort = ss.getLocalPort();
} catch (IOException e) {
getReporter().report(Importance.ERROR, "Can't start server because:"+e);
throw new RuntimeException(e);
}
}
public void start() {
starting();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
Socket s;
try {
s = ss.accept();
getReporter().report(Importance.DETAIL, "Accepted connection");
} catch (IOException e) {
getReporter().report(Importance.WARN, "Failed to accept a connection request because: "+e);
continue;
}
final Instance i = create(s,getReporter());
if ( i == null ) {
continue;
}
new Thread( new Runnable() {
@Override
public void run() {
try {
getReporter().report(Importance.INFO, "Starting server instance "+i.mInstanceId);
i.start();
} catch ( StartFailedException ex ) {
getReporter().report(Importance.WARN, "Stopping server instance "+i.mInstanceId +" because: "+ex);
i.stop();
}
stopped(i);
}
}).start();
}
}}).start();
}
/**
* called just before the server starts listening for connections.
*/
protected void starting() {
getReporter().report(Importance.INFO, "Server started on port: "+mPort);
}
protected abstract Instance create(Socket connectionToClient, Reporter reporter);
/**
* called when an Instance is stopped.
* @param instance
*/
protected void stopped(Instance instance) {
getReporter().report(Importance.INFO, "Stopped server instance "+instance.mInstanceId);
}
protected Reporter createReporter() {
return new SysoutReporter();
}
public Reporter getReporter() {
return mReporter;
}
public int getPort() {
return mPort;
}
}