com.delcyon.capo.server.CapoServer.java Source code

Java tutorial

Introduction

Here is the source code for com.delcyon.capo.server.CapoServer.java

Source

/**
Copyright (C) 2012  Delcyon, Inc.
    
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
    
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
    
You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.delcyon.capo.server;

import java.io.BufferedInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.math.BigInteger;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStore.PrivateKeyEntry;
import java.security.KeyStore.TrustedCertificateEntry;
import java.security.Security;
import java.security.Signature;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import javax.xml.bind.DatatypeConverter;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.bouncycastle.asn1.x500.X500NameBuilder;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.tanukisoftware.wrapper.WrapperManager;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import com.delcyon.capo.CapoApplication;
import com.delcyon.capo.CapoThreadFactory;
import com.delcyon.capo.Configuration;
import com.delcyon.capo.Configuration.PREFERENCE;
import com.delcyon.capo.controller.LocalRequestProcessor;
import com.delcyon.capo.datastream.BufferedSocket;
import com.delcyon.capo.datastream.ConsoleOutputStreamFilter;
import com.delcyon.capo.datastream.SocketFinalizer;
import com.delcyon.capo.datastream.StreamFinalizer;
import com.delcyon.capo.datastream.StreamHandler;
import com.delcyon.capo.datastream.StreamProcessor;
import com.delcyon.capo.datastream.StreamUtil;
import com.delcyon.capo.preferences.Preference;
import com.delcyon.capo.preferences.PreferenceInfo;
import com.delcyon.capo.preferences.PreferenceInfoHelper;
import com.delcyon.capo.preferences.PreferenceProvider;
import com.delcyon.capo.protocol.client.CapoConnection.ConnectionResponses;
import com.delcyon.capo.protocol.client.CapoConnection.ConnectionTypes;
import com.delcyon.capo.protocol.server.ClientRequestProcessorSessionManager;
import com.delcyon.capo.resourcemanager.CapoDataManager;
import com.delcyon.capo.resourcemanager.ResourceDescriptor;
import com.delcyon.capo.resourcemanager.ResourceDescriptor.Action;
import com.delcyon.capo.resourcemanager.ResourceDescriptor.LifeCycle;
import com.delcyon.capo.resourcemanager.ResourceParameter;
import com.delcyon.capo.resourcemanager.types.FileResourceType;
import com.delcyon.capo.server.jackrabbit.CapoJcrServer;
import com.delcyon.capo.server.jetty.CapoJettyServer;
import com.delcyon.capo.tasks.TaskManagerThread;
import com.delcyon.capo.xml.XPath;

/**
 * @author jeremiah
 */
@PreferenceProvider(preferences = CapoServer.Preferences.class)
public class CapoServer extends CapoApplication {

    public enum Preferences implements Preference {

