org.krakenapps.main.Kraken.java Source code

Java tutorial

Introduction

Here is the source code for org.krakenapps.main.Kraken.java

Source

/*
 * Copyright 2009 NCHOVY
 * 
 * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.krakenapps.main;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.instrument.Instrumentation;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URLDecoder;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;

import org.apache.felix.framework.Felix;
import org.apache.felix.framework.util.FelixConstants;
import org.apache.felix.framework.util.StringMap;
import org.apache.felix.prefs.impl.PreferencesManager;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.DailyRollingFileAppender;
import org.apache.log4j.Level;
import org.apache.log4j.PatternLayout;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
import org.apache.sshd.SshServer;
import org.apache.sshd.common.NamedFactory;
import org.apache.sshd.server.Command;
import org.apache.sshd.server.UserAuth;
import org.apache.sshd.server.auth.UserAuthPassword;
import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
import org.apache.sshd.server.sftp.SftpSubsystem;
import org.krakenapps.account.AccountScriptFactory;
import org.krakenapps.api.Environment;
import org.krakenapps.api.InstrumentationService;
import org.krakenapps.api.LoggerControlService;
import org.krakenapps.api.ScriptFactory;
import org.krakenapps.auth.AuthScriptFactory;
import org.krakenapps.auth.DefaultAuthService;
import org.krakenapps.auth.api.AuthService;
import org.krakenapps.bundle.BundleManagerService;
import org.krakenapps.bundle.BundleScript;
import org.krakenapps.bundle.BundleScriptFactory;
import org.krakenapps.confdb.ConfigService;
import org.krakenapps.confdb.file.FileConfigService;
import org.krakenapps.console.TelnetCodecFactory;
import org.krakenapps.console.TelnetHandler;
import org.krakenapps.cron.CronService;
import org.krakenapps.cron.impl.CronScriptFactory;
import org.krakenapps.cron.impl.CronServiceImpl;
import org.krakenapps.cron.msgbus.CronPlugin;
import org.krakenapps.instrumentation.InstrumentationServiceImpl;
import org.krakenapps.keystore.KeyStoreScriptFactory;
import org.krakenapps.logger.KrakenLogService;
import org.krakenapps.logger.LogCleaner;
import org.krakenapps.logger.LoggerScriptFactory;
import org.krakenapps.pkg.PackageScriptFactory;
import org.krakenapps.script.ConfScriptFactory;
import org.krakenapps.script.CoreScriptFactory;
import org.krakenapps.script.HistoryScriptFactory;
import org.krakenapps.script.OsgiScriptFactory;
import org.krakenapps.script.OutputOnlyScriptContext;
import org.krakenapps.script.PerfScriptFactory;
import org.krakenapps.script.SunPerfScriptFactory;
import org.krakenapps.script.batch.BatchScriptFactory;
import org.krakenapps.ssh.SshCommandFactory;
import org.krakenapps.ssh.SshFileSystemFactory;
import org.krakenapps.ssh.SshPasswordAuthenticator;
import org.krakenapps.thread.ThreadScriptFactory;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
import org.osgi.framework.launch.Framework;
import org.osgi.service.log.LogService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.impl.KrakenLoggerFactory;
import org.slf4j.impl.StaticLoggerBinder;

import sun.misc.Signal;
import sun.misc.SignalHandler;

/**
 * Create the OSGi world using apache felix framework. Kraken class acts as a
 * system bundle.
 * 
 * @author xeraph
 * 
 */
@SuppressWarnings("restriction")
public class Kraken implements BundleActivator, SignalHandler {
    public static String BANNER = "Kraken";

    private static BundleContext context = null;
    public static Instrumentation instrumentation = null;
    private Logger logger = null;

    private Thread logCleaner = null;

    private PreferencesManager prefsManager;
    private ConfigService conf;
    private AuthService auth;
    private CronService cron;

    private static boolean serviceMode = false;

    // temporal access. remind cyclic dependency.
    public static BundleContext getContext() {
        return context;
    }

    public static void premain(String args, Instrumentation inst) {
        instrumentation = inst;
    }

