/** * OrbisGIS is a java GIS application dedicated to research in GIScience. * OrbisGIS is developed by the GIS group of the DECIDE team of the * Lab-STICC CNRS laboratory, see <http://www.lab-sticc.fr/>. * * The GIS group of the DECIDE team is located at : * * Laboratoire Lab-STICC – CNRS UMR 6285 * Equipe DECIDE * UNIVERSITÉ DE BRETAGNE-SUD * Institut Universitaire de Technologie de Vannes * 8, Rue Montaigne - BP 561 56017 Vannes Cedex * * OrbisGIS is distributed under GPL 3 license. * * Copyright (C) 2007-2014 CNRS (IRSTV FR CNRS 2488) * Copyright (C) 2015-2017 CNRS (Lab-STICC UMR CNRS 6285) * * This file is part of OrbisGIS. * * OrbisGIS is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * OrbisGIS 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 General Public License for more details. * * You should have received a copy of the GNU General Public License along with * OrbisGIS. If not, see <http://www.gnu.org/licenses/>. * * For more information, please consult: <http://www.orbisgis.org/> * or contact directly: * info_at_ orbisgis.org */ package org.orbisgis.framework; import java.io.File; import java.util.*; import org.apache.felix.framework.Logger; import org.apache.felix.framework.util.Util; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.BundleException; import org.osgi.framework.Constants; import org.osgi.framework.launch.Framework; import org.osgi.framework.launch.FrameworkFactory; /** * Core internal manager for OSGI Framework * @author Nicolas Fortin */ public class PluginHost { private Framework framework; private final static int STOP_TIMEOUT = 15000; private final static int BUNDLE_STATE_CHECK_INTERVAL = 100; private File pluginCacheFolder; private List<PackageDeclaration> packageList = new ArrayList<PackageDeclaration>(); private Logger LOGGER; /** * * @param pluginCacheFolder Cache folder */ public PluginHost(File pluginCacheFolder, Logger logger) { this.pluginCacheFolder = pluginCacheFolder; packageList.add(new PackageDeclaration("org.orbisgis.view", 4,0,0)); this.LOGGER = logger; } /** * The host will automatically export all packages, but without version information. * This method give the ability to introduce a package constraint on export, like version number. * @param packageInfo Extanded package information, providing a new version * to packages help to identify incompatibilities between bundles. */ public void exportCorePackage(PackageDeclaration packageInfo) throws IllegalStateException { if(framework!=null) { throw new IllegalStateException("The OSGI framework has been already initialised"); } packageList.add(packageInfo); } private void addPackage(PackageDeclaration packInfo,List<String> sortedPackagesExport) { if(!packInfo.isVersionDefined()) { sortedPackagesExport.add(packInfo.getPackageName()); } else { sortedPackagesExport.add(packInfo.getPackageName()+ "; version="+packInfo.getVersion()); } } /** * Parse classpath to find all packages name available. Write them all without version information, * Except for defined packages through exportCorePackage(), * @return */ private String getExtraPackage(Set<String> ignorePackages) { BundleTools bundleTools = new BundleTools(LOGGER); //Build a set of packages to skip programmaticaly defined packages Set<String> packagesName = new HashSet<>(ignorePackages); List<String> sortedPackagesExport = new ArrayList<String>(); for(PackageDeclaration packInfo : packageList) { packagesName.add(packInfo.getPackageName()); addPackage(packInfo,sortedPackagesExport); } // Fetch built-ins OSGi bundles package declarations Collection<PackageDeclaration> packageDeclarations = bundleTools.fetchManifests(); for(PackageDeclaration packageDeclaration : packageDeclarations) { if(!packagesName.contains(packageDeclaration.getPackageName())) { packagesName.add(packageDeclaration.getPackageName()); addPackage(packageDeclaration,sortedPackagesExport); } } // Export Host provided packages, by classpaths List<String> classPathExtensions = bundleTools.getAvailablePackages(); for(String ext : classPathExtensions) { if(!packagesName.contains(ext) && !ext.startsWith("org.osgi.") && !ext.startsWith("java.")) { sortedPackagesExport.add(ext); } } // Sort export package Collections.sort(sortedPackagesExport); StringBuilder sb = new StringBuilder(); for(String ext : sortedPackagesExport) { if(sb.length()!=0) { sb.append(","); } sb.append(ext); } return sb.toString(); } /** * Create a valid bundleContext, but do not start bundles */ public void init(Map<String, String> frameworkConfig) { // Define service interface exported by Framework orbisgis-core Set<String> ignorePackages = new HashSet<>(); String ignorePackageString = frameworkConfig.get("org.osgi.framework.system.packages.ignore"); if(ignorePackageString != null) { StringTokenizer st = new StringTokenizer(ignorePackageString, ","); while(st.hasMoreTokens()) { String ignorePackage = st.nextToken(); ignorePackages.add(ignorePackage); } } String fileExtraPackage = frameworkConfig.get(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA); frameworkConfig.put(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA,getExtraPackage(ignorePackages)+(fileExtraPackage != null ? ","+fileExtraPackage : "")); // Apply ignore list on default export package if(!ignorePackages.isEmpty()) { StringBuilder newSysPackages = new StringBuilder(); String defaultSysPackage = frameworkConfig.get(Constants.FRAMEWORK_SYSTEMPACKAGES); if (defaultSysPackage == null) { defaultSysPackage = Util.getDefaultProperty(LOGGER, Constants.FRAMEWORK_SYSTEMPACKAGES); } StringTokenizer st = new StringTokenizer(defaultSysPackage, ","); while(st.hasMoreTokens()) { String packageDeclaration = st.nextToken(); // Remove version and uses String packagePart = packageDeclaration.indexOf(';') > 0 ? packageDeclaration.substring(0, packageDeclaration.indexOf(';')) : packageDeclaration; if(!ignorePackages.contains(packagePart.trim())) { if(newSysPackages.length() != 0) { newSysPackages.append(","); } newSysPackages.append(packageDeclaration); } } frameworkConfig.put(Constants.FRAMEWORK_SYSTEMPACKAGES, newSysPackages.toString()); } // Persistence's data frameworkConfig.put(Constants.FRAMEWORK_STORAGE, pluginCacheFolder.getAbsolutePath()); framework = createEmbeddedFramework(frameworkConfig); try { framework.init(); } catch(BundleException ex) { LOGGER.log(Logger.LOG_ERROR, ex.getLocalizedMessage(), ex); } } /** * Start the Framework */ public void start() { try { framework.start(); openTrackers(); } catch(BundleException ex) { LOGGER.log(Logger.LOG_ERROR, ex.getLocalizedMessage(), ex); } } private void openTrackers() { } private void closeTrackers() { } /** * Stop the host Framework, and wait that all bundles are stopped * @throws BundleException * @throws InterruptedException */ public void stop() throws BundleException, InterruptedException { framework.stop(); closeTrackers(); framework.waitForStop(STOP_TIMEOUT); } /** * @return The OSGI host framework */ final public Framework getFramework() { return framework; } /** * @return The BundleContext of the host */ final public BundleContext getHostBundleContext() { return framework.getBundleContext(); } /** * Create an embedded Framework. * @param frameworkConfig Framework parameters * @return Framework instance * @throws IllegalStateException If the Framework cannot be created */ private Framework createEmbeddedFramework(Map<String, String> frameworkConfig) { ServiceLoader<FrameworkFactory> factoryLoader = ServiceLoader.load(FrameworkFactory.class); Iterator<FrameworkFactory> it = factoryLoader.iterator(); if(!it.hasNext()) { throw new IllegalStateException("FrameworkFactory service could not be created."); } return it.next().newFramework(frameworkConfig); } /** * @param timeout Ms, wait this time max * @return True if the state is stable. */ public boolean waitForBundlesStableState(int timeout) { long begin = System.currentTimeMillis(); while(!isBundleStateStable()) { if(System.currentTimeMillis() > begin + timeout) { return isBundleStateStable(); } else { try { Thread.sleep(BUNDLE_STATE_CHECK_INTERVAL); } catch (InterruptedException e) { return isBundleStateStable(); } } } return isBundleStateStable(); } private boolean isBundleStateStable() { Set<Integer> unStableStates = new HashSet<Integer>(Arrays.asList(new Integer[]{ Bundle.SIGNERS_ALL, Bundle.SIGNERS_TRUSTED, Bundle.START_ACTIVATION_POLICY, Bundle.STARTING, Bundle.STOP_TRANSIENT, Bundle.STOPPING})); for (Bundle bundle : getHostBundleContext().getBundles()) { if(unStableStates.contains(bundle.getState())) { return false; } } return true; } }