/*
* $Id$
*
* Copyright 2006, The jCoderZ.org Project. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of the jCoderZ.org Project nor the names of
* its contributors may be used to endorse or promote products
* derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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.jcoderz.commons.connector.http.transport;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* This class defines a simple http server used for testing
* the HttpConnectionImpl based on the commons-httpclient lib.
*/
public class SimpleServer
extends Thread
{
/** Class name used for logging. */
private static final String CLASSNAME
= SimpleServer.class.getName();
/** Logger in use. */
private static final Logger logger
= Logger.getLogger(CLASSNAME);
private static final int SOCKET_TIMEOUT = 10000;
private static final int JOIN_TIMEFRAME = 5000;
private static final int WAITING_LOOPS = 10;
/** Thread for stopping the server. */
private final Terminator mTerminator;
/** Flag indicating that the server have to be stopped. */
private boolean mDoStop = false;
/** Used for sychronize stopping the server. */
private final Object mMonitor = new Object();
/** Handler for client requests. */
private final Map mHandler = new HashMap();
/** Server port. */
private final int mPort;
/** Flag indicating if the server has been started. */
private volatile boolean mServerStarted = false;
/**
* Constructor.
* @param port
* the server port in use
*/
public SimpleServer (int port)
{
mPort = port;
mTerminator = new Terminator(this);
}
/**
* Main method. Starts a thread for terminating the server and starts
* a ClientHandler thread for every incomming request.
* @param args
* args include the port number to use
*/
public static void main (String[] args)
{
if (args.length != 1)
{
logger.info("Usage: SimpleServer <port>");
return;
}
final SimpleServer server = new SimpleServer(Integer.parseInt(args[0]));
server.startServer();
server.stopServer();
}
/**
* Starts the server thread.
*/
public void run ()
{
startServer();
stopServer();
}
/**
* Creates a client socket for every incoming request and creates a
* ClientHandler thread to handle the request/response.
* Server will be stopped after a certain timeframe.
*/
public void startServer ()
{
logger.info("Starting server..");
try
{
final ServerSocket s = new ServerSocket(mPort);
s.setSoTimeout(SOCKET_TIMEOUT);
int clientCounter = 0;
int waitingCounter = 0;
ClientHandler client = null;
logger.info("Waiting for connection on " + s);
while (!haveToStop() && waitingCounter < WAITING_LOOPS)
{
Socket incoming = null;
try
{
mServerStarted = true;
incoming = s.accept();
}
catch (SocketTimeoutException ste)
{
waitingCounter++;
}
if (incoming != null)
{
clientCounter++;
client = new ClientHandler(incoming, clientCounter);
mHandler.put(Integer.toString(clientCounter), client);
client.start();
}
}
}
catch (Exception e)
{
logger.log(Level.WARNING, "Failed to start SimpleServer: " + e, e);
e.printStackTrace();
}
// don't let others wait for ever.
mServerStarted = true;
}
/**
* Sets the flag for stopping the server.
*/
public void doStop ()
{
synchronized (mMonitor)
{
mDoStop = true;
}
}
/**
* Checks the flag for stopping the server.
* @return boolean
* the flag for stopping the server
*/
public boolean haveToStop ()
{
synchronized (mMonitor)
{
return mDoStop;
}
}
/**
* Stops the client handler threads.
*
*/
public void stopServer ()
{
logger.info("\n---Stopping Server with " + mHandler.size()
+ " client handler---\n");
final Iterator it = mHandler.keySet().iterator();
while (it.hasNext())
{
try
{
final String clientId = (String) it.next();
final ClientHandler client = (ClientHandler) mHandler.get(clientId);
if (client != null)
{
client.haveToStop();
client.join(JOIN_TIMEFRAME);
if (client.isAlive())
{
interruptClient(client);
}
}
}
catch (Exception ex)
{
// ignore
}
}
if (mTerminator != null && mTerminator.isAlive())
{
mTerminator.interrupt();
}
logger.warning("---Stopping DONE---");
}
/**
* Returns <tt>true</tt> if the server has been started.
* @return <tt>true</tt> if the server has been started;
* <tt>false</tt> otherwise.
*/
public boolean isServerStarted ()
{
return mServerStarted;
}
/**
* Interrupts the client handler thread.
* @param client
* the thread to interrupt
* @throws InterruptedException
* if interrupt fails
*/
private void interruptClient (ClientHandler client)
throws InterruptedException
{
try
{
client.interrupt();
logger.info("Needed to interrupt client handler thread.");
}
catch (Exception ex)
{
logger.log(Level.WARNING,
"Exception while interrupting client handler thread: "
+ ex.getMessage(), ex);
}
client.join(JOIN_TIMEFRAME);
if (client.isAlive())
{
logger.warning("unable to interrupt client thread");
}
}
}