        @PreferenceInfo(arguments = {
                "sec" }, defaultValue = "15", description = "The number of seconds to tell a client to wait before attempting to connect again", longOption = "INITIAL_CLIENT_RETRY_TIME", option = "INITIAL_CLIENT_RETRY_TIME")
        INITIAL_CLIENT_RETRY_TIME, @PreferenceInfo(arguments = {
                "ms" }, defaultValue = "60000", description = "The number of ms to keep an idle thread alive", longOption = "THREAD_IDLE_TIME", option = "THREAD_IDLE_TIME")
        THREAD_IDLE_TIME, @PreferenceInfo(arguments = {
                "ms" }, defaultValue = "0", description = "The number of ms to keep an socket alive", longOption = "SOCKET_IDLE_TIME", option = "SOCKET_IDLE_TIME")
        SOCKET_IDLE_TIME, @PreferenceInfo(arguments = {
                "int" }, defaultValue = "30", description = "The number of concurrent connection to allow", longOption = "MAX_THREADPOOL_SIZE", option = "MAX_THREADPOOL_SIZE")
        MAX_THREADPOOL_SIZE, @PreferenceInfo(arguments = {
                "int" }, defaultValue = "10", description = "The number of server threads to start with", longOption = "START_THREADPOOL_SIZE", option = "START_THREADPOOL_SIZE")
        START_THREADPOOL_SIZE, @PreferenceInfo(arguments = {
                "string" }, defaultValue = "capo.server.0", description = "ID that this server will use when communicating with clients", longOption = "SERVER_ID", option = "SERVER_ID")
        SERVER_ID, @PreferenceInfo(arguments = {
                "int" }, defaultValue = "1024", description = "Encryption key size", longOption = "KEY_SIZE", option = "KEY_SIZE")
        KEY_SIZE, @PreferenceInfo(arguments = {
                "months" }, defaultValue = "36", description = "Number of Months before key expires", longOption = "KEY_MONTHS_VALID", option = "KEY_MONTHS_VALID")
        KEY_MONTHS_VALID, @PreferenceInfo(arguments = {
                "dir" }, defaultValue = "clients", description = "resource where client information is stored", longOption = "CLIENTS_DIR", option = "CLIENTS_DIR")
        CLIENTS_DIR, @PreferenceInfo(arguments = {
                "webPort" }, defaultValue = "8443", description = "port to start webserver on", longOption = "WEB_PORT", option = "WEB_PORT")
        WEB_PORT, @PreferenceInfo(arguments = {
                "webSessionTimeout" }, defaultValue = "1800", description = "number of seconds before an inactive web session is logged out automatically", longOption = "WEB_SESSION_TIMEOUT", option = "WEB_SESSION_TIMEOUT")
        WEB_SESSION_TIMEOUT, @PreferenceInfo(arguments = {
                "disableRepo" }, defaultValue = "false", description = "prevent JCR repository from starting automatically", longOption = "DISABLE_REPO", option = "DISABLE_REPO")
        DISABLE_REPO, @PreferenceInfo(arguments = {
                "disableWebServer" }, defaultValue = "false", description = "prevent webserver from starting automatically", longOption = "DISABLE_WEBSERVER", option = "DISABLE_WEBSERVER")
        DISABLE_WEBSERVER;
        @Override
        public String[] getArguments() {
            return PreferenceInfoHelper.getInfo(this).arguments();
        }

        @Override
        public String getDefaultValue() {
            return java.util.prefs.Preferences.systemNodeForPackage(CapoApplication.getApplication().getClass())
                    .get(getLongOption(), PreferenceInfoHelper.getInfo(this).defaultValue());
        }

        @Override
        public String getDescription() {
            return PreferenceInfoHelper.getInfo(this).description();
        }

        @Override
        public String getLongOption() {
            return PreferenceInfoHelper.getInfo(this).longOption();
        }

        @Override
        public String getOption() {
            return PreferenceInfoHelper.getInfo(this).option();
        }

        @Override
        public Location getLocation() {
            return PreferenceInfoHelper.getInfo(this).location();
        }

    }

    private static final String BC = org.bouncycastle.jce.provider.BouncyCastleProvider.PROVIDER_NAME;
    private static final String APPLICATION_DIRECTORY_NAME = "server";

    private ServerSocket serverSocket;
    private boolean attemptSSL = true;
    //private boolean isReady = false;
    //private boolean isShutdown = false;
    private ThreadPoolExecutor threadPoolExecutor;
    private ClientRequestProcessorSessionManager clientRequestProcessorSessionManager;

    private ThreadGroup threadPoolGroup;
    private TrustManagerFactory trustManagerFactory;
    private KeyManagerFactory keyManagerFactory;
    //private SecureSocketListener secureSocketListener;
    //private SocketListener socketListener;
    private CapoJettyServer capoJettyServer;
    private CapoJcrServer capoJcrServer;

    public static final PrintStream sysout = System.out;
    public static final PrintStream syserr = System.err;
    public static ConsoleOutputStreamFilter outConsole = null;
    public static ConsoleOutputStreamFilter errConsole = null;

    /**
     * This is so that we can steal the output streams as quickly as possible
     */
    static {
        outConsole = new ConsoleOutputStreamFilter(System.out);
        System.setOut(new PrintStream(outConsole));
        errConsole = new ConsoleOutputStreamFilter(System.err);
        System.setErr(new PrintStream(errConsole));
    }

    public CapoServer() throws Exception {
        super();
    }

