/**************************************************************************************
* Copyright (c) Jonas Bon�r, Alexandre Vasseur. All rights reserved. *
* http://aspectwerkz.codehaus.org *
* ---------------------------------------------------------------------------------- *
* The software in this package is published under the terms of the LGPL license *
* a copy of which has been included with this distribution in the license.txt file. *
**************************************************************************************/
package org.codehaus.aspectwerkz.connectivity;
import EDU.oswego.cs.dl.util.concurrent.BoundedBuffer;
import EDU.oswego.cs.dl.util.concurrent.LinkedQueue;
import EDU.oswego.cs.dl.util.concurrent.PooledExecutor;
import org.codehaus.aspectwerkz.exception.WrappedRuntimeException;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Properties;
/**
* Server that listens to a specified port for client requests. <p/>The implementation is based on sockets. <p/>The
* invoker spawns a specified number of listener threads in which each one of these spawns a new RemoteProxyServerThread
* for each client request that comes in. <p/>Uses a thread pool from util.concurrent.
*
* @author <a href="mailto:jboner@codehaus.org">Jonas Bon�r </a>
*/
public class RemoteProxyServer implements Runnable {
private static String HOST_NAME;
private static int PORT;
private static boolean BOUNDED_THREAD_POOL;
private static boolean LISTENER_THREAD_RUN_AS_DAEMON;
private static int BACKLOG;
private static int NUM_LISTENER_THREADS;
private static int LISTENER_THREAD_PRIORITY = Thread.NORM_PRIORITY;
private static int CLIENT_THREAD_TIMEOUT;
private static int THREAD_POOL_MAX_SIZE;
private static int THREAD_POOL_MIN_SIZE;
private static int THREAD_POOL_INIT_SIZE;
private static int THREAD_POOL_KEEP_ALIVE_TIME;
private static boolean THREAD_POOL_WAIT_WHEN_BLOCKED;
/**
* Initalize the server properties.
*/
static {
Properties properties = new Properties();
try {
properties.load(new FileInputStream(System.getProperty("aspectwerkz.resource.bundle")));
} catch (Exception e) {
System.out.println("no aspectwerkz resource bundle found on classpath, using defaults");
// ignore, use defaults
}
String property = properties.getProperty("remote.server.hostname");
if (property == null) {
HOST_NAME = property;
} else {
HOST_NAME = property;
}
property = properties.getProperty("remote.server.port");
if (property == null) {
PORT = 7777;
} else {
PORT = Integer.parseInt(property);
}
property = properties.getProperty("remote.server.listener.threads.backlog");
if (property == null) {
BACKLOG = 200;
} else {
BACKLOG = Integer.parseInt(property);
}
property = properties.getProperty("remote.server.listener.threads.nr");
if (property == null) {
NUM_LISTENER_THREADS = 10;
} else {
NUM_LISTENER_THREADS = Integer.parseInt(property);
}
property = properties.getProperty("remote.server.client.threads.timeout");
if (property == null) {
CLIENT_THREAD_TIMEOUT = 60000;
} else {
CLIENT_THREAD_TIMEOUT = Integer.parseInt(property);
}
property = properties.getProperty("remote.server.thread.pool.max.size");
if (property == null) {
THREAD_POOL_MAX_SIZE = 100;
} else {
THREAD_POOL_MAX_SIZE = Integer.parseInt(property);
}
property = properties.getProperty("remote.server.thread.pool.min.size");
if (property == null) {
THREAD_POOL_MIN_SIZE = 10;
} else {
THREAD_POOL_MIN_SIZE = Integer.parseInt(property);
}
property = properties.getProperty("remote.server.thread.pool.init.size");
if (property == null) {
THREAD_POOL_INIT_SIZE = 10;
} else {
THREAD_POOL_INIT_SIZE = Integer.parseInt(property);
}
property = properties.getProperty("remote.server.thread.pool.keep.alive.time");
if (property == null) {
THREAD_POOL_KEEP_ALIVE_TIME = 300000;
} else {
THREAD_POOL_KEEP_ALIVE_TIME = Integer.parseInt(property);
}
property = properties.getProperty("remote.server.thread.pool.type");
if ((property != null) && property.equals("dynamic")) {
BOUNDED_THREAD_POOL = false;
} else {
BOUNDED_THREAD_POOL = true;
}
property = properties.getProperty("remote.server.listener.threads.run.as.daemon");
if ((property != null) && property.equals("true")) {
LISTENER_THREAD_RUN_AS_DAEMON = true;
} else {
LISTENER_THREAD_RUN_AS_DAEMON = false;
}
property = properties.getProperty("remote.server.thread.pool.wait.when.blocked");
if ((property != null) && property.equals("true")) {
THREAD_POOL_WAIT_WHEN_BLOCKED = true;
} else {
THREAD_POOL_WAIT_WHEN_BLOCKED = false;
}
}
/**
* The server socket.
*/
private ServerSocket m_serverSocket = null;
/**
* All listener threads.
*/
private Thread[] m_listenerThreads = null;
/**
* The thread pool.
*/
private PooledExecutor m_threadPool = null;
/**
* The class loader to use.
*/
private ClassLoader m_loader = null;
/**
* The invoker instance.
*/
private Invoker m_invoker = null;
/**
* Marks the server as running.
*/
private boolean m_running = true;
/**
* Starts a server object and starts listening for client access.
*
* @param loader the classloader to use
* @param invoker the invoker that makes the method invocation in the client thread
*/
public RemoteProxyServer(final ClassLoader loader, final Invoker invoker) {
m_invoker = invoker;
m_loader = loader;
}
/**
* Starts up the proxy server.
*/
public void start() {
m_running = true;
try {
InetAddress bindAddress = InetAddress.getByName(HOST_NAME);
m_serverSocket = new ServerSocket(PORT, BACKLOG, bindAddress);
if (BOUNDED_THREAD_POOL) {
createBoundedThreadPool(
THREAD_POOL_MAX_SIZE,
THREAD_POOL_MIN_SIZE,
THREAD_POOL_INIT_SIZE,
THREAD_POOL_KEEP_ALIVE_TIME,
THREAD_POOL_WAIT_WHEN_BLOCKED
);
} else {
createDynamicThreadPool(THREAD_POOL_MIN_SIZE, THREAD_POOL_INIT_SIZE, THREAD_POOL_KEEP_ALIVE_TIME);
}
m_listenerThreads = new Thread[NUM_LISTENER_THREADS];
for (int i = 0; i < NUM_LISTENER_THREADS; i++) {
m_listenerThreads[i] = new Thread(this);
m_listenerThreads[i].setName("AspectWerkz::Listener " + (i + 1));
m_listenerThreads[i].setDaemon(LISTENER_THREAD_RUN_AS_DAEMON);
m_listenerThreads[i].setPriority(LISTENER_THREAD_PRIORITY);
m_listenerThreads[i].start();
}
} catch (IOException e) {
throw new WrappedRuntimeException(e);
}
}
/**
* Stops the socket proxy server.
*/
public void stop() {
m_running = false;
for (int i = 0; i < NUM_LISTENER_THREADS; i++) {
m_listenerThreads[i].interrupt();
}
m_threadPool.shutdownNow();
}
/**
* Does the actual work of listening for a client request and spawns a new RemoteProxyServerThread to serve the
* client.
*/
public void run() {
try {
while (m_running) {
final Socket clientSocket = m_serverSocket.accept();
synchronized (m_threadPool) {
m_threadPool.execute(
new RemoteProxyServerThread(
clientSocket,
m_loader,
m_invoker,
CLIENT_THREAD_TIMEOUT
)
);
}
}
m_serverSocket.close();
} catch (Exception e) {
throw new WrappedRuntimeException(e);
}
}
/**
* Creates a new bounded thread pool.
*
* @param threadPoolMaxSize
* @param threadPoolMinSize
* @param threadPoolInitSize
* @param keepAliveTime
* @param waitWhenBlocked
*/
private void createBoundedThreadPool(final int threadPoolMaxSize,
final int threadPoolMinSize,
final int threadPoolInitSize,
final int keepAliveTime,
final boolean waitWhenBlocked) {
m_threadPool = new PooledExecutor(new BoundedBuffer(threadPoolInitSize), threadPoolMaxSize);
m_threadPool.setKeepAliveTime(keepAliveTime);
m_threadPool.createThreads(threadPoolInitSize);
m_threadPool.setMinimumPoolSize(threadPoolMinSize);
if (waitWhenBlocked) {
m_threadPool.waitWhenBlocked();
}
}
/**
* Creates a new dynamic thread pool
*
* @param threadPoolMinSize
* @param threadPoolInitSize
* @param keepAliveTime
*/
private void createDynamicThreadPool(final int threadPoolMinSize,
final int threadPoolInitSize,
final int keepAliveTime) {
m_threadPool = new PooledExecutor(new LinkedQueue());
m_threadPool.setKeepAliveTime(keepAliveTime);
m_threadPool.createThreads(threadPoolInitSize);
m_threadPool.setMinimumPoolSize(threadPoolMinSize);
}
}