taverna3-osgi-launher as taverna-osgi/
Project: http://git-wip-us.apache.org/repos/asf/incubator-taverna-osgi/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-taverna-osgi/commit/b04134c8 Tree: http://git-wip-us.apache.org/repos/asf/incubator-taverna-osgi/tree/b04134c8 Diff: http://git-wip-us.apache.org/repos/asf/incubator-taverna-osgi/diff/b04134c8 Branch: refs/heads/master Commit: b04134c824858f41e1f58e45499d1fd4f002ff35 Parents: 07ea44d Author: Stian Soiland-Reyes <soiland-re...@cs.manchester.ac.uk> Authored: Thu Dec 18 22:20:14 2014 -0600 Committer: Stian Soiland-Reyes <soiland-re...@cs.manchester.ac.uk> Committed: Thu Dec 18 22:20:14 2014 -0600 ---------------------------------------------------------------------- .gitignore | 24 - .project | 23 - .travis.yml | 1 - README.md | 3 - pom.xml | 37 -- .../java/uk/org/taverna/osgi/OsgiLauncher.java | 434 ------------------- taverna-osgi/pom.xml | 37 ++ .../java/uk/org/taverna/osgi/OsgiLauncher.java | 434 +++++++++++++++++++ 8 files changed, 471 insertions(+), 522 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-taverna-osgi/blob/b04134c8/.gitignore ---------------------------------------------------------------------- diff --git a/.gitignore b/.gitignore deleted file mode 100644 index ebe0e5b..0000000 --- a/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -# ignore project files # -.classpath -.project -.settings/ -catalog-v001.xml - -# ignore target files # -target/ -bin/ -build/ -dist/ -apidoc/ -*.swp - -# ignore svn files if there -.svn - -# ignore log files # -*.log -/logs/* -*/logs/* - - - http://git-wip-us.apache.org/repos/asf/incubator-taverna-osgi/blob/b04134c8/.project ---------------------------------------------------------------------- diff --git a/.project b/.project deleted file mode 100644 index a7d3468..0000000 --- a/.project +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<projectDescription> - <name>osgi-launcher</name> - <comment></comment> - <projects> - </projects> - <buildSpec> - <buildCommand> - <name>org.eclipse.jdt.core.javabuilder</name> - <arguments> - </arguments> - </buildCommand> - <buildCommand> - <name>org.eclipse.m2e.core.maven2Builder</name> - <arguments> - </arguments> - </buildCommand> - </buildSpec> - <natures> - <nature>org.eclipse.jdt.core.javanature</nature> - <nature>org.eclipse.m2e.core.maven2Nature</nature> - </natures> -</projectDescription> http://git-wip-us.apache.org/repos/asf/incubator-taverna-osgi/blob/b04134c8/.travis.yml ---------------------------------------------------------------------- diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index dff5f3a..0000000 --- a/.travis.yml +++ /dev/null @@ -1 +0,0 @@ -language: java http://git-wip-us.apache.org/repos/asf/incubator-taverna-osgi/blob/b04134c8/README.md ---------------------------------------------------------------------- diff --git a/README.md b/README.md deleted file mode 100644 index f93284f..0000000 --- a/README.md +++ /dev/null @@ -1,3 +0,0 @@ -OSGi Framework Launcher - -This code was previously hosted at http://taverna.googlecode.com/svn/osgi-launcher/ \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-taverna-osgi/blob/b04134c8/pom.xml ---------------------------------------------------------------------- diff --git a/pom.xml b/pom.xml deleted file mode 100644 index 0868c28..0000000 --- a/pom.xml +++ /dev/null @@ -1,37 +0,0 @@ -<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - <modelVersion>4.0.0</modelVersion> - <parent> - <groupId>net.sf.taverna</groupId> - <artifactId>taverna-parent</artifactId> - <version>3.0.1-SNAPSHOT</version> - </parent> - <groupId>uk.org.taverna.osgi</groupId> - <artifactId>osgi-launcher</artifactId> - <version>0.1.1-SNAPSHOT</version> - <name>OSGi Framework Launcher</name> - <description>Launches an OSGi framework and handles loading and starting of OSGi bundles and Spring DM managed services</description> - <dependencies> - <dependency> - <groupId>org.osgi</groupId> - <artifactId>org.osgi.core</artifactId> - <version>${osgi.core.version}</version> - </dependency> - </dependencies> - <repositories> - <repository> - <id>mygrid-repository</id> - <name>myGrid Repository</name> - <url>http://www.mygrid.org.uk/maven/repository</url> - </repository> - <repository> - <id>mygrid-snapshot-repository</id> - <name>myGrid Snapshot Repository</name> - <url>http://www.mygrid.org.uk/maven/snapshot-repository</url> - <releases> - <enabled>false</enabled> - </releases> - <snapshots /> - </repository> - </repositories> -</project> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-taverna-osgi/blob/b04134c8/src/main/java/uk/org/taverna/osgi/OsgiLauncher.java ---------------------------------------------------------------------- diff --git a/src/main/java/uk/org/taverna/osgi/OsgiLauncher.java b/src/main/java/uk/org/taverna/osgi/OsgiLauncher.java deleted file mode 100644 index cfd006b..0000000 --- a/src/main/java/uk/org/taverna/osgi/OsgiLauncher.java +++ /dev/null @@ -1,434 +0,0 @@ -/******************************************************************************* - * Copyright (C) 2012 The University of Manchester - * - * Modifications to the initial code base are copyright of their - * respective authors, or their employers as appropriate. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation; either version 2.1 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - ******************************************************************************/ -package uk.org.taverna.osgi; - -import java.io.File; -import java.io.FilenameFilter; -import java.net.URI; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Dictionary; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.ServiceLoader; -import java.util.Set; -import java.util.logging.Logger; - -import org.osgi.framework.Bundle; -import org.osgi.framework.BundleContext; -import org.osgi.framework.BundleException; -import org.osgi.framework.Constants; -import org.osgi.framework.ServiceEvent; -import org.osgi.framework.ServiceListener; -import org.osgi.framework.ServiceReference; -import org.osgi.framework.launch.Framework; -import org.osgi.framework.launch.FrameworkFactory; - -/** - * OSGi Framework launcher. - * - * Handles loading and starting of OSGi bundles and Spring DM managed services. - * - * An implementation of an OSGi Service Platform Release 4.1 (or higher) must be available on the - * classpath. - * - * @author David Withers - */ -public class OsgiLauncher { - - /** - * Default boot delegation packages. - */ - public static final String DEFAULT_BOOT_DELEGATION_PACKAGES = "sun.*,com.sun.*,java.*"; - - /** - * Default system packages. - */ - public static final String DEFAULT_SYSTEM_PACKAGES = "com.sun.org.apache.xml.internal.utils"; - - /** - * Default time to wait for services to start up. - */ - private static final long serviceLoadTimeoutSeconds = 30; - - private static final Logger logger = Logger.getLogger(OsgiLauncher.class.getName()); - - private Framework framework; - private BundleContext context; - - private Map<String, String> frameworkConfiguration = new HashMap<String, String>(); - private List<URI> bundlesToInstall = new ArrayList<URI>(); - private List<Bundle> installedBundles = new ArrayList<Bundle>(); - private Set<String> startedSpringContexts = new HashSet<String>(); - private Bundle springOsgiExtender; - - private OsgiLauncher(File storageDirectory) { - setStorageDirectory(storageDirectory); - setCleanStorageDirectory(true); - setBootDelegationPackages(DEFAULT_BOOT_DELEGATION_PACKAGES); - setSystemPackages(DEFAULT_SYSTEM_PACKAGES); - } - - /** - * Constructs an <code>OsgiLauncher</code> that loads bundles from a directory. - * - * Any file in the specified directory with a .jar extension will be loaded when the framework - * is started. - * - * @param storageDirectory - * persistent storage area used by the framework - * @param storageDirectory - * the directory containing bundles to load - */ - public OsgiLauncher(File storageDirectory, File bundleDirectory) { - this(storageDirectory); - List<File> jars = Arrays.asList(bundleDirectory.listFiles(new FilenameFilter() { - public boolean accept(File dir, String name) { - return name.endsWith(".jar"); - } - })); - for (File jar : jars) { - bundlesToInstall.add(jar.toURI()); - } - } - - /** - * Constructs an <code>OsgiLauncher</code> that loads the specified bundles. - * - * @param storageDirectory - * persistent storage area used by the framework - * @param bundleURIs - * bundles to load - */ - public OsgiLauncher(File storageDirectory, List<URI> bundleURIs) { - this(storageDirectory); - for (URI bundleURI : bundleURIs) { - bundlesToInstall.add(bundleURI); - } - } - - /** - * Starts the OSGi framework, installs and starts the bundles. - * - * @throws BundleException - * if the framework could not be started - */ - public void start() throws BundleException { - logger.info("Loading the OSGi Framework Factory"); - FrameworkFactory frameworkFactory = ServiceLoader.load(FrameworkFactory.class).iterator() - .next(); - - logger.info("Creating the OSGi Framework"); - framework = frameworkFactory.newFramework(frameworkConfiguration); - logger.info("Starting the OSGi Framework"); - framework.start(); - - context = framework.getBundleContext(); - context.addServiceListener(new ServiceListener() { - public void serviceChanged(ServiceEvent event) { - ServiceReference serviceReference = event.getServiceReference(); - if (event.getType() == ServiceEvent.REGISTERED) { - Object property = serviceReference - .getProperty("org.springframework.context.service.name"); - if (property != null) { - addStartedSpringContext(property.toString()); - } - } - logger.fine((event.getType() == ServiceEvent.REGISTERED ? "Registering : " - : "Unregistering : ") + serviceReference); - } - }); - - installedBundles = installBundles(bundlesToInstall); - - List<Bundle> bundlesToStart = new ArrayList<Bundle>(); - for (Bundle bundle : installedBundles) { - if ("org.springframework.osgi.extender".equals(bundle.getSymbolicName())) { - springOsgiExtender = bundle; - } else { - bundlesToStart.add(bundle); - } - } - startBundles(bundlesToStart); - } - - /** - * Starts SpringDM managed services. - * - * @param waitForServices - * if true waits for services to start before returning - * @throws BundleException - * if the framework has not been started or a service could not be started - */ - public void startServices(boolean waitForServices) throws BundleException { - if (framework == null || framework.getState() != Bundle.ACTIVE) { - throw new BundleException("Framework not started"); - } - if (springOsgiExtender != null) { - logger.info("Starting Spring OSGi Extender"); - springOsgiExtender.start(); - if (waitForServices) { - logger.info("Waiting for spring contexts to be started"); - for (Bundle bundle : installedBundles) { - if (bundle.getState() == Bundle.ACTIVE) { - if (hasSpringContext(bundle)) { - logger.fine("Waiting for " + bundle.getSymbolicName()); - waitForSpringContext(context, bundle.getSymbolicName(), serviceLoadTimeoutSeconds); - } - } - } - } - } - } - - /** - * Stops the OSGI framework. - * - * @throws BundleException - * if the framework has not been started - * @throws InterruptedException - * if the thread is interrupted while the framework is stopping - */ - public void stop() throws BundleException, InterruptedException { - if (framework == null || framework.getState() != Bundle.ACTIVE) { - throw new BundleException("Framework not started"); - } - framework.stop(); - framework.waitForStop(0); - context = null; - } - - /** - * Installs the bundles specified by the URIs into the framework. - * - * @param bundlesURIs - * the URIs of the bundles to install - * @return the installed bundles - * @throws BundleException - * if a bundle could not be installed - */ - public List<Bundle> installBundles(List<URI> bundlesURIs) throws BundleException { - List<Bundle> installedBundles = new ArrayList<Bundle>(); - logger.info("Installing bundles into the OSGi Framework"); - for (URI bundleURI : bundlesURIs) { - installedBundles.add(installBundle(bundleURI)); - } - return installedBundles; - } - - /** - * Installs the bundle specified by the URI into the framework. - * - * @param bundleURI - * the URI of the bundle to install - * @return the installed bundle - * @throws BundleException - * if the bundle could not be installed - */ - public Bundle installBundle(URI bundleURI) throws BundleException { - logger.fine("Installing bundle " + bundleURI); - return context.installBundle(bundleURI.toASCIIString()); - } - - /** - * Starts the bundles. - * - * If a bundle is a fragment bundle that bundle is not started. - * - * @param bundles - * the bundles to start - * @throws BundleException - * if a bundle could not be started - */ - public void startBundles(List<Bundle> bundles) throws BundleException { - logger.info("Starting bundles in the OSGi Framework"); - for (Bundle bundle : bundles) { - startBundle(bundle); - } - } - - /** - * Starts the bundle. - * - * If the bundle is a fragment bundle the bundle is not started. - * - * @param bundle - * the bundle to start - * @throws BundleException - * if the bundle could not be started - */ - public void startBundle(Bundle bundle) throws BundleException { - if (bundle.getHeaders().get(Constants.FRAGMENT_HOST) == null) { - logger.fine("Starting bundle " + bundle); - bundle.start(); - } - } - - /** - * Returns the context. Returns <code>null</code> if the framework is not started. - * - * @return the context - */ - public BundleContext getContext() { - return context; - } - - /** - * Sets the configuration to use when creating the OSGi framework. - * - * @param frameworkConfiguration the configuration to use when creating the OSGi framework - */ - public void setFrameworkConfiguration(Map<String, String> frameworkConfiguration) { - this.frameworkConfiguration = frameworkConfiguration; - } - - /** - * Adds boot delegation packages. - * - * Multiple packages must be separated by a ','. - * - * @param additionalBootDelegationPackages - * boot delegation packages to add - */ - public void addBootDelegationPackages(String additionalBootDelegationPackages) { - String bootDelegationPackages = frameworkConfiguration - .get(Constants.FRAMEWORK_BOOTDELEGATION); - if (bootDelegationPackages == null || bootDelegationPackages.isEmpty()) { - bootDelegationPackages = additionalBootDelegationPackages; - } else { - bootDelegationPackages = bootDelegationPackages + "," - + additionalBootDelegationPackages; - } - frameworkConfiguration.put(Constants.FRAMEWORK_BOOTDELEGATION, bootDelegationPackages); - } - - /** - * Sets the boot delegation packages. - * - * Multiple packages must be separated by a ','. - * - * @param bootDelegationPackages - * the boot delegation packages - */ - public void setBootDelegationPackages(String bootDelegationPackages) { - frameworkConfiguration.put(Constants.FRAMEWORK_BOOTDELEGATION, bootDelegationPackages); - } - - /** - * Adds system packages. - * - * Multiple packages must be separated by a ','. - * - * @param additionalSystemPackages - * system packages to add - */ - public void addSystemPackages(String additionalSystemPackages) { - String systemPackages = frameworkConfiguration - .get(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA); - if (systemPackages == null || systemPackages.isEmpty()) { - systemPackages = additionalSystemPackages; - } else { - systemPackages = systemPackages + "," + additionalSystemPackages; - } - frameworkConfiguration.put(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA, systemPackages); - } - - /** - * Sets the system packages. - * - * Multiple packages must be separated by a ','. - * - * @param systemPackages - * the system packages - */ - public void setSystemPackages(String systemPackages) { - frameworkConfiguration.put(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA, systemPackages); - } - - /** - * Sets the persistent storage area used by the framework. - * - * @param storageDirectory the persistent storage area used by the framework - */ - public void setStorageDirectory(File storageDirectory) { - frameworkConfiguration.put(Constants.FRAMEWORK_STORAGE, storageDirectory.getAbsolutePath()); - } - - /** - * Set whether the storage directory should be cleaned on startup. - * - * @param cleanStorageDirectory - * whether the storage directory should be cleaned on startup - */ - public void setCleanStorageDirectory(boolean cleanStorageDirectory) { - if (cleanStorageDirectory) { - frameworkConfiguration.put(Constants.FRAMEWORK_STORAGE_CLEAN, - Constants.FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT); - } else { - frameworkConfiguration.remove(Constants.FRAMEWORK_STORAGE_CLEAN); - } - } - - /** - * Returns true if a bundle contains spring context files. - * - * @param bundle - * the bundle to check - * @return true if a bundle contains spring context files - */ - private boolean hasSpringContext(Bundle bundle) { - String springFilesLocation = "META-INF/spring"; - // check for custom spring files location - @SuppressWarnings("rawtypes") - Dictionary headers = bundle.getHeaders(); - if (headers != null) { - Object header = headers.get("Spring-Context"); - if (header != null) { - springFilesLocation = header.toString().trim(); - } - } - @SuppressWarnings("rawtypes") - Enumeration springFiles = bundle.findEntries(springFilesLocation, "*.xml", false); - return springFiles != null && springFiles.hasMoreElements(); - } - - private synchronized void waitForSpringContext(BundleContext context, String springContext, - long timeoutSeconds) { - long timeLeftToWait = timeoutSeconds * 1000; - long startTime = System.currentTimeMillis(); - - while (!startedSpringContexts.contains(springContext) && timeLeftToWait > 0) { - try { - wait(timeLeftToWait); - } catch (InterruptedException e) {} - timeLeftToWait = timeLeftToWait - (System.currentTimeMillis() - startTime); - } - } - - private synchronized void addStartedSpringContext(String springContext) { - startedSpringContexts.add(springContext); - notifyAll(); - } - -} http://git-wip-us.apache.org/repos/asf/incubator-taverna-osgi/blob/b04134c8/taverna-osgi/pom.xml ---------------------------------------------------------------------- diff --git a/taverna-osgi/pom.xml b/taverna-osgi/pom.xml new file mode 100644 index 0000000..0868c28 --- /dev/null +++ b/taverna-osgi/pom.xml @@ -0,0 +1,37 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>net.sf.taverna</groupId> + <artifactId>taverna-parent</artifactId> + <version>3.0.1-SNAPSHOT</version> + </parent> + <groupId>uk.org.taverna.osgi</groupId> + <artifactId>osgi-launcher</artifactId> + <version>0.1.1-SNAPSHOT</version> + <name>OSGi Framework Launcher</name> + <description>Launches an OSGi framework and handles loading and starting of OSGi bundles and Spring DM managed services</description> + <dependencies> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.core</artifactId> + <version>${osgi.core.version}</version> + </dependency> + </dependencies> + <repositories> + <repository> + <id>mygrid-repository</id> + <name>myGrid Repository</name> + <url>http://www.mygrid.org.uk/maven/repository</url> + </repository> + <repository> + <id>mygrid-snapshot-repository</id> + <name>myGrid Snapshot Repository</name> + <url>http://www.mygrid.org.uk/maven/snapshot-repository</url> + <releases> + <enabled>false</enabled> + </releases> + <snapshots /> + </repository> + </repositories> +</project> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-taverna-osgi/blob/b04134c8/taverna-osgi/src/main/java/uk/org/taverna/osgi/OsgiLauncher.java ---------------------------------------------------------------------- diff --git a/taverna-osgi/src/main/java/uk/org/taverna/osgi/OsgiLauncher.java b/taverna-osgi/src/main/java/uk/org/taverna/osgi/OsgiLauncher.java new file mode 100644 index 0000000..cfd006b --- /dev/null +++ b/taverna-osgi/src/main/java/uk/org/taverna/osgi/OsgiLauncher.java @@ -0,0 +1,434 @@ +/******************************************************************************* + * Copyright (C) 2012 The University of Manchester + * + * Modifications to the initial code base are copyright of their + * respective authors, or their employers as appropriate. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + ******************************************************************************/ +package uk.org.taverna.osgi; + +import java.io.File; +import java.io.FilenameFilter; +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Dictionary; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.ServiceLoader; +import java.util.Set; +import java.util.logging.Logger; + +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleException; +import org.osgi.framework.Constants; +import org.osgi.framework.ServiceEvent; +import org.osgi.framework.ServiceListener; +import org.osgi.framework.ServiceReference; +import org.osgi.framework.launch.Framework; +import org.osgi.framework.launch.FrameworkFactory; + +/** + * OSGi Framework launcher. + * + * Handles loading and starting of OSGi bundles and Spring DM managed services. + * + * An implementation of an OSGi Service Platform Release 4.1 (or higher) must be available on the + * classpath. + * + * @author David Withers + */ +public class OsgiLauncher { + + /** + * Default boot delegation packages. + */ + public static final String DEFAULT_BOOT_DELEGATION_PACKAGES = "sun.*,com.sun.*,java.*"; + + /** + * Default system packages. + */ + public static final String DEFAULT_SYSTEM_PACKAGES = "com.sun.org.apache.xml.internal.utils"; + + /** + * Default time to wait for services to start up. + */ + private static final long serviceLoadTimeoutSeconds = 30; + + private static final Logger logger = Logger.getLogger(OsgiLauncher.class.getName()); + + private Framework framework; + private BundleContext context; + + private Map<String, String> frameworkConfiguration = new HashMap<String, String>(); + private List<URI> bundlesToInstall = new ArrayList<URI>(); + private List<Bundle> installedBundles = new ArrayList<Bundle>(); + private Set<String> startedSpringContexts = new HashSet<String>(); + private Bundle springOsgiExtender; + + private OsgiLauncher(File storageDirectory) { + setStorageDirectory(storageDirectory); + setCleanStorageDirectory(true); + setBootDelegationPackages(DEFAULT_BOOT_DELEGATION_PACKAGES); + setSystemPackages(DEFAULT_SYSTEM_PACKAGES); + } + + /** + * Constructs an <code>OsgiLauncher</code> that loads bundles from a directory. + * + * Any file in the specified directory with a .jar extension will be loaded when the framework + * is started. + * + * @param storageDirectory + * persistent storage area used by the framework + * @param storageDirectory + * the directory containing bundles to load + */ + public OsgiLauncher(File storageDirectory, File bundleDirectory) { + this(storageDirectory); + List<File> jars = Arrays.asList(bundleDirectory.listFiles(new FilenameFilter() { + public boolean accept(File dir, String name) { + return name.endsWith(".jar"); + } + })); + for (File jar : jars) { + bundlesToInstall.add(jar.toURI()); + } + } + + /** + * Constructs an <code>OsgiLauncher</code> that loads the specified bundles. + * + * @param storageDirectory + * persistent storage area used by the framework + * @param bundleURIs + * bundles to load + */ + public OsgiLauncher(File storageDirectory, List<URI> bundleURIs) { + this(storageDirectory); + for (URI bundleURI : bundleURIs) { + bundlesToInstall.add(bundleURI); + } + } + + /** + * Starts the OSGi framework, installs and starts the bundles. + * + * @throws BundleException + * if the framework could not be started + */ + public void start() throws BundleException { + logger.info("Loading the OSGi Framework Factory"); + FrameworkFactory frameworkFactory = ServiceLoader.load(FrameworkFactory.class).iterator() + .next(); + + logger.info("Creating the OSGi Framework"); + framework = frameworkFactory.newFramework(frameworkConfiguration); + logger.info("Starting the OSGi Framework"); + framework.start(); + + context = framework.getBundleContext(); + context.addServiceListener(new ServiceListener() { + public void serviceChanged(ServiceEvent event) { + ServiceReference serviceReference = event.getServiceReference(); + if (event.getType() == ServiceEvent.REGISTERED) { + Object property = serviceReference + .getProperty("org.springframework.context.service.name"); + if (property != null) { + addStartedSpringContext(property.toString()); + } + } + logger.fine((event.getType() == ServiceEvent.REGISTERED ? "Registering : " + : "Unregistering : ") + serviceReference); + } + }); + + installedBundles = installBundles(bundlesToInstall); + + List<Bundle> bundlesToStart = new ArrayList<Bundle>(); + for (Bundle bundle : installedBundles) { + if ("org.springframework.osgi.extender".equals(bundle.getSymbolicName())) { + springOsgiExtender = bundle; + } else { + bundlesToStart.add(bundle); + } + } + startBundles(bundlesToStart); + } + + /** + * Starts SpringDM managed services. + * + * @param waitForServices + * if true waits for services to start before returning + * @throws BundleException + * if the framework has not been started or a service could not be started + */ + public void startServices(boolean waitForServices) throws BundleException { + if (framework == null || framework.getState() != Bundle.ACTIVE) { + throw new BundleException("Framework not started"); + } + if (springOsgiExtender != null) { + logger.info("Starting Spring OSGi Extender"); + springOsgiExtender.start(); + if (waitForServices) { + logger.info("Waiting for spring contexts to be started"); + for (Bundle bundle : installedBundles) { + if (bundle.getState() == Bundle.ACTIVE) { + if (hasSpringContext(bundle)) { + logger.fine("Waiting for " + bundle.getSymbolicName()); + waitForSpringContext(context, bundle.getSymbolicName(), serviceLoadTimeoutSeconds); + } + } + } + } + } + } + + /** + * Stops the OSGI framework. + * + * @throws BundleException + * if the framework has not been started + * @throws InterruptedException + * if the thread is interrupted while the framework is stopping + */ + public void stop() throws BundleException, InterruptedException { + if (framework == null || framework.getState() != Bundle.ACTIVE) { + throw new BundleException("Framework not started"); + } + framework.stop(); + framework.waitForStop(0); + context = null; + } + + /** + * Installs the bundles specified by the URIs into the framework. + * + * @param bundlesURIs + * the URIs of the bundles to install + * @return the installed bundles + * @throws BundleException + * if a bundle could not be installed + */ + public List<Bundle> installBundles(List<URI> bundlesURIs) throws BundleException { + List<Bundle> installedBundles = new ArrayList<Bundle>(); + logger.info("Installing bundles into the OSGi Framework"); + for (URI bundleURI : bundlesURIs) { + installedBundles.add(installBundle(bundleURI)); + } + return installedBundles; + } + + /** + * Installs the bundle specified by the URI into the framework. + * + * @param bundleURI + * the URI of the bundle to install + * @return the installed bundle + * @throws BundleException + * if the bundle could not be installed + */ + public Bundle installBundle(URI bundleURI) throws BundleException { + logger.fine("Installing bundle " + bundleURI); + return context.installBundle(bundleURI.toASCIIString()); + } + + /** + * Starts the bundles. + * + * If a bundle is a fragment bundle that bundle is not started. + * + * @param bundles + * the bundles to start + * @throws BundleException + * if a bundle could not be started + */ + public void startBundles(List<Bundle> bundles) throws BundleException { + logger.info("Starting bundles in the OSGi Framework"); + for (Bundle bundle : bundles) { + startBundle(bundle); + } + } + + /** + * Starts the bundle. + * + * If the bundle is a fragment bundle the bundle is not started. + * + * @param bundle + * the bundle to start + * @throws BundleException + * if the bundle could not be started + */ + public void startBundle(Bundle bundle) throws BundleException { + if (bundle.getHeaders().get(Constants.FRAGMENT_HOST) == null) { + logger.fine("Starting bundle " + bundle); + bundle.start(); + } + } + + /** + * Returns the context. Returns <code>null</code> if the framework is not started. + * + * @return the context + */ + public BundleContext getContext() { + return context; + } + + /** + * Sets the configuration to use when creating the OSGi framework. + * + * @param frameworkConfiguration the configuration to use when creating the OSGi framework + */ + public void setFrameworkConfiguration(Map<String, String> frameworkConfiguration) { + this.frameworkConfiguration = frameworkConfiguration; + } + + /** + * Adds boot delegation packages. + * + * Multiple packages must be separated by a ','. + * + * @param additionalBootDelegationPackages + * boot delegation packages to add + */ + public void addBootDelegationPackages(String additionalBootDelegationPackages) { + String bootDelegationPackages = frameworkConfiguration + .get(Constants.FRAMEWORK_BOOTDELEGATION); + if (bootDelegationPackages == null || bootDelegationPackages.isEmpty()) { + bootDelegationPackages = additionalBootDelegationPackages; + } else { + bootDelegationPackages = bootDelegationPackages + "," + + additionalBootDelegationPackages; + } + frameworkConfiguration.put(Constants.FRAMEWORK_BOOTDELEGATION, bootDelegationPackages); + } + + /** + * Sets the boot delegation packages. + * + * Multiple packages must be separated by a ','. + * + * @param bootDelegationPackages + * the boot delegation packages + */ + public void setBootDelegationPackages(String bootDelegationPackages) { + frameworkConfiguration.put(Constants.FRAMEWORK_BOOTDELEGATION, bootDelegationPackages); + } + + /** + * Adds system packages. + * + * Multiple packages must be separated by a ','. + * + * @param additionalSystemPackages + * system packages to add + */ + public void addSystemPackages(String additionalSystemPackages) { + String systemPackages = frameworkConfiguration + .get(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA); + if (systemPackages == null || systemPackages.isEmpty()) { + systemPackages = additionalSystemPackages; + } else { + systemPackages = systemPackages + "," + additionalSystemPackages; + } + frameworkConfiguration.put(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA, systemPackages); + } + + /** + * Sets the system packages. + * + * Multiple packages must be separated by a ','. + * + * @param systemPackages + * the system packages + */ + public void setSystemPackages(String systemPackages) { + frameworkConfiguration.put(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA, systemPackages); + } + + /** + * Sets the persistent storage area used by the framework. + * + * @param storageDirectory the persistent storage area used by the framework + */ + public void setStorageDirectory(File storageDirectory) { + frameworkConfiguration.put(Constants.FRAMEWORK_STORAGE, storageDirectory.getAbsolutePath()); + } + + /** + * Set whether the storage directory should be cleaned on startup. + * + * @param cleanStorageDirectory + * whether the storage directory should be cleaned on startup + */ + public void setCleanStorageDirectory(boolean cleanStorageDirectory) { + if (cleanStorageDirectory) { + frameworkConfiguration.put(Constants.FRAMEWORK_STORAGE_CLEAN, + Constants.FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT); + } else { + frameworkConfiguration.remove(Constants.FRAMEWORK_STORAGE_CLEAN); + } + } + + /** + * Returns true if a bundle contains spring context files. + * + * @param bundle + * the bundle to check + * @return true if a bundle contains spring context files + */ + private boolean hasSpringContext(Bundle bundle) { + String springFilesLocation = "META-INF/spring"; + // check for custom spring files location + @SuppressWarnings("rawtypes") + Dictionary headers = bundle.getHeaders(); + if (headers != null) { + Object header = headers.get("Spring-Context"); + if (header != null) { + springFilesLocation = header.toString().trim(); + } + } + @SuppressWarnings("rawtypes") + Enumeration springFiles = bundle.findEntries(springFilesLocation, "*.xml", false); + return springFiles != null && springFiles.hasMoreElements(); + } + + private synchronized void waitForSpringContext(BundleContext context, String springContext, + long timeoutSeconds) { + long timeLeftToWait = timeoutSeconds * 1000; + long startTime = System.currentTimeMillis(); + + while (!startedSpringContexts.contains(springContext) && timeLeftToWait > 0) { + try { + wait(timeLeftToWait); + } catch (InterruptedException e) {} + timeLeftToWait = timeLeftToWait - (System.currentTimeMillis() - startTime); + } + } + + private synchronized void addStartedSpringContext(String springContext) { + startedSpringContexts.add(springContext); + notifyAll(); + } + +}