    @Override
    public Integer start(String[] programArgs) {
        try {
            init(programArgs);
            startup(programArgs);
        } catch (Exception e) {
            e.printStackTrace();
            return 1;
        }
        return null;
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        try {
            WrapperManager.start(new CapoServer(), args);
            //         CapoServer capoServer = new CapoServer();
            //         capoServer.init(args);
            //         capoServer.startup(args);
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(1);
        }
    }

    /**
     * @param programArgs
     * @throws Exception
     * @throws SecurityException
     */
    public void init(String[] programArgs) throws SecurityException, Exception {
        setApplicationState(ApplicationState.INITIALIZING);
        setConfiguration(new Configuration(programArgs));

        if (getConfiguration().hasOption(PREFERENCE.HELP)) {
            getConfiguration().printHelp();
            System.exit(0);
        }

        clientRequestProcessorSessionManager = new ClientRequestProcessorSessionManager();
        clientRequestProcessorSessionManager.start();

        //setup resource manager
        setDataManager(CapoDataManager.loadDataManager(getConfiguration().getValue(PREFERENCE.RESOURCE_MANAGER)));

        getDataManager().init(true);

        if (getConfiguration().getBooleanValue(Preferences.DISABLE_REPO) != true) {
            capoJcrServer = new CapoJcrServer();
            capoJcrServer.start();
            setSession(CapoJcrServer.createSession());
        }

        getDataManager().init(false);
        //change over to using the repo by default once were up and running
        //getDataManager().setDefaultResourceTypeScheme("repo");

        TaskManagerThread.startTaskManagerThread();

        Security.addProvider(new BouncyCastleProvider());
        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        ResourceDescriptor keystoreFile = getDataManager().getResourceDescriptor(null,
                getConfiguration().getValue(PREFERENCE.KEYSTORE));
        keystoreFile.addResourceParameters(null, new ResourceParameter(
                FileResourceType.Parameters.PARENT_PROVIDED_DIRECTORY, PREFERENCE.CONFIG_DIR));
        char[] password = getConfiguration().getValue(PREFERENCE.KEYSTORE_PASSWORD).toCharArray();
        if (keystoreFile.getResourceMetaData(null).exists() == false) {
            keyStore = buildKeyStore();
        } else {
            keystoreFile.open(null);
            InputStream keyStoreFileInputStream = keystoreFile.getInputStream(null);
            keyStore.load(keyStoreFileInputStream, password);
            keyStoreFileInputStream.close();
            keystoreFile.close(null);
        }

        setKeyStore(keyStore);

        trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        trustManagerFactory.init(getKeyStore());

        keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        keyManagerFactory.init(getKeyStore(),
                getConfiguration().getValue(PREFERENCE.KEYSTORE_PASSWORD).toCharArray());

        setSslSocketFactory(getLocalSslSocketFactory());

        serverSocket = new ServerSocket(getConfiguration().getIntValue(PREFERENCE.PORT));

        logger.log(Level.INFO, "Listening on " + serverSocket);

        try {
            runStartupScript("repo:" + getConfiguration().getValue(PREFERENCE.STARTUP_SCRIPT));
        } catch (Exception exception) {
            exception.printStackTrace();
        }

        if (getConfiguration().getBooleanValue(Preferences.DISABLE_WEBSERVER) != true) {
            capoJettyServer = new CapoJettyServer(keyStore,
                    getConfiguration().getValue(PREFERENCE.KEYSTORE_PASSWORD),
                    getConfiguration().getIntValue(Preferences.WEB_PORT));
            capoJettyServer.start();
        }

        setApplicationState(ApplicationState.INITIALIZED);

    }

