//----------------------------BEGIN LICENSE----------------------------
/*
* Willow : the Open Source WorkFlow Project
* Distributable under GNU LGPL license by gun.org
*
* Copyright (C) 2004-2010 huihoo.org
* Copyright (C) 2004-2010 ZosaTapo <dertyang@hotmail.com>
*
* ====================================================================
* Project Homepage : http://www.huihoo.org/willow
* Source Forge : http://sourceforge.net/projects/huihoo
* Mailing list : willow@lists.sourceforge.net
*/
//----------------------------END LICENSE-----------------------------
package org.huihoo.willow.core;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.AccessControlException;
import java.util.Random;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.huihoo.willow.Globals;
import org.huihoo.willow.Lifecycle;
import org.huihoo.willow.LifecycleException;
import org.huihoo.willow.LifecycleListener;
import org.huihoo.willow.Server;
import org.huihoo.willow.ServerFactory;
import org.huihoo.willow.Service;
import org.huihoo.willow.util.LifecycleSupport;
import org.huihoo.willow.util.StringManager;
/**
* @author reic
*
* To change the template for this generated type comment go to
* Window - Preferences - Java - Code Generation - Code and Comments
*/
public final class StandardServer implements Lifecycle, Server
{
private static Log log = LogFactory.getLog(StandardServer.class);
/**
* ServerLifecycleListener classname.
*/
private static String SERVER_LISTENER_CLASS_NAME ="org.huihoo.willow.core.ServerLifecycleListener";
// ------------------------------------------------------------ Constructor
/**
* Construct a default instance of this class.
*/
public StandardServer()
{
super();
ServerFactory.setServer(this);
}
// ----------------------------------------------------- Instance Variables
/**
* Debugging detail level.
*/
private int debug = 0;
/**
* Descriptive information about this NamingServer implementation.
*/
private static final String info = "org.huihoo.willow.core.StandardServer/1.0";
/**
* The lifecycle event support for this component.
*/
private LifecycleSupport lifecycle = new LifecycleSupport(this);
/**
* The port number on which we wait for shutdown commands.
*/
private int port = 8023;
/**
* A random number generator that is <strong>only</strong> used if
* the shutdown command string is longer than 1024 characters.
*/
private Random random = null;
/**
* The set of Services associated with this NamingServer.
*/
private Service services[] = new Service[0];
/**
* The shutdown command string we are looking for.
*/
private String shutdown = "SHUTDOWN";
/**
* The string manager for this package.
*/
private static final StringManager sm = StringManager.getManager(Constants.PACKAGE);
/**
* Has this component been started?
*/
private boolean started = false;
/**
* The property change support for this component.
*/
protected PropertyChangeSupport support = new PropertyChangeSupport(this);
// ------------------------------------------------------------- Properties
/**
* Return the debugging detail level.
*/
public int getDebug()
{
return (this.debug);
}
/**
* Set the debugging detail level.
*
* @param debug The new debugging detail level
*/
public void setDebug(int debug)
{
this.debug = debug;
}
/**
* Return descriptive information about this NamingServer implementation and
* the corresponding version number, in the format
* <code><description>/<version></code>.
*/
public String getInfo()
{
return (info);
}
/**
* Return the port number we listen to for shutdown commands.
*/
public int getPort()
{
return (this.port);
}
/**
* Set the port number we listen to for shutdown commands.
*
* @param port The new port number
*/
public void setPort(int port)
{
this.port = port;
}
/**
* Return the shutdown command string we are waiting for.
*/
public String getShutdown()
{
return (this.shutdown);
}
/**
* Set the shutdown command we are waiting for.
*
* @param shutdown The new shutdown command
*/
public void setShutdown(String shutdown)
{
this.shutdown = shutdown;
}
// --------------------------------------------------------- NamingServer Methods
/**
* Add a new Service to the set of defined Services.
*
* @param service The Service to be added
*/
public void addService(Service service)
{
service.setServer(this);
synchronized (services)
{
Service results[] = new Service[services.length + 1];
System.arraycopy(services, 0, results, 0, services.length);
results[services.length] = service;
services = results;
if (started && (service instanceof Lifecycle))
{
try
{
((Lifecycle) service).start();
}
catch (LifecycleException e)
{
;
}
}
// Report this property change to interested listeners
support.firePropertyChange("service", null, service);
}
}
/**
* Wait until a proper shutdown command is received, then return.
*/
public void await()
{
// Set up a server socket to wait on
ServerSocket serverSocket = null;
try
{
port=(port<=0)?Globals.PORT_SERVER_COMMAND:port;
serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
}
catch (IOException e)
{
System.err.println("StandardServer.await: create[" + port + "]: " + e);
e.printStackTrace();
System.exit(1);
}
// Loop waiting for a connection and a valid command
while (true)
{
// Wait for the next connection
Socket socket = null;
InputStream stream = null;
try
{
socket = serverSocket.accept();
socket.setSoTimeout(10 * 1000); // Ten seconds
stream = socket.getInputStream();
}
catch (AccessControlException ace)
{
System.err.println("StandardServer.accept security exception: " + ace.getMessage());
continue;
}
catch (IOException e)
{
System.err.println("StandardServer.await: accept: " + e);
e.printStackTrace();
System.exit(1);
}
// Read a set of characters from the socket
StringBuffer command = new StringBuffer();
int expected = 1024; // Cut off to avoid DoS attack
while (expected < shutdown.length())
{
if (random == null)
random = new Random(System.currentTimeMillis());
expected += (random.nextInt() % 1024);
}
while (expected > 0)
{
int ch = -1;
try
{
ch = stream.read();
}
catch (IOException e)
{
System.err.println("StandardServer.await: read: " + e);
e.printStackTrace();
ch = -1;
}
if (ch < 32) // Control character or EOF terminates loop
break;
command.append((char) ch);
expected--;
}
// Close the socket now that we are done with it
try
{
socket.close();
}
catch (IOException e)
{
;
}
// Match against our command string
boolean match = command.toString().equals(shutdown);
if (match)
{
break;
}
else
System.err.println(
"StandardServer.await: Invalid command '" + command.toString() + "' received");
}
// Close the server socket and return
try
{
serverSocket.close();
}
catch (IOException e)
{
;
}
}
/**
* Return the specified Service (if it exists); otherwise return
* <code>null</code>.
*
* @param name Name of the Service to be returned
*/
public Service findService(String name)
{
if (name == null)
{
return (null);
}
synchronized (services)
{
for (int i = 0; i < services.length; i++)
{
if (name.equals(services[i].getName()))
{
return (services[i]);
}
}
}
return (null);
}
/**
* Return the set of Services defined within this NamingServer.
*/
public Service[] findServices()
{
return (services);
}
/**
* Remove the specified Service from the set associated from this
* NamingServer.
*
* @param service The Service to be removed
*/
public void removeService(Service service)
{
synchronized (services)
{
int j = -1;
for (int i = 0; i < services.length; i++)
{
if (service == services[i])
{
j = i;
break;
}
}
if (j < 0)
return;
if (services[j] instanceof Lifecycle)
{
try
{
((Lifecycle) services[j]).stop();
}
catch (LifecycleException e)
{
;
}
}
int k = 0;
Service results[] = new Service[services.length - 1];
for (int i = 0; i < services.length; i++)
{
if (i != j)
results[k++] = services[i];
}
services = results;
// Report this property change to interested listeners
support.firePropertyChange("service", service, null);
}
}
// --------------------------------------------------------- Public Methods
/**
* Add a property change listener to this component.
*
* @param listener The listener to add
*/
public void addPropertyChangeListener(PropertyChangeListener listener)
{
support.addPropertyChangeListener(listener);
}
/**
* Remove a property change listener from this component.
*
* @param listener The listener to remove
*/
public void removePropertyChangeListener(PropertyChangeListener listener)
{
support.removePropertyChangeListener(listener);
}
/**
* Return a String representation of this component.
*/
public String toString()
{
StringBuffer sb = new StringBuffer("StandardServer[");
sb.append(getPort());
sb.append("]");
return (sb.toString());
}
// ------------------------------------------------------ Lifecycle Methods
/**
* Add a LifecycleEvent listener to this component.
*
* @param listener The listener to add
*/
public void addLifecycleListener(LifecycleListener listener)
{
lifecycle.addLifecycleListener(listener);
}
/**
* Get the lifecycle listeners associated with this lifecycle. If this
* Lifecycle has no listeners registered, a zero-length array is returned.
*/
public LifecycleListener[] findLifecycleListeners()
{
return lifecycle.findLifecycleListeners();
}
/**
* Remove a LifecycleEvent listener from this component.
*
* @param listener The listener to remove
*/
public void removeLifecycleListener(LifecycleListener listener)
{
lifecycle.removeLifecycleListener(listener);
}
/**
* Prepare for the beginning of active use of the public methods of this
* component. This method should be called before any of the public
* methods of this component are utilized. It should also send a
* LifecycleEvent of type START_EVENT to any registered listeners.
*
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
public void start() throws LifecycleException
{
// Validate and update our current component state
if (started)
{
log.debug(sm.getString("standardServer.start.started"));
return;
}
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
lifecycle.fireLifecycleEvent(START_EVENT, null);
started = true;
// Start our defined Services
synchronized (services)
{
for (int i = 0; i < services.length; i++)
{
if (services[i] instanceof Lifecycle)
{
((Lifecycle) services[i]).start();
}
}
}
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
}
/**
* Gracefully terminate the active use of the public methods of this
* component. This method should be the last one called on a given
* instance of this component. It should also send a LifecycleEvent
* of type STOP_EVENT to any registered listeners.
*
* @exception LifecycleException if this component detects a fatal error
* that needs to be reported
*/
public void stop() throws LifecycleException
{
// Validate and update our current component state
if (!started)
{
return;
}
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);
lifecycle.fireLifecycleEvent(STOP_EVENT, null);
started = false;
// Stop our defined Services
for (int i = 0; i < services.length; i++)
{
if (services[i] instanceof Lifecycle)
{
((Lifecycle) services[i]).stop();
}
}
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);
}
}