/* * 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(); } }