    private void runStartupScript(String startupScriptName) throws Exception {
        ResourceDescriptor startupScriptResourceDescriptor = getDataManager().getResourceDescriptor(null,
                startupScriptName);
        startupScriptResourceDescriptor.addResourceParameters(null, new ResourceParameter(
                FileResourceType.Parameters.PARENT_PROVIDED_DIRECTORY, PREFERENCE.CONTROLLER_DIR));
        startupScriptResourceDescriptor.init(null, null, LifeCycle.EXPLICIT, false);
        if (startupScriptResourceDescriptor.getResourceMetaData(null).exists() == false) {
            startupScriptResourceDescriptor.performAction(null, Action.CREATE);
            //          startupScriptResourceDescriptor.close(null);
            //          startupScriptResourceDescriptor.open(null);

            Document startupDocument = CapoApplication.getDefaultDocument("server_startup.xml");
            OutputStream startupFileOutputStream = startupScriptResourceDescriptor.getOutputStream(null);
            TransformerFactory tFactory = TransformerFactory.newInstance();
            Transformer transformer = tFactory.newTransformer();
            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
            transformer.transform(new DOMSource(startupDocument), new StreamResult(startupFileOutputStream));
            startupFileOutputStream.close();
        }

        LocalRequestProcessor localRequestProcessor = new LocalRequestProcessor();
        localRequestProcessor.process(
                CapoApplication.getDocumentBuilder().parse(startupScriptResourceDescriptor.getInputStream(null)));
        startupScriptResourceDescriptor.close(null);
    }

    public void shutdown() throws Exception {
        long incrementalWaitTime = 2000;
        long maxWaitTime = 30000;
        long totalWaitTime = 0;
        logger.log(Level.INFO, "Shuting Down Server");

        capoJettyServer.shutdown();
        while (getApplicationState().ordinal() < ApplicationState.READY.ordinal()) {
            logger.log(Level.INFO, "Waiting for final shutdown...");
            Thread.sleep(incrementalWaitTime);
        }
        setApplicationState(ApplicationState.STOPPING);
        if (serverSocket != null) {
            logger.log(Level.INFO, "Closing Socket");
            serverSocket.close();
        }

        if (threadPoolExecutor != null) {
            logger.log(Level.INFO, "Stopping thread pool");
            threadPoolExecutor.shutdown();

            while (threadPoolExecutor.isTerminated() == false) {
                logger.log(Level.INFO, "Waiting for thread pool to shutdown...");
                sleep(incrementalWaitTime);
                totalWaitTime += incrementalWaitTime;
                if (totalWaitTime >= maxWaitTime) {
                    logger.log(Level.INFO, "Forcing thread pool to shutdown... ");
                    threadPoolExecutor.shutdownNow();
                }
            }
        }

        if (TaskManagerThread.getTaskManagerThread() != null) {
            logger.log(Level.INFO, "Stopping Task Manager");
            TaskManagerThread.getTaskManagerThread().interrupt();
            while (TaskManagerThread.getTaskManagerThread().getTaskManagerState() != ApplicationState.STOPPED) {
                sleep(incrementalWaitTime);
            }
            logger.log(Level.INFO, "Done Waiting for Task Manager to shutdown...");
        }

        if (clientRequestProcessorSessionManager != null) {
            logger.log(Level.INFO, "Stopping Session Manager");
            clientRequestProcessorSessionManager.shutdown();
            while (clientRequestProcessorSessionManager.isAlive()) {
                logger.log(Level.INFO, "Waiting for Session Manager to shutdown...");
                sleep(incrementalWaitTime);
            }
        }

        logger.log(Level.INFO, "Releaseing Resource Manager");
        getDataManager().release();
        logger.log(Level.INFO, "Removing Resource Manager");
        setDataManager(null);

        logger.log(Level.INFO, "Shutting Down JCR");
        capoJcrServer.shutdown();

        logger.log(Level.INFO, "Server Shutdown");
        setApplicationState(ApplicationState.STOPPED);
    }

    /**
     * Start Server Listening
     * 
     * @param programArgs
     * @throws Exception
     */
    public void startup(String[] programArgs) throws Exception {
        setApplicationState(ApplicationState.STARTING);
        SynchronousQueue<Runnable> synchronousQueue = new SynchronousQueue<Runnable>();
        threadPoolGroup = new ThreadGroup("Thread Pool Group");
        threadPoolExecutor = new ThreadPoolExecutor(
                getConfiguration().getIntValue(Preferences.START_THREADPOOL_SIZE),
                getConfiguration().getIntValue(Preferences.MAX_THREADPOOL_SIZE),
                getConfiguration().getIntValue(Preferences.THREAD_IDLE_TIME), TimeUnit.MILLISECONDS,
                synchronousQueue);
        threadPoolExecutor.setThreadFactory(new CapoThreadFactory(threadPoolGroup));

        start();
    }

