Package bndtools.launcher

Source Code of bndtools.launcher.Main

/*******************************************************************************
* Copyright (c) 2010 Neil Bartlett.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*     Neil Bartlett - initial API and implementation
******************************************************************************/
package bndtools.launcher;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.Enumeration;
import java.util.Properties;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.ConsoleHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
import java.util.logging.StreamHandler;

import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
import org.osgi.framework.SynchronousBundleListener;
import org.osgi.framework.launch.Framework;
import org.osgi.framework.launch.FrameworkFactory;

public class Main implements Runnable {

  private static final String DEFAULT_PROPS_FILE = "launch.properties";

  private Logger logger;

  public static void main(String[] args) {
      Main main = new Main();
    try {
      main.init(args);
      main.run();
    } catch (Throwable t) {
      t.printStackTrace();
    } finally {
        int code = main.errorOccurred ? 1 : 0;
      System.exit(code);
    }
  }

  File propsFile;
  boolean enableDebug = false;
  volatile boolean errorOccurred = false;

  public void init(String[] args) throws IllegalArgumentException {
    String fileName = DEFAULT_PROPS_FILE;
    if(args != null) {
      for (String arg : args) {
        if("--debug".equalsIgnoreCase(arg))
          enableDebug = true;
        else if("--help".equalsIgnoreCase(arg)) {
          printHelp();
          System.exit(0);
        }
        else
          fileName = arg;
      }
    }
    propsFile = new File(fileName);
  }

  public void run() {
    // LOAD PROPERTIES
    Properties props = new Properties();
    try {
      if(propsFile.isFile()) {
        debug(MessageFormat.format("Loading launcher properties from {0}.", propsFile.getAbsoluteFile()));
        props.load(new FileInputStream(propsFile));
      } else {
        debug(MessageFormat.format("Launcher properties file {0} not found. Using default properties.", propsFile.getAbsoluteFile()));
      }
    } catch (IOException e) {
      System.err.println("Error loading launch properties.");
      e.printStackTrace();
      return;
    }

    // LOGGING
    Handler logHandler = initialiseLogging(props);

    // STORAGE
    File storageDir = initialiseStorage(props);

    try {
      // LOAD RUNTIME PROPERTIES
      Properties config = new Properties();
      config.put(Constants.FRAMEWORK_STORAGE, storageDir.getAbsolutePath());
      copyFrameworkConfig(props, config);

      // CREATE FRAMEWORK AND SYNC BUNDLES
      Framework framework = createAndRunFramework(config);
      if(framework == null) return;
      BundleContext fwContext = framework.getBundleContext();

      // CREATE INSTALLER
      Thread installerThread = createInstaller(fwContext, props);
     
      // MAIN THREAD EXECUTOR
      if(framework.getState() == Bundle.ACTIVE) // Check the framework hasn't already shutdown
          createAndRunMainThreadExecutor(fwContext, "main");

      // SHUTDOWN
      try {
        logger.info("Waiting for the framework to stop.");
        framework.waitForStop(0);
        if(installerThread != null)
          installerThread.interrupt();
        logger.info("Framework stopped.");
      } catch (InterruptedException e) {
        // Ignore
      }
      logger.info("Main thread finishing.");
    } finally {
      if(logHandler != null) {
        logHandler.close();
      }
    }
  }

  Handler initialiseLogging(Properties props) {
    // Initialise logging
    String logLevelStr = props.getProperty(LauncherConstants.PROP_LOG_LEVEL, LauncherConstants.DEFAULT_LOG_LEVEL);
    if(enableDebug)
      System.err.println(MessageFormat.format("Setting main log level to {0}.", logLevelStr));

    Logger rootLogger = Logger.getLogger("");
    Handler[] handlers = rootLogger.getHandlers();
    for (Handler handler : handlers) {
      rootLogger.removeHandler(handler);
    }

    Handler handler = null;
    String logOutput = props.getProperty(LauncherConstants.PROP_LOG_OUTPUT, LauncherConstants.DEFAULT_LOG_OUTPUT);
    if(logOutput.startsWith("file:")) {
      logOutput = logOutput.substring("file:".length());
      try {
        FileOutputStream stream = new FileOutputStream(logOutput, true);
        handler = new StreamHandler(stream, new SimpleFormatter());
        if(enableDebug)
          System.err.println(MessageFormat.format("Logging to file {0}.", logOutput));
      } catch (FileNotFoundException e) {
        System.err.println(MessageFormat.format("Could not write to specified log file {0}. Falling back to the console.", logOutput));
        handler = new ConsoleHandler();
      }
    } else {
      handler = new ConsoleHandler();
      if(enableDebug)
        System.err.println("Logging to the console.");
    }
    handler.setLevel(Level.ALL); // Ensure the handler does not filter out any messages from the loggers
    rootLogger.addHandler(handler);
    rootLogger.setLevel(Level.parse(logLevelStr));
    logger = Logger.getLogger("bndtools.launcher");

    return handler;
  }

