Java tutorial
/* * Copyright (C) 2016 Keith M. Hughes * Copyright (C) 2012 Google Inc. * * 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 io.smartspaces.launcher.bootstrap; import io.smartspaces.launcher.base.SmartSpacesReturnCodes; import io.smartspaces.system.core.configuration.ConfigurationProvider; import io.smartspaces.system.core.configuration.CoreConfiguration; import io.smartspaces.system.core.container.ContainerCustomizerProvider; import io.smartspaces.system.core.container.ContainerFilesystemLayout; import io.smartspaces.system.core.container.SimpleContainerCustomizerProvider; import io.smartspaces.system.core.container.SmartSpacesStartLevel; import io.smartspaces.system.core.container.SmartSpacesSystemControl; import io.smartspaces.system.core.logging.LoggingProvider; 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; import org.osgi.framework.startlevel.BundleStartLevel; import org.osgi.framework.startlevel.FrameworkStartLevel; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.Thread.UncaughtExceptionHandler; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.jar.Attributes; import java.util.jar.Manifest; /** * The boostrapper for Smart Spaces. * * @author Keith M. Hughes */ public class SmartSpacesFrameworkBootstrap { /** * The system property name for the Felix Gogo shell arguments. */ private static final String SYSTEM_PROPERTY_NAME_GOGO_ARGS = "gosh.args"; /** * The value for the system property for a non-interactive Felix Gogo shell. */ private static final String SYSTEM_PROPERTY_VALUE_GOGO_ARGS_NOINTERACTIVE = "--nointeractive"; /** * Configuration parameter to specify if startup order of bundles should be * logged. */ public static final String CONFIG_PROPERTY_STARTUP_LOGGING = "smartspaces.logging.container.startup"; /** * Configuration parameter value to specify if startup order of bundles should * be logged. */ public static final String CONFIG_PROPERTY_VALUE_STARTUP_LOGGING = "true"; /** * The argument for saying the container should run with no shell access. */ public static final String ARGS_NOSHELL = "--noshell"; /** * Command line argument prefix for specifying a specific runtime path. This * should match the value of * {@code smartspacesLauncher.COMMAND_LINE_RUNTIME_PREFIX}, but can't be a * shared variable because of package dependency considerations. */ public static final String ARGS_PREFIX_RUNTIME = "--runtime="; /** * Command line argument prefix for specifying a specific config path. This * should match the value of * {@code smartspacesLauncher.COMMAND_LINE_CONFIG_PREFIX}, but can't be a * shared variable because of package dependency considerations. */ public static final String ARGS_PREFIX_CONFIG = "--config="; /** * Command line argument prefix for adding in extra bootstrap folders. */ public static final String ARGS_PREFIX_BOOTSTRAP = "--bootstrap="; /** * The OSGi wild card for exporting nested packages. */ public static final String NESTED_PACKAGE_WILDCARD = ".*"; /** * Comment character for delegations.conf. */ public static final String DELEGATIONS_CONF_FILE_COMMENT = "#"; /** * The comment character used in files found in meta-inf, at least for OSGi * framework factory. */ public static final char META_INF_COMMENT_CHARACTER = '#'; /** * The location of the delegations.conf file relative to the SS install. */ public static final String LOCATION_DELEGATIONS_CONF = "lib/system/jvm/delegations.conf"; /** * External packages loaded from the Smart Spaces system folder that must be * exposed for things to work. * * <p> * These packages are critical, IS is crippled without them which is why they * are here in the code. */ public static final String[] PACKAGES_SYSTEM_EXTERNAL = new String[] { "org.apache.commons.logging; version=1.1.1", "org.apache.commons.logging.impl; version=1.1.1", "javax.transaction; version=1.1.0", "javax.transaction.xa; version=1.1.0", "javax.transaction", "javax.transaction.xa" }; /** * The interface describing the container system control. */ public static final Class<SmartSpacesSystemControl> CONTAINER_SYSTEM_CONTROL_INTERFACE = SmartSpacesSystemControl.class; /** * The interface describing the container customizer provider. */ public static final Class<ContainerCustomizerProvider> CONTAINER_COSTUMIZER_PROVIDER_INTERFACE = ContainerCustomizerProvider.class; /** * The interface describing the configuration provider. */ public static final Class<ConfigurationProvider> CONFIGURATION_PROVIDER_INTERFACE = ConfigurationProvider.class; /** * The interface describing the logging provider. */ public static final Class<LoggingProvider> LOGGING_PROVIDER_INTERFACE = LoggingProvider.class; /** * Packages loaded from the Smart Spaces system folder that are part of Smart * Spaces. */ public static final String[] PACKAGES_SYSTEM_SMARTSPACES = new String[] { LOGGING_PROVIDER_INTERFACE.getPackage().getName(), CONFIGURATION_PROVIDER_INTERFACE.getPackage().getName(), CONTAINER_COSTUMIZER_PROVIDER_INTERFACE.getPackage().getName() }; /** * The Jar Manifest property that gives the Smart Spaces version. */ public static final String MANIFEST_PROPERTY_SMARTSPACES_VERSION = "Bundle-Version"; /** * The folder where Smart Spaces will cache OSGi plugins. This is relative to * the run folder. */ public static final String FOLDER_PLUGINS_CACHE = "plugins-cache"; /** * Where the OSGi framework launcher can be found. */ public static final String OSGI_FRAMEWORK_LAUNCH_FRAMEWORK_FACTORY = "META-INF/services/org.osgi.framework.launch.FrameworkFactory"; /** * Bundle manifest header indicating the start level to use. */ public static final String BUNDLE_MANIFEST_START_LEVEL_HEADER = "SmartSpaces-StartLevel"; /** * Command line argument prefix string for configuration value definitions. */ public static final String COMMAND_LINE_VALUE_DEFINITION_PREFIX = "-D"; /** * Command line configuration value key/value split specification. */ public static final String COMMAND_LINE_VALUE_DEFINITION_SPLIT = "="; /** * Environment variable that indicates the home install directory for Smart * Spaces. */ private static final String SMARTSPACES_HOME_ENVIRONMENT_KEY = "SMARTSPACES_HOME"; /** * Default directory for the home directory relative to install of this * component. */ private static final String SMARTSPACES_HOME_DEFAULT_DIR = ".."; /** * The name of the System property for the current operating system. */ private static final String SYSTEM_PROPERTY_OS_NAME = "os.name"; /** * The value of the System property value for the current operating system * when it is Linux. */ private static final String SYSTEM_PROPERTY_OS_VALUE_LINUX = "Linux"; /** * The value of the System property value for the current operating system * when it is OSX (Mac). */ private static final String SYSTEM_PROPERTY_OS_VALUE_OSX = "Mac OS X"; /** * The beginning of the System property value for the current operating system * when it is Windows. */ private static final String SYSTEM_PROPERTY_OS_PREFIX_WINDOWS = "Windows"; /** * The OSGI framework which has been started. */ private Framework framework; /** * A map of bundle symbolic names to start levels for those bundles. */ private Map<String, SmartSpacesStartLevel> bundleStartLevels = new HashMap<String, SmartSpacesStartLevel>(); /** * Extra folders to be added to the bootstrap from the commandline. */ private final List<File> extraBootstrapFolders = new ArrayList<>(); /** * All bundles installed. */ private final Set<Bundle> installedBundles = new HashSet<>(); /** * The initial set of bundles to load. */ private List<File> initialBundles; /** * Whether or not the OSGi shell is needed. */ private boolean needShell = true; /**oshell * Logging provider for the container. */ private Log4jLoggingProvider loggingProvider; /** * The configuration provider for the container. */ private FileConfigurationProvider configurationProvider; /** * The container customizer provider for the container. */ private SimpleContainerCustomizerProvider containerCustomizerProvider; /** * The base install folder for Smart Spaces. */ private File baseInstallFolder; /** * The root runtime directory for this container. May be the same as the base * install folder, but can be independently controlled to allow for multiple * runtime instances. */ private File runtimeFolder; /** * The root config directory for this container. */ private File configFolder; /** * The system control for the container. */ private SmartSpacesSystemControl systemControl; /** * The OSGi bundle context for the OSGi framework bundle. */ private BundleContext rootBundleContext; /** * The start level for the OSGi framework. */ private FrameworkStartLevel frameworkStartLevel; /** * The shutdown option for the container. * * <p> * Set to the default value in case the container is exited from the OSGi * shell. */ private int shutdownReturnCode = SmartSpacesReturnCodes.RETURN_CODE_SUCCESS; /** * Boot the framework. * * @param args * the arguments to be passed to the bootstrap * * @return the return code for the Java process, see * {@link SmartSpacesReturnCodes} */ public int boot(List<String> args) { baseInstallFolder = new File(".").getAbsoluteFile().getParentFile(); // Set default values for various directories. runtimeFolder = baseInstallFolder; configFolder = new File(baseInstallFolder, ContainerFilesystemLayout.FOLDER_DEFAULT_CONFIG); processCommandLineArgs(args); if (!needShell) { System.setProperty(SYSTEM_PROPERTY_NAME_GOGO_ARGS, SYSTEM_PROPERTY_VALUE_GOGO_ARGS_NOINTERACTIVE); } initialBundles = new ArrayList<File>(); initializeBundleStartLevels(); systemControl = new SmartSpacesSystemControl() { @Override public void softRestart() { shutdownReturnCode = SmartSpacesReturnCodes.RETURN_CODE_RESTART_SOFT; stopContainer(); } @Override public void shutdown() { shutdownReturnCode = SmartSpacesReturnCodes.RETURN_CODE_SUCCESS; stopContainer(); } @Override public void hardRestart() { shutdownReturnCode = SmartSpacesReturnCodes.RETURN_CODE_RESTART_HARD; stopContainer(); } /** * Fully stop the container. */ private void stopContainer() { try { framework.stop(); } catch (BundleException e) { e.printStackTrace(); } } }; setupExceptionHandler(); File bootstrapBundleFolder = new File(baseInstallFolder, ContainerFilesystemLayout.FOLDER_SYSTEM_BOOTSTRAP); try { bootAndRunFramework(args, bootstrapBundleFolder); return shutdownReturnCode; } catch (Throwable ex) { if (loggingProvider != null && loggingProvider.getLog() != null) { loggingProvider.getLog().error("Error starting framework", ex); } else { System.err.println("Error starting framework"); ex.printStackTrace(); } return SmartSpacesReturnCodes.RETURN_CODE_FAILURE; } } /** * Boot and run the framework. * * <p> * This method will return when the OSGi framework shuts down. * * @param args * the command line args * @param bootstrapBundleFolder * the folder that contains the bootstrap bundles * * @throws Exception * something bad happened while the framework was setting up */ private void bootAndRunFramework(List<String> args, File bootstrapBundleFolder) throws Exception { getBootstrapBundleJars(bootstrapBundleFolder); setupShutdownHandler(); loadStartupFolder(); loadExtraBootstrapFolders(); createCoreServices(args); if (initialBundles.isEmpty()) { throw new RuntimeException("No bootstrap bundles to install."); } File environmentFolder = new File(configFolder, ContainerFilesystemLayout.FOLDER_CONFIG_ENVIRONMENT); ExtensionsReader extensionsReader = new ExtensionsReader(loggingProvider.getLog()); extensionsReader.processExtensionFiles(environmentFolder); createFramework(extensionsReader); registerCoreServices(); loadLibraries(extensionsReader.getLoadLibraries()); loadClasses(extensionsReader.getLoadClasses()); addContainerPathBundles(extensionsReader.getContainerPath()); framework.start(); startBundles(); frameworkStartLevel.setStartLevel(SmartSpacesStartLevel.STARTUP_LEVEL_LAST.getStartLevel()); framework.waitForStop(0); } /** * Populate the map for bundle start levels. */ private void initializeBundleStartLevels() { // TODO(keith): Once Spring is removed, see if this is even necessary. bundleStartLevels.put("smartspaces.master.webapp", SmartSpacesStartLevel.STARTUP_LEVEL_LAST); bundleStartLevels.put("smartspaces.master", SmartSpacesStartLevel.STARTUP_LEVEL_PENULTIMATE); // TODO(keith): Get bundle dependencies so that this isn't necessary. bundleStartLevels.put("smartspaces.spacecontroller", SmartSpacesStartLevel.STARTUP_LEVEL_LAST); } /** * Process the command line arguments for this container. * * @param args * the list of command line arguments */ private void processCommandLineArgs(List<String> args) { for (String arg : args) { if (arg.equals(ARGS_NOSHELL)) { needShell = false; } else if (arg.startsWith(ARGS_PREFIX_RUNTIME)) { runtimeFolder = new File(arg.substring(ARGS_PREFIX_RUNTIME.length())); } else if (arg.startsWith(ARGS_PREFIX_CONFIG)) { configFolder = new File(arg.substring(ARGS_PREFIX_CONFIG.length())); } else if (arg.startsWith(ARGS_PREFIX_BOOTSTRAP)) { extraBootstrapFolders.add(new File(arg.substring(ARGS_PREFIX_BOOTSTRAP.length()))); } } } /** * Load the contents of the startup folder, which contains additional * resources for the container. * * @throws Exception * could not create the startup folder or load it */ private void loadStartupFolder() throws Exception { File startupFolder = new File(baseInstallFolder, ContainerFilesystemLayout.FOLDER_USER_BOOTSTRAP); if (startupFolder.exists()) { if (startupFolder.isFile()) { throw new Exception(String.format("User bootstrap folder %s is a file not a folder.", startupFolder.getAbsolutePath())); } } else if (!startupFolder.mkdirs()) { throw new Exception( String.format("Cannot create user bootstrap folder %s.", startupFolder.getAbsolutePath())); } getBootstrapBundleJars(startupFolder); } /** * Add in all extra bootstrap folders. */ private void loadExtraBootstrapFolders() { for (File extraBootstrapFolder : extraBootstrapFolders) { // Only add ones that are directories. This also means they exist. if (extraBootstrapFolder.isDirectory()) { getBootstrapBundleJars(extraBootstrapFolder); } } } /** * Set up the default exception handler. */ private void setupExceptionHandler() { Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { loggingProvider.getLog() .error(String.format("Caught previously uncaught exception from thread %s", t), e); } }); } /** * Add configuration items that can be automatically determined to the given * FileConfigurationProvider. * * @param configurationProvider * the FileConfigurationProvider to inject key, value pairs of config * data */ private void addAutomaticConfiguration(FileConfigurationProvider configurationProvider) { // Calculate the proper home directory for this install of Smart // Spaces. String isHomeEnvPath = System.getenv(SMARTSPACES_HOME_ENVIRONMENT_KEY); File isHomeDir = isHomeEnvPath != null ? new File(isHomeEnvPath) : new File(baseInstallFolder, SMARTSPACES_HOME_DEFAULT_DIR); configurationProvider.put(CoreConfiguration.CONFIGURATION_NAME_SMARTSPACES_HOME, isHomeDir.getAbsolutePath()); String platformOs = System.getProperty(SYSTEM_PROPERTY_OS_NAME); // Convert the Os string into the format Smart Spaces is expecting. if (platformOs.equals(SYSTEM_PROPERTY_OS_VALUE_LINUX)) { platformOs = CoreConfiguration.CONFIGURATION_VALUE_PLATFORM_OS_LINUX; } else if (platformOs.equals(SYSTEM_PROPERTY_OS_VALUE_OSX)) { platformOs = CoreConfiguration.CONFIGURATION_VALUE_PLATFORM_OS_OSX; } else if (platformOs.startsWith(SYSTEM_PROPERTY_OS_PREFIX_WINDOWS)) { platformOs = CoreConfiguration.CONFIGURATION_VALUE_PLATFORM_OS_WINDOWS; } else { loggingProvider.getLog() .warn(String.format( "Unsupported operating system: %s. Native applications will not function properly.", platformOs)); platformOs = CoreConfiguration.CONFIGURATION_VALUE_PLATFORM_OS_UNKNOWN; } configurationProvider.put(CoreConfiguration.CONFIGURATION_NAME_SMARTSPACES_PLATFORM_OS, platformOs); configurationProvider.put(CoreConfiguration.CONFIGURATION_NAME_PLATFORM_FILE_SEPARATOR, File.separator); } /** * Add command line configuration parameters to the given system * configuration. * * @param configurationProvider * the FileConfigurationProvider which will have the command line * configurations added * @param args * the command line arguments * * @return the command line arguments minus the configuration parameters */ private List<String> addCommandLineConfigurationParameters(FileConfigurationProvider configurationProvider, List<String> args) { List<String> newArgs = new ArrayList<String>(); for (String arg : args) { if (arg.startsWith(COMMAND_LINE_VALUE_DEFINITION_PREFIX)) { String[] parts = arg.split(COMMAND_LINE_VALUE_DEFINITION_SPLIT, 2); configurationProvider.put(parts[0].substring(COMMAND_LINE_VALUE_DEFINITION_PREFIX.length()), parts.length > 1 ? parts[1] : ""); } else { newArgs.add(arg); } } return newArgs; } /** * Create a FileConfigurationProvider, assign it to configurationProvider, * inject automatic configuration, and then load configuration from config * files. * * @param args * the command line arguments * * @return the command line arguments minus configuration parameters */ private List<String> initializeFileConfigurationProvider(List<String> args) { configurationProvider = new FileConfigurationProvider(baseInstallFolder, configFolder, loggingProvider.getLog()); // The order is important, as it determines which source of // configuration // parameters overrides the other sources. // Configuration sources that are invoked later override those that are // done // sooner. The order is: // 1) automatic configuration // 2) file configuration // 3) command-line configuration addAutomaticConfiguration(configurationProvider); configurationProvider.load(); args = addCommandLineConfigurationParameters(configurationProvider, args); return args; } /** * Create the core services to the base bundle which are platform dependent. * * @param args * the list of command line arguments */ private void createCoreServices(List<String> args) { loggingProvider = new Log4jLoggingProvider(); loggingProvider.configure(runtimeFolder, configFolder); args = initializeFileConfigurationProvider(args); containerCustomizerProvider = new SimpleContainerCustomizerProvider(args, true); } /** * Register all bootstrap core services with the container. */ public void registerCoreServices() { rootBundleContext.registerService(LOGGING_PROVIDER_INTERFACE.getName(), loggingProvider, null); rootBundleContext.registerService(CONFIGURATION_PROVIDER_INTERFACE.getName(), configurationProvider, null); rootBundleContext.registerService(CONTAINER_COSTUMIZER_PROVIDER_INTERFACE.getName(), containerCustomizerProvider, null); rootBundleContext.registerService(CONTAINER_SYSTEM_CONTROL_INTERFACE, systemControl, null); } /** * Start all bundles. * * @throws BundleException * something happened while starting bundles that could not be * recovered from */ private void startBundles() throws BundleException { for (File bundleFile : initialBundles) { String bundleUri = bundleFile.getAbsoluteFile().toURI().toString(); try { Bundle bundle = rootBundleContext.installBundle(bundleUri); String symbolicName = bundle.getSymbolicName(); if (symbolicName != null) { SmartSpacesStartLevel startLevel = bundleStartLevels.get(symbolicName); if (startLevel == null) { String smartspacesStartLevel = bundle.getHeaders().get(BUNDLE_MANIFEST_START_LEVEL_HEADER); if (smartspacesStartLevel != null) { startLevel = SmartSpacesStartLevel.valueOf(smartspacesStartLevel); } else { startLevel = SmartSpacesStartLevel.STARTUP_LEVEL_DEFAULT; } } if (startLevel != SmartSpacesStartLevel.STARTUP_LEVEL_DEFAULT) { bundle.adapt(BundleStartLevel.class).setStartLevel(startLevel.getStartLevel()); } installedBundles.add(bundle); } else { logBadBundle(bundleUri, new Exception("No symbolic name in bundle")); } } catch (Exception e) { logBadBundle(bundleUri, e); } } // Start all installed non-fragment bundles. for (Bundle bundle : installedBundles) { if (isFragment(bundle)) { continue; } startBundle(bundle); } } /** * Log that we had a bad bundle. * * @param bundleUri * URI for the bundle * @param e * triggering exception */ private void logBadBundle(String bundleUri, Exception e) { loggingProvider.getLog().error( String.format("Bundle %s is not an OSGi bundle, skipping during Smart Spaces startup", bundleUri), e); } /** * Start a particular bundle. * * @param bundle * the bundle to start */ private void startBundle(Bundle bundle) { try { bundle.start(); } catch (Exception e) { loggingProvider.getLog().error(String.format("Error while starting bundle %s", bundle.getLocation()), e); } } /** * Create, configure, and start the OSGi framework instance. * * @param extensionsReader * the reader for extensions files * * @throws Exception * unable to create and/or start the framework */ private void createFramework(ExtensionsReader extensionsReader) throws Exception { Map<String, String> frameworkConfig = new HashMap<String, String>(); frameworkConfig.put(Constants.FRAMEWORK_STORAGE_CLEAN, Constants.FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT); // The bootloader delegation loads all classes in the java install. This // covers things like the javax classes which // are not automatically exposed through the OSGi bundle classloaders. List<String> bootDelegationPackages = new ArrayList<String>(); // Extra system packages are not found in the boot classloader but need // to // be exported by the system bundle // classloader. List<String> extraSystemPackages = new ArrayList<String>(); bootDelegationPackages.addAll(extensionsReader.getBootPackages()); processDelegationsFile(bootDelegationPackages, extraSystemPackages); configureBootDelegationPackages(frameworkConfig, bootDelegationPackages); // Get the initial packages into the extra system packages. Collections.addAll(extraSystemPackages, PACKAGES_SYSTEM_EXTERNAL); Collections.addAll(extraSystemPackages, PACKAGES_SYSTEM_SMARTSPACES); extraSystemPackages.addAll(extensionsReader.getPackages()); configureExtraSystemPackages(frameworkConfig, extraSystemPackages); frameworkConfig.put(CoreConfiguration.CONFIGURATION_NAME_SMARTSPACES_BASE_INSTALL_DIR, baseInstallFolder.getAbsolutePath()); frameworkConfig.put(CoreConfiguration.CONFIGURATION_NAME_SMARTSPACES_RUNTIME_DIR, runtimeFolder.getAbsolutePath()); frameworkConfig.put(CoreConfiguration.CONFIGURATION_NAME_SMARTSPACES_VERSION, getSmartSpacesVersion()); frameworkConfig.putAll(configurationProvider.getInitialConfiguration()); File pluginsCacheFolder = new File( new File(runtimeFolder, ContainerFilesystemLayout.FOLDER_SMARTSPACES_RUN), FOLDER_PLUGINS_CACHE); frameworkConfig.put(Constants.FRAMEWORK_STORAGE, pluginsCacheFolder.getCanonicalPath()); framework = getFrameworkFactory().newFramework(frameworkConfig); frameworkStartLevel = framework.adapt(FrameworkStartLevel.class); framework.init(); rootBundleContext = framework.getBundleContext(); if (CONFIG_PROPERTY_VALUE_STARTUP_LOGGING.equals(frameworkConfig.get(CONFIG_PROPERTY_STARTUP_LOGGING))) { rootBundleContext.addBundleListener(new SynchronousBundleListener() { @Override public void bundleChanged(BundleEvent event) { try { if (event.getType() == BundleEvent.STARTED) { Bundle bundle = event.getBundle(); loggingProvider.getLog() .info(String.format("Bundle %s:%s started with start level %d", bundle.getSymbolicName(), bundle.getVersion(), bundle.adapt(BundleStartLevel.class).getStartLevel())); } } catch (Exception e) { loggingProvider.getLog().error("Exception while responding to bundle change events", e); } } }); } } /** * Configure the extra system packages for the framework. * * @param frameworkConfig * the framework configuration * @param extraPackages * the list of packages to be used as extra system packages */ private void configureExtraSystemPackages(Map<String, String> frameworkConfig, List<String> extraPackages) { StringBuilder packages = new StringBuilder(); String separator = ""; for (String extraPackage : extraPackages) { packages.append(separator).append(extraPackage); separator = ", "; } loggingProvider.getLog().debug(String.format("Extra packages: %s", packages)); frameworkConfig.put(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA, packages.toString()); } /** * Configure the extra system packages for the framework. * * @param frameworkConfig * the framework configuration * @param bootPackages * the list of packages to be used as bootloader packages */ private void configureBootDelegationPackages(Map<String, String> frameworkConfig, List<String> bootPackages) { if (bootPackages.isEmpty()) { return; } StringBuilder packages = new StringBuilder(); String separator = ""; for (String bootPackage : bootPackages) { packages.append(separator).append(bootPackage); if (!bootPackage.endsWith(NESTED_PACKAGE_WILDCARD)) { packages.append(NESTED_PACKAGE_WILDCARD); } separator = ", "; } loggingProvider.getLog().debug(String.format("Boot delegations: %s", packages)); frameworkConfig.put(Constants.FRAMEWORK_BOOTDELEGATION, packages.toString()); } /** * Load a collection of libraries. * * @param libraries * the libraries to load */ private void loadLibraries(List<String> libraries) { for (String library : libraries) { loggingProvider.getLog().debug(String.format("Loading system library %s", library)); System.loadLibrary(library); } } /** * Load a collection of classes. * * @param classes * the classes to load */ private void loadClasses(List<String> classes) { for (String className : classes) { loggingProvider.getLog().debug(String.format("Loading class %s", className)); try { Class<?> clazz = SmartSpacesFrameworkBootstrap.class.getClassLoader().loadClass(className); Object obj = clazz.newInstance(); rootBundleContext.registerService(obj.getClass().getName(), obj, null); } catch (Exception e) { loggingProvider.getLog().error(String.format("Error while creating class %s", className), e); } } } /** * Add in all container path entries from the extensions files to the initial * bundles list as long as the files actually exist. * * @param containerPath * the elements to be on the container classpath. */ private void addContainerPathBundles(List<String> containerPath) { for (String containerBundlePath : containerPath) { File bundleFile = new File(containerBundlePath); if (bundleFile.isFile()) { initialBundles.add(bundleFile); } else { loggingProvider.getLog() .warn(String.format("Container path file %s is not a file", containerBundlePath)); } } } /** * Set up a shutdown hook which will stop the framework when the VM shuts * down. */ private void setupShutdownHandler() { Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { try { if (framework != null) { systemControl.shutdown(); } } catch (Exception ex) { loggingProvider.getLog().error("Error stopping framework", ex); } } }); } /** * Get all jars from the bootstrap folder. * * @param folder * the folder containing the bootstrap bundles */ private void getBootstrapBundleJars(File folder) { // Look in the specified bundle directory to create a list // of all JAR files to install. File[] files = folder.listFiles(new FilenameFilter() { @Override public boolean accept(File dir, String name) { String filename = name.toLowerCase(); return filename.endsWith(".jar") || filename.endsWith(".war"); } }); for (File f : files) { initialBundles.add(f.getAbsoluteFile()); } } /** * Is the bundle a fragment host? * * @param bundle * the bundle to check * * @return {@code true} if the bundle is a fragment host */ private boolean isFragment(Bundle bundle) { return bundle.getHeaders().get(Constants.FRAGMENT_HOST) != null; } /** * Simple method to parse META-INF/services file for framework factory. * Currently, it assumes the first non-commented line is the class nodeName of * the framework factory implementation. * * @return the created <tt>FrameworkFactory</tt> instance * * @throws Exception * if any errors occur. **/ private FrameworkFactory getFrameworkFactory() throws Exception { // using the ServiceLoader to get a factory. ClassLoader classLoader = SmartSpacesFrameworkBootstrap.class.getClassLoader(); URL url = classLoader.getResource(OSGI_FRAMEWORK_LAUNCH_FRAMEWORK_FACTORY); if (url != null) { BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream())); try { for (String s = br.readLine(); s != null; s = br.readLine()) { // Try to load first non-empty, non-commented line. s = s.trim(); if (!s.isEmpty() && s.charAt(0) != META_INF_COMMENT_CHARACTER) { return (FrameworkFactory) classLoader.loadClass(s).newInstance(); } } } finally { if (br != null) { br.close(); } } } throw new Exception("Could not find framework factory."); } /** * Get the list of boot delegation packages and extra system packages from the * delegations file. * * <p> * The bootloader loads all classes in the java install. This covers things * like the javax classes which are not automatically exposed through the OSGi * bundle classloaders. * * @param bootDelegationPackages * packages to be exported in the boot delegation * @param extraSystemPackages * packages to be exported in the exta system classes */ private void processDelegationsFile(List<String> bootDelegationPackages, List<String> extraSystemPackages) { File delegationFile = new File(baseInstallFolder, LOCATION_DELEGATIONS_CONF); if (!delegationFile.exists()) { return; } BufferedReader reader = null; try { reader = new BufferedReader(new FileReader(delegationFile)); String line; while ((line = reader.readLine()) != null) { line = line.trim(); if (!line.isEmpty()) { if (line.startsWith(DELEGATIONS_CONF_FILE_COMMENT)) { continue; } else if (line.startsWith(ExtensionsReader.EXTENSION_FILE_KEYWORD_PACKAGE)) { extraSystemPackages.add( line.substring(ExtensionsReader.EXTENSION_FILE_KEYWORD_PACKAGE.length()).trim()); } else if (line.startsWith(ExtensionsReader.EXTENSION_FILE_KEYWORD_PACKAGE_BOOT)) { bootDelegationPackages.add(line .substring(ExtensionsReader.EXTENSION_FILE_KEYWORD_PACKAGE_BOOT.length()).trim()); } else { // The default is to be a boot delegation package bootDelegationPackages.add(line); } } } } catch (Exception e) { loggingProvider.getLog().error( String.format("Error file processing delegations file %s", delegationFile.getAbsolutePath()), e); } finally { if (reader != null) { try { reader.close(); } catch (IOException e) { // Don't care. Closing. } } } } /** * Get the Smart Spaces version from the JAR manifest. * * @return The smart spaces version */ private String getSmartSpacesVersion() { // This little lovely line gives us the name of the jar that gave the // class // we are looking at. String classContainer = getClass().getProtectionDomain().getCodeSource().getLocation().toString(); InputStream in = null; try { URL manifestUrl = new URL("jar:" + classContainer + "!/META-INF/MANIFEST.MF"); in = manifestUrl.openStream(); Manifest manifest = new Manifest(in); Attributes attributes = manifest.getMainAttributes(); return attributes.getValue(MANIFEST_PROPERTY_SMARTSPACES_VERSION); } catch (IOException ex) { return null; } finally { if (in != null) { try { in.close(); } catch (IOException e) { // Don't care } } } } }