nor.core.Nor.java Source code

Java tutorial

Introduction

Here is the source code for nor.core.Nor.java

Source

/*
 *  Copyright (C) 2010, 2011 Junpei Kawamoto
 *
 *  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 nor.core;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.ServiceLoader;
import java.util.logging.Level;

import nor.core.plugin.Plugin;
import nor.core.proxy.ProxyServer;
import nor.http.server.HttpRequestHandler;
import nor.http.server.local.TextResource;
import nor.http.server.proxyserver.ProxyRequestHandler;
import nor.http.server.proxyserver.Router;
import nor.util.io.Stream;
import nor.util.log.Logger;

import org.apache.commons.cli.BasicParser;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.Parser;
import org.apache.commons.cli.UnrecognizedOptionException;

/**
 * ?
 * ?????????????
 *
 * @author Junpei Kawamoto
 *
 */
public class Nor {

    /**
     * Local proxy server.
     */
    private ProxyServer server;

    /**
     * Proxy request handler.
     */
    private final ProxyRequestHandler handler;

    /**
     * Request router.
     */
    private final Router router = new Router();

    //
    private final File rootConfDir;
    private final File localConfDir;
    //

    private static final class Key {

        public static final String LISTEN_ADDRESS = "nor.address";
        public static final String LISTEN_PORT = "nor.port";

    }

    private static final class Template {

        public static final String PLUGIN_ENABEL = "%s.enable";

    }

    /**
     * 
     */
    private final Context context = new Context();

    /**
     * ?
     */
    private final List<Plugin> plugins = new ArrayList<Plugin>();

    /**
     * 
     */
    private static final Logger LOGGER = Logger.getLogger(Nor.class);

    //============================================================================
    //  Constants
    //============================================================================
    private static final String ConfigFileTemplate = "%s.conf";
    private static final String LoggindConfigFile = "logging.conf";

    //============================================================================
    //  Constractor
    //============================================================================
    private Nor(final String rootConfDir) {
        assert rootConfDir != null;

        this.rootConfDir = new File(rootConfDir);
        this.localConfDir = new File(this.rootConfDir, this.context.getHostName());
        this.localConfDir.mkdirs();

        // Load common/local conf files
        final File base = new File(this.rootConfDir, "nor.conf");
        try {

            final Properties baseProp = new Properties();
            baseProp.load(new FileInputStream(base));
            System.getProperties().putAll(baseProp);

            final File local = new File(this.localConfDir, "nor.conf");
            if (!local.exists()) {

                final InputStream in = this.getClass().getResourceAsStream("nor.conf");
                final OutputStream out = new FileOutputStream(local);

                Stream.copy(in, out);

                out.close();
                in.close();

            }

            final Properties localProp = new Properties();
            localProp.load(new FileInputStream(local));
            System.getProperties().putAll(localProp);

        } catch (final IOException e) {

            LOGGER.catched(Level.SEVERE, "Cannot load config file", e);

            final RuntimeException ne = new RuntimeException("Cannot start nor.");
            LOGGER.throwing("<init>", ne);
            throw ne;

        }

        // Create request handler
        this.handler = new ProxyRequestHandler(Nor.class.getSimpleName(), this.router);

    }

    //============================================================================
    //  Public methods
    //============================================================================
    public boolean enable(final Plugin plugin) {
        LOGGER.entering("enable", plugin);
        assert plugin != null;

        final boolean ret = Boolean.parseBoolean(
                System.getProperty(String.format(Template.PLUGIN_ENABEL, plugin.getClass().getName()), "true"));

        LOGGER.exiting("enable", ret);
        return ret;
    }

    //============================================================================
    //  Private methods
    //============================================================================
    //----------------------------------------------------------------------------
    //  
    //----------------------------------------------------------------------------
    private void init(final List<URL> jarUrls) throws IOException {
        LOGGER.entering("init");

        /*
         * Create a proxy server.
         */
        this.server = new ProxyServer(this.handler, this.router);

        final String pluginPath = System.getProperty("nor.plugin");
        if (pluginPath != null) {

            final File dir = new File(pluginPath);
            if (dir.isDirectory()) {

                for (final File f : dir.listFiles()) {

                    try {

                        jarUrls.add(f.toURI().toURL());

                    } catch (final MalformedURLException e) {

                        LOGGER.catched(Level.WARNING, "init", e);

                    }

                }

            }

        }

        /*
         * Load installed plugins
         */
        for (final URL url : jarUrls) {

            final URLClassLoader loader = new URLClassLoader(new URL[] { url });
            for (final Plugin p : ServiceLoader.load(Plugin.class, loader)) {

                final String name = p.getClass().getName();
                if (this.enable(p)) {

                    final File common = new File(this.rootConfDir, String.format(ConfigFileTemplate, name));
                    final File local = new File(this.localConfDir, String.format(ConfigFileTemplate, name));
                    p.init(common, local);

                    this.server.attach(p);
                    this.plugins.add(p);

                    LOGGER.info("init", "Loading a plugin {0}", p.getClass().getName());

                }

            }

        }

        /*
         * Load a routing table.
         */
        final File route = new File(this.localConfDir, "route.conf");
        if (!route.exists()) {

            final InputStream in = this.getClass().getResourceAsStream("route.conf");
            final BufferedReader r = new BufferedReader(new InputStreamReader(in));
            final PrintWriter w = new PrintWriter(new FileWriter(route));

            String buf;
            while ((buf = r.readLine()) != null) {

                w.println(buf);

            }
            w.close();
            r.close();

        }

        final Properties routings = new Properties();
        final Reader rin = new FileReader(route);
        routings.load(rin);
        rin.close();
        for (final Object key : routings.keySet()) {

            final String skey = (String) key;
            this.router.put(skey, routings.getProperty(skey));

        }

        LOGGER.exiting("init");
    }