  File initialiseStorage(Properties props) {
    // Check the storage dir path
    String storagePathStr = props.getProperty(LauncherConstants.PROP_STORAGE_DIR, LauncherConstants.DEFAULT_STORAGE_DIR);

    File storagePath = new File(storagePathStr);
    if(!storagePath.isAbsolute()) {
      File workingDir = new File(System.getProperty("user.dir"));
      storagePath = new File(workingDir, storagePathStr);
    }
    logger.log(Level.FINE, "Using storage dir: {0}.", storagePath.getAbsolutePath());

    // Clean it if requested
    boolean clean = "true".equalsIgnoreCase(props.getProperty(LauncherConstants.PROP_STORAGE_CLEAN));
    if(clean) {
      try {
        logger.log(Level.INFO, "Cleaning storage directory {0}.", storagePath.getAbsolutePath());
        FileUtil.deleteDirectory(storagePath);
      } catch (IOException e) {
        logger.log(Level.SEVERE, "Error while cleaning framework storage directory {0}.", storagePath.getAbsolutePath());
      }
    }
    return storagePath;
  }

  void copyFrameworkConfig(Properties props, Properties frameworkConfig) {
    for(Enumeration<?> names = props.propertyNames(); names.hasMoreElements(); ) {
      String name = (String) names.nextElement();
      String value = props.getProperty(name);
      if(value != null)
        frameworkConfig.setProperty(name, value);
    }
  }

  Framework createAndRunFramework(Properties config) {
    ServiceFinder<FrameworkFactory> finder = ServiceFinder.create(FrameworkFactory.class, Main.class.getClassLoader());
    FrameworkFactory fwkFactory = finder.loadOneInstance();
    if (fwkFactory == null) {
      logger.severe("No FrameworkFactory service providers available.");
      return null;
    }

    Framework framework = fwkFactory.newFramework(config);
    logger.info("Created framework");
    try {
      framework.start();
      logger.info("Started framework");
    } catch (BundleException e) {
      logger.log(Level.SEVERE, "Error starting framework.", e);
      return null;
    }

    return framework;
  }

  Thread createInstaller(final BundleContext framework, Properties props) {
    boolean dynamic = "true".equalsIgnoreCase(props.getProperty(LauncherConstants.PROP_DYNAMIC_BUNDLES, LauncherConstants.DEFAULT_DYNAMIC_BUNDLES));
    boolean killOnError = "true".equalsIgnoreCase(props.getProperty(LauncherConstants.PROP_SHUTDOWN_ON_BUNDLE_ERROR, LauncherConstants.DEFAULT_SHUTDOWN_ON_BUNDLE_ERROR));
   
    Runnable errorCallback = null;
    if(killOnError) {
        errorCallback = new Runnable() {
                public void run() {
                    logger.severe("SHUTTING DOWN due to errors.");
                    errorOccurred = true;
                    try {
                        framework.getBundle(0).stop();
                    } catch (BundleException e) {
                        logger.log(Level.SEVERE, "Failed to shutdown OSGi Framework.", e);
                    }
                }
            };
    }

    // Start the framework and synchronize the bundles; either once or continuously
    Thread installerThread = null;
    BundleInstaller installer = new BundleInstaller(propsFile, framework, errorCallback);

    if(dynamic) {
      installerThread = new Thread(installer);
      installerThread.start();
    } else {
      installer.synchronizeBundles();
    }

    return installerThread;
  }

  /**
   * This method creates and registers an {@link Executor} service, then
   * performs work received by that executor on the calling thread, continuing
   * until the system bundle is shutdown. This method <b>will not return</b>
   * until the framework is shutting down.
   *
   * @param framework
   *            The OSGi framework
   * @param threadName
   *            The name of the calling thread; this will be used to set the
   *            {@code thread} property of the Executor service.
   */
  public void createAndRunMainThreadExecutor(BundleContext framework, String threadName) {
    // Create work queue
    final BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<Runnable>(1);

    // Register executor as a service
    Properties mainThreadExecutorProps = new Properties();
    mainThreadExecutorProps.put("thread", threadName);
    mainThreadExecutorProps.put(Constants.SERVICE_RANKING, Integer.valueOf(-1000));
    Executor mainThreadExecutor = new Executor() {
      public void execute(Runnable command) {
        logger.info("Main-thread executor enqueuing a new task");
        // add() will throw an exception if the queue is full, which is what we want
        workQueue.add(command);
      }
    };
    framework.registerService(Executor.class.getName(), mainThreadExecutor, mainThreadExecutorProps);

    // Create a bundle listener that will pull us out of the queue polling loop
    // when the system bundle starts to shutdown
    final AtomicBoolean shutdown = new AtomicBoolean(false);
    final Thread mainThread = Thread.currentThread();
    framework.addBundleListener(new SynchronousBundleListener() {
      public void bundleChanged(BundleEvent event) {
        if(event.getBundle().getBundleId() == 0 && event.getType() == BundleEvent.STOPPING) {
            logger.info("Signalling main thread to stop");
          shutdown.set(true);
          mainThread.interrupt();
        }
      }
    });

    // Enter a loop to poll on the work queue
    logger.fine("Main thread polling for work.");
    while(!shutdown.get() && framework.getBundle().getState() == Bundle.ACTIVE) {
      try {
        Runnable work = workQueue.poll(3, TimeUnit.SECONDS);
        if(work != null) {
          logger.fine("Main thread received a work task, executing.");
          work.run();
        }
      } catch (InterruptedException e) {
        logger.fine("Main thread work queue polling loop was interrupted.");
      }
    }
    // Clear the interrupted state if it was uncaught during the above loop
    Thread.interrupted();
  }

  void debug(String message) {
    if(enableDebug) System.err.println(message);
  }

  void printHelp() {
    System.out.println("java -cp org.eclipse.osgi-3.5.2.jar:bndtools.launcher.jar bndtools.launcher.Main [--debug] [launch.properties]");
  }
}
TOP

Related Classes of bndtools.launcher.Main

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.