    /**
     * Entry point.
     * 
     * @param args
     *            use system property instead.
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        startKraken(new StartOptions(args));
    }

    private static Kraken kraken;

    private static void startKraken(StartOptions startOptions) throws Exception {
        kraken = new Kraken();
        try {
            Signal signal = new Signal("TERM");
            Signal.handle(signal, kraken);
        } catch (Exception e) {
            System.out.println("Signal handling is only supported on Sun JVM");
        }

        kraken.boot(startOptions);
    }

    public static boolean isServiceMode() {
        return serviceMode;
    }

    public static void stopKraken() throws Exception {
        context.getBundle(0).stop();
    }

    public static void windowsService(String[] args) throws Exception {
        String cmd = "start";
        if (args.length > 0) {
            cmd = args[0];
        }

        if ("start".equals(cmd)) {
            serviceMode = true;
            startKraken(new StartOptions());
        } else {
            stopKraken();
        }
    }

    @Override
    public void handle(Signal signal) {
        try {
            context.getBundle(0).stop();
        } catch (BundleException e) {
            e.printStackTrace();
        }
    }

    /**
     * Boot felix framework up.
     * 
     * @param startOptions
     * 
     * @throws Exception
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public void boot(StartOptions startOptions) throws Exception {
        File jarPath = new File(URLDecoder
                .decode(Kraken.class.getProtectionDomain().getCodeSource().getLocation().getPath(), "utf-8"));
        File dir = jarPath.getParentFile();

        Environment.setKrakenSystemProperties(dir.getAbsolutePath());

        setLogger();

        List activators = new ArrayList();
        activators.add(this);

        Map configMap = new StringMap(false);
        Logger logger = LoggerFactory.getLogger(Felix.class.getName());
        configMap.put(FelixConstants.LOG_LOGGER_PROP, logger);
        configMap.put(FelixConstants.LOG_LEVEL_PROP, "3"); // INFO
        configMap.put(FelixConstants.SYSTEMBUNDLE_ACTIVATORS_PROP, activators);
        configMap.put(Constants.FRAMEWORK_SYSTEMPACKAGES, getSystemPackages());
        configMap.put(Constants.FRAMEWORK_STORAGE,
                new File(System.getProperty("kraken.cache.dir")).getAbsolutePath());

        configMap.put(Constants.FRAMEWORK_BOOTDELEGATION,
                "org.eclipse.tptp.martini,com.jprofiler.*,com.jprofiler.agent.*");

        felix = new Felix(configMap);
        if (startOptions.isDeveloperMode()) {
            felix.init();
            BundleContext bundleContext = felix.getBundleContext();
            BundleManagerService manager = new BundleManagerService(bundleContext);
            BundleScript script = new BundleScript(context, manager);
            script.setScriptContext(new OutputOnlyScriptContext(logger));
            script.updateAll(new String[] { "force" });
            script.refresh(new String[0]);
            bundleContext.removeBundleListener(manager);
        }
        felix.start();
    }

    private Felix felix = null;

    public Framework getFramework() {
        return felix;
    }

    /**
     * Load log4j.properties file from working directory by default. If you need
     * to change log4j configuration file's location, set log4j.configuration
     * system property.
     * 
     * @throws IOException
     */
    private void setLogger() throws IOException {
        if (new File("log4j.properties").exists()) {
            System.setProperty("log4j.configuration", "file:log4j.properties");
        } else {
            setDefaultLogging();
        }

        logger = LoggerFactory.getLogger(Kraken.class.getName());
    }