    private SSLSocketFactory getLocalSslSocketFactory() throws Exception {
        SSLContext sslContext = SSLContext.getInstance("SSL");
        sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(),
                new java.security.SecureRandom());
        return sslContext.getSocketFactory();
    }

    @Override
    public void run() {

        setApplicationState(ApplicationState.READY);
        try {

            while (true) {
                Socket socket = null;
                try {
                    logger.log(Level.FINER, "waiting for connection");

                    try {
                        socket = serverSocket.accept();
                        socket.setTcpNoDelay(true);
                        socket.setSoTimeout(getConfiguration().getIntValue(Preferences.SOCKET_IDLE_TIME));
                    } catch (SocketException socketException) {
                        if (getApplicationState().ordinal() > ApplicationState.READY.ordinal()
                                && serverSocket.isClosed()) {
                            logger.log(Level.INFO, "Shutting down server");
                            //isReady = false;
                            return;
                        }
                    }
                    logger.log(Level.FINE, "got connection: " + socket);
                    socket = new BufferedSocket(socket);
                    BufferedInputStream inputStream = (BufferedInputStream) socket.getInputStream();

                    //figure out what kind of socket this is

                    inputStream.mark(getConfiguration().getIntValue(PREFERENCE.BUFFER_SIZE));
                    byte[] buffer = new byte[getConfiguration().getIntValue(PREFERENCE.BUFFER_SIZE)];
                    inputStream.read(buffer);
                    inputStream.reset();

                    String message = new String(buffer).trim();

                    if (message.matches(ConnectionTypes.CAPO_REQUEST.toString())) {
                        if (threadPoolExecutor.getActiveCount() < threadPoolExecutor.getMaximumPoolSize()) {
                            writeOKMessage(inputStream, socket, message, buffer);
                        } else {
                            writeBusyMessage(socket);
                            continue;
                        }
                    }

                    StreamProcessor streamProcessor = StreamHandler.getStreamProcessor(buffer);
                    String clientID = null;
                    HashMap<String, String> sessionHashMap = new HashMap<String, String>();
                    //if the header is an unknown type assume its SSL and pass it on, since we can't read anything from it
                    if (streamProcessor == null && attemptSSL == true) {
                        try {

                            socket = getSslSocketFactory().createSocket(socket,
                                    socket.getLocalAddress().getHostAddress(), socket.getLocalPort(), true);

                        } catch (Exception exception) {
                            CapoApplication.logger.log(Level.WARNING,
                                    "Unknown Stream Type: " + exception.getMessage());
                            socket.close();
                            continue;
                        }

                        //keep the ssl socket so that we can use it's session id for validation
                        SSLSocket sslSocket = (SSLSocket) socket;
                        sslSocket.setUseClientMode(false);
                        //sslSocket.setReuseAddress(false); it's too late for this here, just left as a note
                        sslSocket.setSendBufferSize(
                                (CapoApplication.getConfiguration().getIntValue(PREFERENCE.BUFFER_SIZE) * 2) + 728);
                        //we effectively have a brand new socket, so we have to wrap it as well, so we can reset of checking it's content
                        socket = new BufferedSocket(socket);
                        inputStream = (BufferedInputStream) socket.getInputStream();
                        inputStream.mark(getConfiguration().getIntValue(PREFERENCE.BUFFER_SIZE));
                        try {
                            inputStream.read(buffer);
                        } catch (SSLException sslException) {
                            CapoApplication.logger.log(Level.WARNING,
                                    "Unknown SSLException Type: " + sslException.getMessage());
                            socket.close();
                            continue;
                        }
                        String authMessage = new String(buffer).trim();
                        if (authMessage.matches("AUTH:CID=capo\\.(client|server)\\.\\d+:SIG=[A-F0-9]+.*:.*")) {
                            logger.fine(
                                    "SSL SID:" + DatatypeConverter.printHexBinary(sslSocket.getSession().getId()));
                            if (isValidAuthMessage(authMessage, sslSocket.getSession().getId()) == false) {
                                CapoApplication.logger.log(Level.WARNING,
                                        "Invalid AUTH attempt " + authMessage + " from: " + socket);
                                socket.close();
                                continue;
                            } else //got verified AUTH message
                            {
                                //the client is waiting for a response so send any single value;
                                socket.getOutputStream().write(0);
                                socket.getOutputStream().flush();
                                //reset to the beginning
                                inputStream.reset();
                                //skip ahead to the end of our AUTH message
                                inputStream.skip(authMessage.length());
                                //mark the spot
                                inputStream.mark(getConfiguration().getIntValue(PREFERENCE.BUFFER_SIZE));
                                //not re-read the buffer from our newly reset and marked location
                                Arrays.fill(buffer, (byte) 0);
                                StreamUtil.fullyReadIntoBufferUntilPattern(inputStream, buffer,
                                        ConnectionTypes.CAPO_REQUEST.toString().getBytes());
                                //store client id
                                clientID = authMessage.replaceFirst(
                                        "AUTH:CID=(capo\\.(client|server)\\.\\d+):SIG=[A-F0-9]+.*:.*", "$1");
                                sessionHashMap.put("clientID", clientID);

                                //check for a local client directory
                                ResourceDescriptor clientResourceDescriptor = getDataManager()
                                        .getResourceDescriptor(null, "clients:" + clientID);
                                if (clientResourceDescriptor.getResourceMetaData(null).exists() == false) {
                                    logger.log(Level.INFO, "Creating new clients resource for " + clientID);
                                    clientResourceDescriptor.performAction(null, Action.CREATE,
                                            new ResourceParameter(ResourceDescriptor.DefaultParameters.CONTAINER,
                                                    "true"));
                                    clientResourceDescriptor.close(null);
                                    clientResourceDescriptor.open(null);
                                }

                                //check for a client resource directory
                                ResourceDescriptor clientResourcesResourceDescriptor = clientResourceDescriptor
                                        .getChildResourceDescriptor(null,
                                                getConfiguration().getValue(PREFERENCE.RESOURCE_DIR));
                                if (clientResourcesResourceDescriptor.getResourceMetaData(null).exists() == false) {
                                    logger.log(Level.INFO, "Creating new resource dir for " + clientID);
                                    clientResourcesResourceDescriptor.performAction(null, Action.CREATE,
                                            new ResourceParameter(ResourceDescriptor.DefaultParameters.CONTAINER,
                                                    "true"));
                                    clientResourcesResourceDescriptor.close(null);
                                    clientResourcesResourceDescriptor.open(null);
                                }

                                //check for a client tasks directory
                                ResourceDescriptor clientTasksResourceDescriptor = clientResourceDescriptor
                                        .getChildResourceDescriptor(null, getConfiguration()
                                                .getValue(TaskManagerThread.Preferences.TASK_DIR));
                                if (clientTasksResourceDescriptor.getResourceMetaData(null).exists() == false) {
                                    logger.log(Level.INFO, "Creating new tasks dir for " + clientID);
                                    clientTasksResourceDescriptor.performAction(null, Action.CREATE,
                                            new ResourceParameter(ResourceDescriptor.DefaultParameters.CONTAINER,
                                                    "true"));
                                    clientTasksResourceDescriptor.close(null);
                                    clientTasksResourceDescriptor.open(null);
                                }

                                //update status information
                                ResourceDescriptor statusResourceDescriptor = clientResourceDescriptor
                                        .getChildResourceDescriptor(null, "status.xml");
                                Element statusRootElement = null;
                                if (statusResourceDescriptor.getResourceMetaData(null).exists() == false) {
                                    statusResourceDescriptor.performAction(null, Action.CREATE);
                                    statusRootElement = CapoApplication.getDefaultDocument("status.xml")
                                            .getDocumentElement();
                                } else {
                                    statusRootElement = getDocumentBuilder()
                                            .parse(statusResourceDescriptor.getInputStream(null))
                                            .getDocumentElement();
                                }
                                statusRootElement.setAttribute("lastConnectTime", System.currentTimeMillis() + "");
                                XPath.dumpNode(statusRootElement, statusResourceDescriptor.getOutputStream(null));
                                statusResourceDescriptor.close(null);

                            }

                            inputStream.reset();

                            message = new String(buffer).trim();

                            if (message.matches(ConnectionTypes.CAPO_REQUEST.toString())) {
                                if (threadPoolExecutor.getActiveCount() < threadPoolExecutor.getMaximumPoolSize()) {
                                    writeOKMessage(inputStream, socket, message, buffer);
                                } else {
                                    writeBusyMessage(socket);
                                    continue;
                                }
                            }
                        }

                        streamProcessor = StreamHandler.getStreamProcessor(buffer);

                        //reset the buffer
                        inputStream.reset();
                    }
                    logger.log(Level.FINE, "Request Buffer: '" + new String(buffer) + "'");

                    //we should have a stream handler by this point
                    if (streamProcessor == null) {

                        int initailSocketTimeout = socket.getSoTimeout();
                        try {
                            socket.setSoTimeout(10000);
                            //keep trying to read enough into the buffer to make a determination;                               
                            while (streamProcessor == null) {
                                int count = inputStream.read(buffer);
                                if (count > 0) {
                                    inputStream.reset();
                                    inputStream.read(buffer);
                                }
                                streamProcessor = StreamHandler.getStreamProcessor(buffer);
                            }
                        } catch (SocketTimeoutException socketTimeoutException) {
                            //one final try on the buffer                               
                            streamProcessor = StreamHandler.getStreamProcessor(buffer);
                        }
                        //still didn't find anything after at least 10 seconds 
                        if (streamProcessor == null) {
                            CapoApplication.logger.log(Level.WARNING,
                                    "Unknown Stream Type from " + socket.getRemoteSocketAddress());
                            CapoApplication.logger.log(Level.WARNING, "Unknown Stream Type: " + new String(buffer));
                            socket.close();
                            continue;
                        }
                        //reset the buffer, since we've been beating the crap out of it.
                        inputStream.reset();
                        //reset the socket timeout
                        socket.setSoTimeout(initailSocketTimeout);

                    }

                    StreamHandler streamHandler = new StreamHandler(streamProcessor);
                    StreamFinalizer streamFinalizer = new SocketFinalizer(socket);
                    streamHandler.add(streamFinalizer);
                    streamHandler.init(inputStream, socket.getOutputStream(), sessionHashMap);
                    logger.log(Level.FINE, "Starting a " + streamProcessor.getClass().getSimpleName()
                            + " Stream Handler for " + clientID + "@" + socket);
                    try {
                        threadPoolExecutor.execute(streamHandler);
                    } catch (RejectedExecutionException e) {
                        writeBusyMessage(socket);
                    }
                } catch (SocketTimeoutException socketTimeoutException) {
                    socket.close();
                    socketTimeoutException.printStackTrace();
                }
            }
        } catch (Exception exception) {
            CapoApplication.logger.log(Level.SEVERE, "Exiting due to uncaught exception.", exception);
        }

    }

    private void writeOKMessage(InputStream inputStream, Socket socket, String message, byte[] buffer)
            throws Exception {
        inputStream.skip(message.length());
        inputStream.mark(getConfiguration().getIntValue(PREFERENCE.BUFFER_SIZE));
        socket.getOutputStream().write(ConnectionResponses.OK.toString().getBytes());
        socket.getOutputStream().write(0);
        socket.getOutputStream().flush();
        Arrays.fill(buffer, (byte) 0);
        inputStream.read(buffer);
        inputStream.reset();
    }

    private void writeBusyMessage(Socket socket) throws Exception {
        String busyString = new String(ConnectionResponses.BUSY + " "
                + (getConfiguration().getIntValue(Preferences.INITIAL_CLIENT_RETRY_TIME) * 1000));
        logger.log(Level.WARNING,
                "Rejecting a request from " + socket.getRemoteSocketAddress() + " with " + busyString);
        socket.getOutputStream().write(busyString.getBytes());
        socket.getOutputStream().write(0);
        socket.getOutputStream().flush();
        socket.close();
    }

    private boolean isValidAuthMessage(String authMessage, byte[] sessionID) {
        if (authMessage == null) {
            return false;
        }
        if (authMessage.matches("AUTH:CID=capo\\.(client|server)\\.\\d+:SIG=[A-F0-9]+.*:.*") == false) {
            return false;
        }
        String[] splitAuthMessage = authMessage.split(":|=");
        if (splitAuthMessage.length != 5) {
            return false;
        }
        String clientID = splitAuthMessage[2];
        byte[] encodedSignature = DatatypeConverter.parseHexBinary(splitAuthMessage[4]);
        try {
            Certificate certificate = getKeyStore().getCertificate(clientID + ".cert");
            if (certificate == null) {
                return false;
            }
            Signature signature = Signature.getInstance("SHA256withRSA");
            signature.initVerify(certificate);
            signature.update(clientID.getBytes());
            signature.update(sessionID);
            if (signature.verify(encodedSignature) == true) {
                return true;
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return false;
    }

    @Override
    public String getApplicationDirectoryName() {
        return APPLICATION_DIRECTORY_NAME;
    }

    /**
     * This loads an xml file, starts he server, and sends the document as a
     * request, then returns an array of [requestDocument,responseDocument]
     * TESTING ONLY
     * 
     * @param filename
     * @return
     * @throws Exception
     */

    private KeyStore buildKeyStore() throws Exception {

        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        char[] password = getConfiguration().getValue(PREFERENCE.KEYSTORE_PASSWORD).toCharArray();
        //generate keys
        KeyPairGenerator rsakeyPairGenerator = KeyPairGenerator.getInstance("RSA");
        rsakeyPairGenerator.initialize(getConfiguration().getIntValue(Preferences.KEY_SIZE));
        KeyPair rsaKeyPair = rsakeyPairGenerator.generateKeyPair();

        //begin bouncy castle crap
        X500NameBuilder x500NameBuilder = new X500NameBuilder(BCStyle.INSTANCE);
        x500NameBuilder.addRDN(BCStyle.CN, getConfiguration().getValue(Preferences.SERVER_ID));

        ContentSigner contentSigner = new JcaContentSignerBuilder("SHA256WithRSAEncryption").setProvider(BC)
                .build(rsaKeyPair.getPrivate());

        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.MONTH, getConfiguration().getIntValue(Preferences.KEY_MONTHS_VALID));

        X509v3CertificateBuilder certificateBuilder = new JcaX509v3CertificateBuilder(x500NameBuilder.build(),
                BigInteger.valueOf(System.currentTimeMillis()), new Date(System.currentTimeMillis() - 50000),
                calendar.getTime(), x500NameBuilder.build(), rsaKeyPair.getPublic());

        X509Certificate certificate = new JcaX509CertificateConverter().setProvider(BC)
                .getCertificate(certificateBuilder.build(contentSigner));
        //end bouncy castle crap

        keyStore.load(null, password);
        KeyStore.TrustedCertificateEntry trustedCertificateEntry = new TrustedCertificateEntry(certificate);
        keyStore.setEntry(getConfiguration().getValue(Preferences.SERVER_ID), trustedCertificateEntry, null);
        keyStore.setEntry("capo.server.cert", trustedCertificateEntry, null);
        KeyStore.PrivateKeyEntry privateKeyEntry = new PrivateKeyEntry(rsaKeyPair.getPrivate(),
                new Certificate[] { certificate });
        keyStore.setEntry(getConfiguration().getValue(Preferences.SERVER_ID) + ".private", privateKeyEntry,
                new KeyStore.PasswordProtection(password));

        writeKeyStore(keyStore);

        return keyStore;
    }

    public synchronized void writeKeyStore(KeyStore keyStore) throws Exception {
        ResourceDescriptor keystoreFile = getDataManager().getResourceDescriptor(null,
                getConfiguration().getValue(PREFERENCE.KEYSTORE));
        keystoreFile.addResourceParameters(null, new ResourceParameter(
                FileResourceType.Parameters.PARENT_PROVIDED_DIRECTORY, PREFERENCE.CONFIG_DIR));
        char[] password = getConfiguration().getValue(PREFERENCE.KEYSTORE_PASSWORD).toCharArray();
        //save keystore
        OutputStream keyStoreFileOutputStream = keystoreFile.getOutputStream(null);
        keyStore.store(keyStoreFileOutputStream, password);
        keyStoreFileOutputStream.close();
        keystoreFile.close(null);
    }

}