org.columba.core.main.ColumbaServer.java Source code

Java tutorial

Introduction

Here is the source code for org.columba.core.main.ColumbaServer.java

Source

//The contents of this file are subject to the Mozilla Public License Version 1.1
//(the "License"); you may not use this file except in compliance with the
//License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
//
//Software distributed under the License is distributed on an "AS IS" basis,
//WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
//for the specific language governing rights and
//limitations under the License.
//
//The Original Code is "The Columba Project"
//
//The Initial Developers of the Original Code are Frederik Dietz and Timo Stich.
//Portions created by Frederik Dietz and Timo Stich are Copyright (C) 2003.
//
//All Rights Reserved.

package org.columba.core.main;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.StringTokenizer;

import javax.swing.JOptionPane;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.ParseException;
import org.columba.core.component.ComponentManager;
import org.columba.core.resourceloader.GlobalResourceLoader;
import org.columba.core.shutdown.ShutdownManager;

/**
 * Opens a server socket to manage multiple sessions of Columba capable of
 * passing commands to the main session.
 * <p>
 * This class is a singleton because there can only be one server per Columba
 * session.
 * <p>
 * Basic idea taken from www.jext.org (author Roman Guy)
 * 
 * @author fdietz
 */
public class ColumbaServer {
    static final String RESOURCE_PATH = "org.columba.core.i18n.dialog";

    /**
     * The anonymous user for single-user systems without user name.
     */
    protected static final String ANONYMOUS_USER = "anonymous";

    /**
     * The singleton instance of this class.
     */
    private static ColumbaServer instance;

    /**
     * The port range Columba should use is between LOWEST_PORT and 65536.
     */
    private static final int LOWEST_PORT = 30000;

    /**
     * Server runs in its own thread.
     */
    protected Thread thread;

    /**
     * The ServerSocket used by the server.
     */
    protected ServerSocket serverSocket;

    /**
     * Constructor
     */
    protected ColumbaServer() {
        thread = new Thread(new Runnable() {
            public void run() {
                while (!Thread.currentThread().isInterrupted()) {
                    try {
                        handleClient(serverSocket.accept());
                    } catch (SocketTimeoutException ste) {
                        // do nothing here, just continue
                    } catch (IOException ioe) {
                        ioe.printStackTrace();

                        // what to do here? we could start a new server...
                    }
                }

                try {
                    serverSocket.close();

                    // cleanup: remove port number file
                    SessionController.serializePortNumber(-1);
                } catch (IOException ioe) {
                }

                serverSocket = null;
            }
        }, "ColumbaServer");
        thread.setDaemon(true);

        // stop server when shutting down
        ShutdownManager.getInstance().register(new Runnable() {
            public void run() {
                stop();
            }
        });
    }

    /**
     * Starts the server.
     * 
     * @throws IOException
     */
    public synchronized void start() throws IOException {
        if (!isRunning()) {
            int port;
            int count = 0;

            while (serverSocket == null) {
                // create random port number within range
                Random random = new Random();
                port = random.nextInt(65536 - LOWEST_PORT) + LOWEST_PORT;

                try {
                    serverSocket = new ServerSocket();

                    // bind to localhost
                    serverSocket.bind(new InetSocketAddress("127.0.0.1", port));

                    // store port number in file
                    SessionController.serializePortNumber(port);
                } catch (SocketException se) { // port is in use, try next
                    count++;

                    if (count == 10) { // something is very wrong here
                        JOptionPane.showMessageDialog(null,
                                GlobalResourceLoader.getString(RESOURCE_PATH, "session", "err_10se_msg"),
                                GlobalResourceLoader.getString(RESOURCE_PATH, "session", "err_10se_title"),
                                JOptionPane.ERROR_MESSAGE);

                        // this is save because the only shutdown plugin
                        // to stop this server, the configuration isn't touched
                        System.exit(1);
                    }
                }
            }

            serverSocket.setSoTimeout(2000);
            thread.start();
        }
    }

    /**
     * Stops the server.
     */
    public synchronized void stop() {
        thread.interrupt();
    }

    /**
     * Check if server is already running
     * 
     * @return true, if server is running. False, otherwise
     */
    public synchronized boolean isRunning() {
        return thread.isAlive();
    }

    /**
     * Handles a client connect and authentication.
     */
    protected void handleClient(Socket client) {
        try {
            // only accept client from local machine
            String host = client.getLocalAddress().getHostAddress();
            if (!(host.equals("127.0.0.1"))) {
                // client isn't from local machine
                return;
            }

            BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream()));
            String line = reader.readLine();
            if (!line.startsWith("Columba ")) {
                return;
            }

            line = reader.readLine();
            if (!line.startsWith("User ")) {
                return;
            }

            PrintWriter writer = new PrintWriter(client.getOutputStream());
            if (!line.substring(5).equals(System.getProperty("user.name", ANONYMOUS_USER))) {
                writer.write("WRONG USER\r\n");
                writer.close();
                return;
            }
            writer.write("\r\n");
            writer.flush();

            line = reader.readLine();
            // do something with the arguments..
            List<String> list = new LinkedList<String>();
            StringTokenizer st = new StringTokenizer(line, "%");
            while (st.hasMoreTokens()) {
                String tok = (String) st.nextToken();
                list.add(tok);
            }

            try {
                CommandLine commandLine = ColumbaCmdLineParser.getInstance()
                        .parse((String[]) list.toArray(new String[0]));

                ComponentManager.getInstance().handleCommandLineParameters(commandLine);

            } catch (ParseException e) {
                e.printStackTrace();
            }
        } catch (IOException ioe) {
            ioe.printStackTrace();
        } finally {
            try {
                client.close();
            } catch (IOException ioe) {
            }
        }
    }

    /**
     * Returns the singleton instance of this class.
     * 
     * @return instance
     */
    public static synchronized ColumbaServer getColumbaServer() {
        if (instance == null) {
            instance = new ColumbaServer();
        }
        return instance;
    }
}