    //----------------------------------------------------------------------------
    //  ?
    //----------------------------------------------------------------------------
    /**
     * ??
     * @throws IOException
     */
    private void start() throws IOException {
        LOGGER.entering("start");

        final String addr = System.getProperty(Key.LISTEN_ADDRESS);
        final int port = Integer.valueOf(System.getProperty(Key.LISTEN_PORT));

        /*
         * Register the PAC file.
         */
        this.server.localResourceRoot()
                .add(new TextResource("/nor/core/proxy.pac",
                        this.server.getPAC(addr, port, Boolean.valueOf(System.getProperty("nor.https", "false"))),
                        "application/x-javascript-config"));

        /*
         * Start the web server.
         */
        this.server.start(addr, port);

        LOGGER.exiting("start");
    }

    //----------------------------------------------------------------------------
    //  ?
    //----------------------------------------------------------------------------
    /**
     * ??
     * @throws IOException ??????????
     *
     */
    private void close() throws IOException {
        LOGGER.entering("close");

        // ??
        this.server.close();

        // ??
        for (final Plugin p : this.plugins) {

            p.close();

        }

        // ??
        // this.config.store();

        LOGGER.exiting("close");
    }

    //============================================================================
    //  Class members
    //============================================================================
    private static Nor nor;

    //============================================================================
    //  Class methods
    //============================================================================
    public static HttpRequestHandler getRequestHandler() {

        return nor.handler;

    }

    public static Router getRouter() {

        return nor.router;

    }

    //============================================================================
    //  main
    //============================================================================
    /**
     * ??
     * @param args ?????
     * @throws MalformedURLException
     */
    @SuppressWarnings("static-access")
    public static void main(final String[] args) {
        LOGGER.info("main", "Start up...");

        final Options ops = new Options();
        ops.addOption(OptionBuilder.withArgName("dir").hasArg()
                .withDescription("set directory which has config files").create("config"));

        ops.addOption(OptionBuilder.withArgName("file").hasArg()
                .withDescription("use given configu file for logging system").create("log"));

        final Option pluginsPath = OptionBuilder.withArgName("dir").hasArg()
                .withDescription("use given directory for a serch path of plugins").create("plugin_dir");
        ops.addOption(pluginsPath);

        final Option plugins = OptionBuilder.withArgName("file").hasArg().withDescription("use given plugin file")
                .create("plugin");
        ops.addOption(plugins);

        ops.addOption("help", false, "show this help");

        try {

            final Parser parser = new BasicParser();
            final CommandLine cmd = parser.parse(ops, args);

            if (cmd.hasOption("help")) {

                final HelpFormatter help = new HelpFormatter();
                help.printHelp("nor", ops, true);
                System.exit(0);

            }

            // Configure about logging system.
            InputStream logStream;
            if (cmd.hasOption("log")) {

                logStream = new FileInputStream(cmd.getOptionValue("log"));

            } else {

                final String file = System.getProperty("nor.log", LoggindConfigFile);
                logStream = Nor.class.getResourceAsStream(file);

            }
            Logger.loadConfig(logStream);
            logStream.close();

            // Create the application instance by given config directory
            if (cmd.hasOption("config")) {

                Nor.nor = new Nor(cmd.getOptionValue("config"));

            } else {

                Nor.nor = new Nor(System.getProperty("nor.config", "config"));

            }

            // Load plugins
            final List<URL> pluginJar = new ArrayList<URL>();
            if (cmd.hasOption("plugin")) {

                for (final String filename : cmd.getOptionValues("plugin")) {

                    final File f = new File(filename);
                    pluginJar.add(f.toURI().toURL());

                }

            }
            if (cmd.hasOption("plugin_dir")) {

                for (final String dirname : cmd.getOptionValues("plugin_dir")) {

                    final File dir = new File(dirname);
                    if (dir.isDirectory()) {

                        for (final String filename : dir.list()) {

                            final File f = new File(dir, filename);
                            pluginJar.add(f.toURI().toURL());

                        }

                    }

                }

            }
            nor.init(pluginJar);

            nor.start();

            // Waiting for end
            System.in.read();

            // Closing
            nor.close();

        } catch (final UnrecognizedOptionException e) {

            final HelpFormatter help = new HelpFormatter();
            help.printHelp("nor", ops, true);

        } catch (final Exception e) {

            LOGGER.catched(Level.SEVERE, "main", e);

        }

        LOGGER.info("main", "End.");

    }

}