    /**
     * Fetch all default packages provided by JavaSE 1.6 environment. All OSGi
     * bundles can use JavaSE packages naturally by doing this. Some OSGi and
     * logging related packages also included.
     * 
     * @return the whole concatenated list of system packages.
     * @throws FileNotFoundException
     */
    private String getSystemPackages() throws FileNotFoundException {
        StringBuffer buffer = new StringBuffer(4096);
        BufferedReader reader = new BufferedReader(
                new InputStreamReader(ClassLoader.getSystemResourceAsStream("system.packages")));
        String s = null;
        try {
            while ((s = reader.readLine()) != null) {
                buffer.append(s);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return buffer.toString();
    }

    /**
     * Bind telnet port.
     * 
     * @throws IOException
     */
    private void openConsolePort() throws IOException {
        InetAddress address = getConsoleBindAddress();
        int port = getConsolePortNumber();
        InetSocketAddress bindSocketAddress = new InetSocketAddress(address, port);

        NioSocketAcceptor acceptor = new NioSocketAcceptor();
        acceptor.getFilterChain().addLast("protocol", new ProtocolCodecFilter(new TelnetCodecFactory()));
        acceptor.setHandler(new TelnetHandler(context));
        acceptor.setReuseAddress(true);
        acceptor.bind(bindSocketAddress);
        logger.info("Console " + bindSocketAddress + " opened.");
    }

    /**
     * Return localhost address by default. Use kraken.bind.address system
     * property if you need to connect telnet server from remote host.
     * 
     * @return the bind address
     * @throws UnknownHostException
     */
    private InetAddress getConsoleBindAddress() throws UnknownHostException {
        String telnetAddress = System.getProperty("kraken.telnet.address");
        if (telnetAddress == null)
            return InetAddress.getByName("localhost");

        return InetAddress.getByName(telnetAddress);
    }

    /**
     * 
     * @return the telnet port number
     */
    private int getConsolePortNumber() {
        int consolePort = 7004;
        try {
            // @deprecated
            consolePort = Integer.parseInt((String) System.getProperty("kraken.port"));
        } catch (Exception e) {
            // ignore
        }
        try {
            consolePort = Integer.parseInt((String) System.getProperty("kraken.telnet.port"));
        } catch (Exception e) {
            // ignore
        }

        return consolePort;
    }

    /*************************************************
     * BundleActivator interfaces
     *************************************************/

    /**
     * Start system bundle.
     */
    @Override
    public void start(BundleContext context) throws Exception {
        Kraken.context = context;

        logger.info("Booting Kraken.");
        startLogging();
        setBanner();

        prefsManager = new PreferencesManager();
        prefsManager.start(context);
        conf = new FileConfigService();
        auth = new DefaultAuthService(context, conf);
        cron = new CronServiceImpl(context, conf);

        registerScripts();
        registerInstrumentation();
        openConsolePort();
        startSshServer();
        logger.info("Kraken started.");
    }

    private void setBanner() throws IOException {
        try {
            String jarFileName = System.getProperty("java.class.path")
                    .split(System.getProperty("path.separator"))[0];
            JarFile jar = new JarFile(jarFileName);
            Manifest mf = jar.getManifest();
            Attributes attrs = mf.getMainAttributes();
            BANNER = "Kraken (version " + attrs.getValue("Kraken-Version") + ")";
        } catch (FileNotFoundException e) {
            BANNER = "Kraken (Debug mode)";
        }
    }

    /**
     * Register default kraken scripts.
     * 
     * @see Kraken API documentation
     */
    private void registerScripts() {
        // config service should be registered first
        registerScriptFactory(new ConfScriptFactory(conf), "conf");

        registerScriptFactory(CoreScriptFactory.class, "core");
        registerScriptFactory(BundleScriptFactory.class, "bundle");
        registerScriptFactory(LoggerScriptFactory.class, "logger");
        registerScriptFactory(OsgiScriptFactory.class, "osgi");
        registerScriptFactory(PackageScriptFactory.class, "pkg");
        registerScriptFactory(HistoryScriptFactory.class, "history");
        registerScriptFactory(ThreadScriptFactory.class, "thread");
        registerScriptFactory(PerfScriptFactory.class, "perf");
        registerScriptFactory(KeyStoreScriptFactory.class, "keystore");
        registerScriptFactory(new AccountScriptFactory(context, conf), "account");
        registerScriptFactory(SunPerfScriptFactory.class, "sunperf");
        registerScriptFactory(new AuthScriptFactory(auth), "auth");
        registerScriptFactory(new CronScriptFactory(context, cron), "cron");
        registerScriptFactory(BatchScriptFactory.class, "batch");
    }

    /**
     * Register script factory to OSGi service registry.
     * 
     * @param scriptFactory
     *            the script factory
     * @param alias
     *            the script alias (e.g. logger is alias in "logger.list"
     *            command)
     */
    private void registerScriptFactory(Class<? extends ScriptFactory> scriptFactory, String alias) {
        try {
            registerScriptFactory(scriptFactory.newInstance(), alias);
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    private void registerScriptFactory(ScriptFactory scriptFactory, String alias) {
        Dictionary<String, Object> props = new Hashtable<String, Object>();
        props.put("alias", alias);
        context.registerService(ScriptFactory.class.getName(), scriptFactory, props);
    }

    private void registerInstrumentation() {
        context.registerService(InstrumentationService.class.getName(), new InstrumentationServiceImpl(), null);
        context.registerService(CronService.class.getName(), cron, null);
        context.registerService(CronPlugin.class.getName(), new CronPlugin(cron), null);
    }

    /**
     * Stop system bundle.
     */
    @Override
    public void stop(BundleContext context) throws Exception {
        prefsManager.stop(context);

        stopLogging();

        if (!serviceMode)
            System.exit(0);
    }

    /**
     * Register OSGi log service to OSGi service registry, and start logging
     * thread. Logging thread will log using log4j logger and pass all logs to
     * connected log monitor.
     */
    private void startLogging() {
        context.registerService(new String[] { LogService.class.getName(), LoggerControlService.class.getName() },
                new KrakenLogService(), null);

        KrakenLoggerFactory krakenLoggerFactory = (KrakenLoggerFactory) StaticLoggerBinder.getSingleton()
                .getLoggerFactory();
        krakenLoggerFactory.start();

        enforceLogLevel("httpclient.wire");
        enforceLogLevel("org.apache.commons.httpclient");
    }

    private void enforceLogLevel(String loggerName) {
        org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(loggerName);
        logger.setLevel(Level.INFO);
    }

    /**
     * Stop the logging thread.
     */
    private void stopLogging() {
        KrakenLoggerFactory krakenLoggerFactory = (KrakenLoggerFactory) StaticLoggerBinder.getSingleton()
                .getLoggerFactory();
        krakenLoggerFactory.stop();
    }

    private void setDefaultLogging() throws IOException {
        org.apache.log4j.Logger rootLogger = org.apache.log4j.Logger.getRootLogger();
        if (!rootLogger.getAllAppenders().hasMoreElements()) {
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss,SSS");
            System.out.println(String.format(
                    "[%s]  INFO (Kraken) - Default logging enabled. "
                            + "Configure log4j.properties file for custom logging.",
                    dateFormat.format(new Date())));

            String logPath = new File(System.getProperty("kraken.log.dir"), "kraken.log").getAbsolutePath();
            rootLogger.setLevel(Level.DEBUG);
            PatternLayout layout = new PatternLayout("[%d] %5p (%c{1}) - %m%n");
            rootLogger.addAppender(new ConsoleAppender(layout));
            rootLogger.addAppender(new DailyRollingFileAppender(layout, logPath, ".yyyy-MM-dd"));

            logCleaner = new Thread(new LogCleaner(), "Kraken Log Cleaner");
            logCleaner.start();
        }
    }

    private void startSshServer() throws IOException {
        org.apache.log4j.Logger sshLogger = org.apache.log4j.Logger
                .getLogger("org.apache.sshd.server.session.ServerSession");
        sshLogger.setLevel(Level.WARN);

        String sshAddress = System.getProperty("kraken.ssh.address");

        int port = 7022;
        try {
            port = Integer.parseInt((String) System.getProperty("kraken.ssh.port"));
        } catch (Exception e) {
            // ignore
        }

        SshServer sshd = SshServer.setUpDefaultServer();
        sshd.setHost(sshAddress);
        sshd.setPort(port);
        sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider("hostkey.pem"));
        sshd.setShellFactory(new SshCommandFactory());
        sshd.setPasswordAuthenticator(new SshPasswordAuthenticator());

        List<NamedFactory<UserAuth>> userAuthFactories = new ArrayList<NamedFactory<UserAuth>>();
        userAuthFactories.add(new UserAuthPassword.Factory());
        sshd.setUserAuthFactories(userAuthFactories);

        List<NamedFactory<Command>> namedFactories = new ArrayList<NamedFactory<Command>>();
        namedFactories.add(new SftpSubsystem.Factory());
        sshd.setSubsystemFactories(namedFactories);
        sshd.setFileSystemFactory(new SshFileSystemFactory());

        sshd.start();
    }
}