| /******************************************************************************* |
| * Copyright (c) 2003, 2010 IBM Corporation and others. |
| * 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: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.osgi.framework.internal.core; |
| |
| import java.io.*; |
| import java.lang.reflect.*; |
| import java.net.*; |
| import java.security.*; |
| import java.util.*; |
| import org.eclipse.core.runtime.internal.adaptor.ContextFinder; |
| import org.eclipse.osgi.baseadaptor.BaseAdaptor; |
| import org.eclipse.osgi.framework.adaptor.*; |
| import org.eclipse.osgi.framework.debug.Debug; |
| import org.eclipse.osgi.framework.eventmgr.*; |
| import org.eclipse.osgi.framework.internal.protocol.ContentHandlerFactory; |
| import org.eclipse.osgi.framework.internal.protocol.StreamHandlerFactory; |
| import org.eclipse.osgi.framework.log.FrameworkLog; |
| import org.eclipse.osgi.framework.log.FrameworkLogEntry; |
| import org.eclipse.osgi.framework.util.SecureAction; |
| import org.eclipse.osgi.internal.loader.*; |
| import org.eclipse.osgi.internal.permadmin.EquinoxSecurityManager; |
| import org.eclipse.osgi.internal.permadmin.SecurityAdmin; |
| import org.eclipse.osgi.internal.profile.Profile; |
| import org.eclipse.osgi.internal.serviceregistry.ServiceRegistry; |
| import org.eclipse.osgi.signedcontent.SignedContentFactory; |
| import org.eclipse.osgi.util.ManifestElement; |
| import org.eclipse.osgi.util.NLS; |
| import org.osgi.framework.*; |
| import org.osgi.util.tracker.ServiceTracker; |
| |
| /** |
| * Core OSGi Framework class. |
| */ |
| public class Framework implements EventDispatcher, EventPublisher, Runnable { |
| // System property used to set the context classloader parent classloader type (ccl is the default) |
| private static final String PROP_CONTEXTCLASSLOADER_PARENT = "osgi.contextClassLoaderParent"; //$NON-NLS-1$ |
| private static final String CONTEXTCLASSLOADER_PARENT_APP = "app"; //$NON-NLS-1$ |
| private static final String CONTEXTCLASSLOADER_PARENT_EXT = "ext"; //$NON-NLS-1$ |
| private static final String CONTEXTCLASSLOADER_PARENT_BOOT = "boot"; //$NON-NLS-1$ |
| private static final String CONTEXTCLASSLOADER_PARENT_FWK = "fwk"; //$NON-NLS-1$ |
| |
| public static final String PROP_FRAMEWORK_THREAD = "osgi.framework.activeThreadType"; //$NON-NLS-1$ |
| public static final String THREAD_NORMAL = "normal"; //$NON-NLS-1$ |
| public static final String PROP_EQUINOX_SECURITY = "eclipse.security"; //$NON-NLS-1$ |
| public static final String SECURITY_OSGI = "osgi"; //$NON-NLS-1$ |
| |
| private static String J2SE = "J2SE-"; //$NON-NLS-1$ |
| private static String JAVASE = "JavaSE-"; //$NON-NLS-1$ |
| private static String PROFILE_EXT = ".profile"; //$NON-NLS-1$ |
| /** FrameworkAdaptor specific functions. */ |
| protected FrameworkAdaptor adaptor; |
| /** Framework properties object. A reference to the |
| * System.getProperies() object. The properties from |
| * the adaptor will be merged into these properties. |
| */ |
| protected Properties properties; |
| /** Has the framework been started */ |
| protected boolean active; |
| /** Event indicating the reason for shutdown*/ |
| private FrameworkEvent[] shutdownEvent; |
| /** The bundles installed in the framework */ |
| protected BundleRepository bundles; |
| /** Package Admin object. This object manages the exported packages. */ |
| protected PackageAdminImpl packageAdmin; |
| /** PermissionAdmin and ConditionalPermissionAdmin impl. This object manages the bundle permissions. */ |
| protected SecurityAdmin securityAdmin; |
| /** Startlevel object. This object manages the framework and bundle startlevels */ |
| protected StartLevelManager startLevelManager; |
| /** The ServiceRegistry */ |
| private ServiceRegistry serviceRegistry; |
| |
| /* |
| * The following EventListeners objects keep track of event listeners |
| * by BundleContext. Each element is a EventListeners that is the list |
| * of event listeners for a particular BundleContext. The max number of |
| * elements each of the following lists will have is the number of bundles |
| * installed in the Framework. |
| */ |
| /** List of BundleContexts for bundle's BundleListeners. */ |
| protected Map bundleEvent; |
| protected static final int BUNDLEEVENT = 1; |
| /** List of BundleContexts for bundle's SynchronousBundleListeners. */ |
| protected Map bundleEventSync; |
| protected static final int BUNDLEEVENTSYNC = 2; |
| /* SERVICEEVENT(3) is now handled by ServiceRegistry */ |
| /** List of BundleContexts for bundle's FrameworkListeners. */ |
| protected Map frameworkEvent; |
| protected static final int FRAMEWORKEVENT = 4; |
| protected static final int BATCHEVENT_BEGIN = Integer.MIN_VALUE + 1; |
| protected static final int BATCHEVENT_END = Integer.MIN_VALUE; |
| /** EventManager for event delivery. */ |
| protected EventManager eventManager; |
| /* Reservation object for install synchronization */ |
| protected Hashtable installLock; |
| /** System Bundle object */ |
| protected InternalSystemBundle systemBundle; |
| private String[] bootDelegation; |
| private String[] bootDelegationStems; |
| private boolean bootDelegateAll = false; |
| public final boolean contextBootDelegation = "true".equals(FrameworkProperties.getProperty("osgi.context.bootdelegation", "true")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| public final boolean compatibiltyBootDelegation = "true".equals(FrameworkProperties.getProperty(Constants.OSGI_COMPATIBILITY_BOOTDELEGATION, "true")); //$NON-NLS-1$ //$NON-NLS-2$ |
| ClassLoaderDelegateHook[] delegateHooks; |
| private volatile boolean forcedRestart = false; |
| /** |
| * The AliasMapper used to alias OS Names. |
| */ |
| protected static AliasMapper aliasMapper = new AliasMapper(); |
| SecureAction secureAction = (SecureAction) AccessController.doPrivileged(SecureAction.createSecureAction()); |
| // cache of AdminPermissions keyed by Bundle ID |
| private HashMap adminPermissions = new HashMap(); |
| |
| // we need to hold these so that we can unregister them at shutdown |
| private StreamHandlerFactory streamHandlerFactory; |
| private ContentHandlerFactory contentHandlerFactory; |
| |
| private volatile ServiceTracker signedContentFactory; |
| private volatile ContextFinder contextFinder; |
| |
| /* |
| * We need to make sure that the GetDataFileAction class loads early to prevent a ClassCircularityError when checking permissions. |
| * see bug 161561 |
| */ |
| static { |
| Class c; |
| c = GetDataFileAction.class; |
| c.getName(); // to prevent compiler warnings |
| } |
| |
| static class GetDataFileAction implements PrivilegedAction { |
| private AbstractBundle bundle; |
| private String filename; |
| |
| public GetDataFileAction(AbstractBundle bundle, String filename) { |
| this.bundle = bundle; |
| this.filename = filename; |
| } |
| |
| public Object run() { |
| return bundle.getBundleData().getDataFile(filename); |
| } |
| } |
| |
| /** |
| * Constructor for the Framework instance. This method initializes the |
| * framework to an unlaunched state. |
| * |
| */ |
| public Framework(FrameworkAdaptor adaptor) { |
| initialize(adaptor); |
| } |
| |
| /** |
| * Initialize the framework to an unlaunched state. This method is called |
| * by the Framework constructor. |
| * |
| */ |
| protected void initialize(FrameworkAdaptor initAdaptor) { |
| if (Profile.PROFILE && Profile.STARTUP) |
| Profile.logEnter("Framework.initialze()", null); //$NON-NLS-1$ |
| long start = System.currentTimeMillis(); |
| this.adaptor = initAdaptor; |
| delegateHooks = initAdaptor instanceof BaseAdaptor ? ((BaseAdaptor) initAdaptor).getHookRegistry().getClassLoaderDelegateHooks() : null; |
| active = false; |
| installSecurityManager(); |
| if (Debug.DEBUG_SECURITY) { |
| Debug.println("SecurityManager: " + System.getSecurityManager()); //$NON-NLS-1$ |
| Debug.println("ProtectionDomain of Framework.class: \n" + this.getClass().getProtectionDomain()); //$NON-NLS-1$ |
| } |
| setNLSFrameworkLog(); |
| // initialize ContextFinder |
| initializeContextFinder(); |
| /* initialize the adaptor */ |
| initAdaptor.initialize(this); |
| if (Profile.PROFILE && Profile.STARTUP) |
| Profile.logTime("Framework.initialze()", "adapter initialized"); //$NON-NLS-1$//$NON-NLS-2$ |
| try { |
| initAdaptor.initializeStorage(); |
| } catch (IOException e) /* fatal error */{ |
| throw new RuntimeException(e.getMessage(), e); |
| } |
| if (Profile.PROFILE && Profile.STARTUP) |
| Profile.logTime("Framework.initialze()", "adapter storage initialized"); //$NON-NLS-1$//$NON-NLS-2$ |
| /* |
| * This must be done before calling any of the framework getProperty |
| * methods. |
| */ |
| initializeProperties(initAdaptor.getProperties()); |
| /* initialize admin objects */ |
| packageAdmin = new PackageAdminImpl(this); |
| try { |
| // always create security admin even with security off |
| securityAdmin = new SecurityAdmin(null, this, initAdaptor.getPermissionStorage()); |
| } catch (IOException e) /* fatal error */{ |
| e.printStackTrace(); |
| throw new RuntimeException(e.getMessage(), e); |
| } |
| if (Profile.PROFILE && Profile.STARTUP) |
| Profile.logTime("Framework.initialze()", "done init props & new PermissionAdminImpl"); //$NON-NLS-1$//$NON-NLS-2$ |
| startLevelManager = new StartLevelManager(this); |
| /* create the event manager and top level event dispatchers */ |
| eventManager = new EventManager("Framework Event Dispatcher"); //$NON-NLS-1$ |
| bundleEvent = new CopyOnWriteIdentityMap(); |
| bundleEventSync = new CopyOnWriteIdentityMap(); |
| frameworkEvent = new CopyOnWriteIdentityMap(); |
| if (Profile.PROFILE && Profile.STARTUP) |
| Profile.logTime("Framework.initialze()", "done new EventManager"); //$NON-NLS-1$ //$NON-NLS-2$ |
| /* create the service registry */ |
| serviceRegistry = new ServiceRegistry(this); |
| // Initialize the installLock; there is no way of knowing |
| // what the initial size should be, at most it will be the number |
| // of threads trying to install a bundle (probably a very low number). |
| installLock = new Hashtable(10); |
| /* create the system bundle */ |
| createSystemBundle(); |
| loadVMProfile(); // load VM profile after the system bundle has been created |
| setBootDelegation(); //set boot delegation property after system exports have been set |
| if (Profile.PROFILE && Profile.STARTUP) |
| Profile.logTime("Framework.initialze()", "done createSystemBundle"); //$NON-NLS-1$ //$NON-NLS-2$ |
| /* install URLStreamHandlerFactory */ |
| installURLStreamHandlerFactory(systemBundle.context, initAdaptor); |
| /* install ContentHandlerFactory for OSGi URLStreamHandler support */ |
| installContentHandlerFactory(systemBundle.context, initAdaptor); |
| if (Profile.PROFILE && Profile.STARTUP) |
| Profile.logTime("Framework.initialze()", "done new URLStream/Content HandlerFactory"); //$NON-NLS-1$//$NON-NLS-2$ |
| /* create bundle objects for all installed bundles. */ |
| BundleData[] bundleDatas = initAdaptor.getInstalledBundles(); |
| bundles = new BundleRepository(bundleDatas == null ? 10 : bundleDatas.length + 1); |
| /* add the system bundle to the Bundle Repository */ |
| bundles.add(systemBundle); |
| if (bundleDatas != null) { |
| for (int i = 0; i < bundleDatas.length; i++) { |
| try { |
| AbstractBundle bundle = AbstractBundle.createBundle(bundleDatas[i], this, true); |
| bundles.add(bundle); |
| } catch (BundleException be) { |
| // This is not a fatal error. Publish the framework event. |
| publishFrameworkEvent(FrameworkEvent.ERROR, systemBundle, be); |
| } |
| } |
| } |
| if (Debug.DEBUG_GENERAL) |
| System.out.println("Initialize the framework: " + (System.currentTimeMillis() - start)); //$NON-NLS-1$ |
| if (Profile.PROFILE && Profile.STARTUP) |
| Profile.logExit("Framework.initialize()"); //$NON-NLS-1$ |
| } |
| |
| public FrameworkAdaptor getAdaptor() { |
| return adaptor; |
| } |
| |
| public ClassLoaderDelegateHook[] getDelegateHooks() { |
| return delegateHooks; |
| } |
| |
| public ServiceRegistry getServiceRegistry() { |
| return serviceRegistry; |
| } |
| |
| private void setNLSFrameworkLog() { |
| try { |
| Field frameworkLogField = NLS.class.getDeclaredField("frameworkLog"); //$NON-NLS-1$ |
| frameworkLogField.setAccessible(true); |
| frameworkLogField.set(null, adaptor.getFrameworkLog()); |
| } catch (Exception e) { |
| adaptor.getFrameworkLog().log(new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.ERROR, 0, e.getMessage(), 0, e, null)); |
| } |
| } |
| |
| private void createSystemBundle() { |
| try { |
| systemBundle = new InternalSystemBundle(this); |
| systemBundle.getBundleData().setBundle(systemBundle); |
| } catch (BundleException e) { // fatal error |
| e.printStackTrace(); |
| throw new RuntimeException(NLS.bind(Msg.OSGI_SYSTEMBUNDLE_CREATE_EXCEPTION, e.getMessage()), e); |
| } |
| } |
| |
| /** |
| * Initialize the System properties by copying properties from the adaptor |
| * properties object. This method is called by the initialize method. |
| * |
| */ |
| protected void initializeProperties(Properties adaptorProperties) { |
| properties = FrameworkProperties.getProperties(); |
| Enumeration enumKeys = adaptorProperties.propertyNames(); |
| while (enumKeys.hasMoreElements()) { |
| String key = (String) enumKeys.nextElement(); |
| if (properties.getProperty(key) == null) { |
| properties.put(key, adaptorProperties.getProperty(key)); |
| } |
| } |
| properties.put(Constants.FRAMEWORK_VENDOR, Constants.OSGI_FRAMEWORK_VENDOR); |
| properties.put(Constants.FRAMEWORK_VERSION, Constants.OSGI_FRAMEWORK_VERSION); |
| String value = properties.getProperty(Constants.FRAMEWORK_PROCESSOR); |
| if (value == null) { |
| value = properties.getProperty(Constants.JVM_OS_ARCH); |
| if (value != null) { |
| properties.put(Constants.FRAMEWORK_PROCESSOR, aliasMapper.aliasProcessor(value)); |
| } |
| } |
| value = properties.getProperty(Constants.FRAMEWORK_OS_NAME); |
| if (value == null) { |
| value = properties.getProperty(Constants.JVM_OS_NAME); |
| try { |
| String canonicalValue = (String) aliasMapper.aliasOSName(value); |
| if (canonicalValue != null) { |
| value = canonicalValue; |
| } |
| } catch (ClassCastException ex) { |
| //A vector was returned from the alias mapper. |
| //The alias mapped to more than one canonical value |
| //such as "win32" for example |
| } |
| if (value != null) { |
| properties.put(Constants.FRAMEWORK_OS_NAME, value); |
| } |
| } |
| value = properties.getProperty(Constants.FRAMEWORK_OS_VERSION); |
| if (value == null) { |
| value = properties.getProperty(Constants.JVM_OS_VERSION); |
| if (value != null) { |
| // only use the value upto the first space |
| int space = value.indexOf(' '); |
| if (space > 0) { |
| value = value.substring(0, space); |
| } |
| // fix up cases where the os version does not make a valid Version string. |
| int major = 0, minor = 0, micro = 0; |
| String qualifier = ""; //$NON-NLS-1$ |
| try { |
| StringTokenizer st = new StringTokenizer(value, ".", true); //$NON-NLS-1$ |
| major = parseVersionInt(st.nextToken()); |
| |
| if (st.hasMoreTokens()) { |
| st.nextToken(); // consume delimiter |
| minor = parseVersionInt(st.nextToken()); |
| |
| if (st.hasMoreTokens()) { |
| st.nextToken(); // consume delimiter |
| micro = parseVersionInt(st.nextToken()); |
| |
| if (st.hasMoreTokens()) { |
| st.nextToken(); // consume delimiter |
| qualifier = st.nextToken(); |
| } |
| } |
| } |
| } catch (NoSuchElementException e) { |
| // ignore, use the values parsed so far |
| } |
| try { |
| value = new Version(major, minor, micro, qualifier).toString(); |
| } catch (IllegalArgumentException e) { |
| // must be an invalid qualifier; just ignore it |
| value = new Version(major, minor, micro).toString(); |
| } |
| properties.put(Constants.FRAMEWORK_OS_VERSION, value); |
| } |
| } |
| value = properties.getProperty(Constants.FRAMEWORK_LANGUAGE); |
| if (value == null) |
| // set the value of the framework language property |
| properties.put(Constants.FRAMEWORK_LANGUAGE, Locale.getDefault().getLanguage()); |
| // set the support properties for fragments and require-bundle (bug 173090) |
| properties.put(Constants.SUPPORTS_FRAMEWORK_FRAGMENT, "true"); //$NON-NLS-1$ |
| properties.put(Constants.SUPPORTS_FRAMEWORK_REQUIREBUNDLE, "true"); //$NON-NLS-1$ |
| } |
| |
| private int parseVersionInt(String value) { |
| try { |
| return Integer.parseInt(value); |
| } catch (NumberFormatException e) { |
| // try up to the first non-number char |
| StringBuffer sb = new StringBuffer(value.length()); |
| char[] chars = value.toCharArray(); |
| for (int i = 0; i < chars.length; i++) { |
| if (!Character.isDigit(chars[i])) |
| break; |
| sb.append(chars[i]); |
| } |
| if (sb.length() > 0) |
| return Integer.parseInt(sb.toString()); |
| return 0; |
| } |
| } |
| |
| private void setBootDelegation() { |
| // set the boot delegation according to the osgi boot delegation property |
| String bootDelegationProp = properties.getProperty(Constants.FRAMEWORK_BOOTDELEGATION); |
| if (bootDelegationProp == null) |
| return; |
| if (bootDelegationProp.trim().length() == 0) |
| return; |
| String[] bootPackages = ManifestElement.getArrayFromList(bootDelegationProp); |
| ArrayList exactMatch = new ArrayList(bootPackages.length); |
| ArrayList stemMatch = new ArrayList(bootPackages.length); |
| for (int i = 0; i < bootPackages.length; i++) { |
| if (bootPackages[i].equals("*")) { //$NON-NLS-1$ |
| bootDelegateAll = true; |
| return; |
| } else if (bootPackages[i].endsWith("*")) { //$NON-NLS-1$ |
| if (bootPackages[i].length() > 2 && bootPackages[i].endsWith(".*")) //$NON-NLS-1$ |
| stemMatch.add(bootPackages[i].substring(0, bootPackages[i].length() - 1)); |
| } else { |
| exactMatch.add(bootPackages[i]); |
| } |
| } |
| if (exactMatch.size() > 0) |
| bootDelegation = (String[]) exactMatch.toArray(new String[exactMatch.size()]); |
| if (stemMatch.size() > 0) |
| bootDelegationStems = (String[]) stemMatch.toArray(new String[stemMatch.size()]); |
| } |
| |
| private void loadVMProfile() { |
| Properties profileProps = findVMProfile(); |
| String systemExports = properties.getProperty(Constants.FRAMEWORK_SYSTEMPACKAGES); |
| // set the system exports property using the vm profile; only if the property is not already set |
| if (systemExports == null) { |
| systemExports = profileProps.getProperty(Constants.FRAMEWORK_SYSTEMPACKAGES); |
| if (systemExports != null) |
| properties.put(Constants.FRAMEWORK_SYSTEMPACKAGES, systemExports); |
| } |
| // set the org.osgi.framework.bootdelegation property according to the java profile |
| String type = properties.getProperty(Constants.OSGI_JAVA_PROFILE_BOOTDELEGATION); // a null value means ignore |
| String profileBootDelegation = profileProps.getProperty(Constants.FRAMEWORK_BOOTDELEGATION); |
| if (Constants.OSGI_BOOTDELEGATION_OVERRIDE.equals(type)) { |
| if (profileBootDelegation == null) |
| properties.remove(Constants.FRAMEWORK_BOOTDELEGATION); // override with a null value |
| else |
| properties.put(Constants.FRAMEWORK_BOOTDELEGATION, profileBootDelegation); // override with the profile value |
| } else if (Constants.OSGI_BOOTDELEGATION_NONE.equals(type)) |
| properties.remove(Constants.FRAMEWORK_BOOTDELEGATION); // remove the bootdelegation property in case it was set |
| // set the org.osgi.framework.executionenvironment property according to the java profile |
| if (properties.getProperty(Constants.FRAMEWORK_EXECUTIONENVIRONMENT) == null) { |
| // get the ee from the java profile; if no ee is defined then try the java profile name |
| String ee = profileProps.getProperty(Constants.FRAMEWORK_EXECUTIONENVIRONMENT, profileProps.getProperty(Constants.OSGI_JAVA_PROFILE_NAME)); |
| if (ee != null) |
| properties.put(Constants.FRAMEWORK_EXECUTIONENVIRONMENT, ee); |
| } |
| } |
| |
| private Properties findVMProfile() { |
| Properties result = new Properties(); |
| // Find the VM profile name using J2ME properties |
| String j2meConfig = properties.getProperty(Constants.J2ME_MICROEDITION_CONFIGURATION); |
| String j2meProfiles = properties.getProperty(Constants.J2ME_MICROEDITION_PROFILES); |
| String vmProfile = null; |
| String javaEdition = null; |
| Version javaVersion = null; |
| if (j2meConfig != null && j2meConfig.length() > 0 && j2meProfiles != null && j2meProfiles.length() > 0) { |
| // save the vmProfile based off of the config and profile |
| // use the last profile; assuming that is the highest one |
| String[] j2meProfileList = ManifestElement.getArrayFromList(j2meProfiles, " "); //$NON-NLS-1$ |
| if (j2meProfileList != null && j2meProfileList.length > 0) |
| vmProfile = j2meConfig + '_' + j2meProfileList[j2meProfileList.length - 1]; |
| } else { |
| // No J2ME properties; use J2SE properties |
| // Note that the CDC spec appears not to require VM implementations to set the |
| // javax.microedition properties!! So we will try to fall back to the |
| // java.specification.name property, but this is pretty ridiculous!! |
| String javaSpecVersion = properties.getProperty("java.specification.version"); //$NON-NLS-1$ |
| // set the profile and EE based off of the java.specification.version |
| // TODO We assume J2ME Foundation and J2SE here. need to support other profiles J2EE ... |
| if (javaSpecVersion != null) { |
| StringTokenizer st = new StringTokenizer(javaSpecVersion, " _-"); //$NON-NLS-1$ |
| javaSpecVersion = st.nextToken(); |
| String javaSpecName = properties.getProperty("java.specification.name"); //$NON-NLS-1$ |
| // See bug 291269 we check for Foundation Specification and Foundation Profile Specification |
| if (javaSpecName != null && (javaSpecName.indexOf("Foundation Specification") >= 0 || javaSpecName.indexOf("Foundation Profile Specification") >= 0)) //$NON-NLS-1$ //$NON-NLS-2$ |
| vmProfile = "CDC-" + javaSpecVersion + "_Foundation-" + javaSpecVersion; //$NON-NLS-1$ //$NON-NLS-2$ |
| else { |
| // look for JavaSE if 1.6 or greater; otherwise look for J2SE |
| Version v16 = new Version("1.6"); //$NON-NLS-1$ |
| javaEdition = J2SE; |
| try { |
| javaVersion = new Version(javaSpecVersion); |
| if (v16.compareTo(javaVersion) <= 0) |
| javaEdition = JAVASE; |
| } catch (IllegalArgumentException e) { |
| // do nothing |
| } |
| vmProfile = javaEdition + javaSpecVersion; |
| } |
| } |
| } |
| URL url = null; |
| // check for the java profile property for a url |
| String propJavaProfile = FrameworkProperties.getProperty(Constants.OSGI_JAVA_PROFILE); |
| if (propJavaProfile != null) |
| try { |
| // we assume a URL |
| url = new URL(propJavaProfile); |
| } catch (MalformedURLException e1) { |
| // try using a relative path in the system bundle |
| url = findInSystemBundle(propJavaProfile); |
| } |
| if (url == null && vmProfile != null) { |
| // look for a profile in the system bundle based on the vm profile |
| String javaProfile = vmProfile + PROFILE_EXT; |
| url = findInSystemBundle(javaProfile); |
| if (url == null) |
| url = getNextBestProfile(javaEdition, javaVersion); |
| } |
| if (url == null) |
| // the profile url is still null then use the osgi min profile in OSGi by default |
| url = findInSystemBundle("OSGi_Minimum-1.2.profile"); //$NON-NLS-1$ |
| if (url != null) { |
| InputStream in = null; |
| try { |
| in = url.openStream(); |
| result.load(new BufferedInputStream(in)); |
| } catch (IOException e) { |
| // TODO consider logging ... |
| } finally { |
| if (in != null) |
| try { |
| in.close(); |
| } catch (IOException ee) { |
| // do nothing |
| } |
| } |
| } |
| // set the profile name if it does not provide one |
| if (result.getProperty(Constants.OSGI_JAVA_PROFILE_NAME) == null) |
| if (vmProfile != null) |
| result.put(Constants.OSGI_JAVA_PROFILE_NAME, vmProfile.replace('_', '/')); |
| else |
| // last resort; default to the absolute minimum profile name for the framework |
| result.put(Constants.OSGI_JAVA_PROFILE_NAME, "OSGi/Minimum-1.2"); //$NON-NLS-1$ |
| return result; |
| } |
| |
| private URL getNextBestProfile(String javaEdition, Version javaVersion) { |
| if (javaVersion == null || (javaEdition != J2SE && javaEdition != JAVASE)) |
| return null; // we cannot automatically choose the next best profile unless this is a J2SE or JavaSE vm |
| URL bestProfile = findNextBestProfile(javaEdition, javaVersion); |
| if (bestProfile == null && javaEdition == JAVASE) |
| // if this is a JavaSE VM then search for a lower J2SE profile |
| bestProfile = findNextBestProfile(J2SE, javaVersion); |
| return bestProfile; |
| } |
| |
| private URL findNextBestProfile(String javaEdition, Version javaVersion) { |
| URL result = null; |
| int minor = javaVersion.getMinor(); |
| do { |
| result = findInSystemBundle(javaEdition + javaVersion.getMajor() + "." + minor + PROFILE_EXT); //$NON-NLS-1$ |
| minor = minor - 1; |
| } while (result == null && minor > 0); |
| return result; |
| } |
| |
| private URL findInSystemBundle(String entry) { |
| URL result = systemBundle.getEntry0(entry); |
| if (result == null) { |
| // Check the ClassLoader in case we're launched off the Java boot classpath |
| ClassLoader loader = getClass().getClassLoader(); |
| result = loader == null ? ClassLoader.getSystemResource(entry) : loader.getResource(entry); |
| } |
| return result; |
| } |
| |
| /** |
| * This method return the state of the framework. |
| * |
| */ |
| protected boolean isActive() { |
| return (active); |
| } |
| |
| /** |
| * This method is called to destory the framework instance. |
| * |
| */ |
| public synchronized void close() { |
| if (adaptor == null) |
| return; |
| if (active) |
| shutdown(FrameworkEvent.STOPPED); |
| |
| synchronized (bundles) { |
| List allBundles = bundles.getBundles(); |
| int size = allBundles.size(); |
| for (int i = 0; i < size; i++) { |
| AbstractBundle bundle = (AbstractBundle) allBundles.get(i); |
| bundle.close(); |
| } |
| bundles.removeAllBundles(); |
| } |
| serviceRegistry = null; |
| if (bundleEvent != null) { |
| bundleEvent.clear(); |
| bundleEvent = null; |
| } |
| if (bundleEventSync != null) { |
| bundleEventSync.clear(); |
| bundleEventSync = null; |
| } |
| if (frameworkEvent != null) { |
| frameworkEvent.clear(); |
| frameworkEvent = null; |
| } |
| if (eventManager != null) { |
| eventManager.close(); |
| eventManager = null; |
| } |
| secureAction = null; |
| packageAdmin = null; |
| adaptor = null; |
| uninstallURLStreamHandlerFactory(); |
| uninstallContentHandlerFactory(); |
| if (System.getSecurityManager() instanceof EquinoxSecurityManager) |
| System.setSecurityManager(null); |
| } |
| |
| /** |
| * Start the framework. |
| * |
| * When the framework is started. The following actions occur: 1. Event |
| * handling is enabled. Events can now be delivered to listeners. 2. All |
| * bundles which are recorded as started are started as described in the |
| * Bundle.start() method. These bundles are the bundles that were started |
| * when the framework was last stopped. Reports any exceptions that occur |
| * during startup using FrameworkEvents. 3. A FrameworkEvent of type |
| * FrameworkEvent.STARTED is broadcast. |
| * |
| */ |
| public synchronized void launch() { |
| /* Return if framework already started */ |
| if (active) { |
| return; |
| } |
| /* mark framework as started */ |
| active = true; |
| shutdownEvent = new FrameworkEvent[1]; |
| if (THREAD_NORMAL.equals(FrameworkProperties.getProperty(PROP_FRAMEWORK_THREAD, THREAD_NORMAL))) { |
| Thread fwkThread = new Thread(this, "Framework Active Thread"); //$NON-NLS-1$ |
| fwkThread.setDaemon(false); |
| fwkThread.start(); |
| } |
| /* Resume systembundle */ |
| if (Debug.DEBUG_GENERAL) { |
| Debug.println("Trying to launch framework"); //$NON-NLS-1$ |
| } |
| systemBundle.resume(); |
| signedContentFactory = new ServiceTracker(systemBundle.getBundleContext(), SignedContentFactory.class.getName(), null); |
| signedContentFactory.open(); |
| } |
| |
| /** |
| * Stop the framework. |
| * |
| * When the framework is stopped. The following actions occur: 1. Suspend |
| * all started bundles as described in the Bundle.stop method except that |
| * the bundle is recorded as started. These bundles will be restarted when |
| * the framework is next started. Reports any exceptions that occur during |
| * stopping using FrameworkEvents. 2. Event handling is disabled. |
| * |
| */ |
| public synchronized void shutdown(int eventType) { |
| /* Return if framework already stopped */ |
| if (!active) |
| return; |
| this.shutdownEvent[0] = new FrameworkEvent(eventType, systemBundle, null); |
| /* |
| * set the state of the System Bundle to STOPPING. |
| * this must be done first according to section 4.19.2 from the OSGi R3 spec. |
| */ |
| systemBundle.state = Bundle.STOPPING; |
| publishBundleEvent(BundleEvent.STOPPING, systemBundle); // need to send system bundle stopping event |
| /* call the FrameworkAdaptor.frameworkStopping method first */ |
| try { |
| adaptor.frameworkStopping(systemBundle.getContext()); |
| } catch (Throwable t) { |
| publishFrameworkEvent(FrameworkEvent.ERROR, systemBundle, t); |
| } |
| /* Suspend systembundle */ |
| if (Debug.DEBUG_GENERAL) { |
| Debug.println("Trying to shutdown Framework"); //$NON-NLS-1$ |
| } |
| systemBundle.suspend(); |
| try { |
| adaptor.compactStorage(); |
| } catch (IOException e) { |
| publishFrameworkEvent(FrameworkEvent.ERROR, systemBundle, e); |
| } |
| if (signedContentFactory != null) |
| signedContentFactory.close(); |
| /* mark framework as stopped */ |
| active = false; |
| notifyAll(); |
| } |
| |
| /** |
| * Create a new Bundle object. |
| * |
| * @param bundledata the BundleData of the Bundle to create |
| */ |
| AbstractBundle createAndVerifyBundle(BundleData bundledata, boolean setBundle) throws BundleException { |
| // Check for a bundle already installed with the same symbolic name and version. |
| if (bundledata.getSymbolicName() != null) { |
| AbstractBundle installedBundle = getBundleBySymbolicName(bundledata.getSymbolicName(), bundledata.getVersion()); |
| if (installedBundle != null && installedBundle.getBundleId() != bundledata.getBundleID()) { |
| String msg = NLS.bind(Msg.BUNDLE_INSTALL_SAME_UNIQUEID, new Object[] {installedBundle.getSymbolicName(), installedBundle.getVersion().toString(), installedBundle.getLocation()}); |
| throw new DuplicateBundleException(msg, installedBundle); |
| } |
| } |
| return AbstractBundle.createBundle(bundledata, this, setBundle); |
| } |
| |
| private class DuplicateBundleException extends BundleException implements StatusException { |
| private static final long serialVersionUID = 135669822846323624L; |
| private final Bundle duplicate; |
| |
| public DuplicateBundleException(String msg, Bundle duplicate) { |
| super(msg, BundleException.DUPLICATE_BUNDLE_ERROR); |
| this.duplicate = duplicate; |
| } |
| |
| public Object getStatus() { |
| return duplicate; |
| } |
| |
| public int getStatusCode() { |
| return StatusException.CODE_OK; |
| } |
| |
| } |
| |
| /** |
| * Retrieve the value of the named environment property. Values are |
| * provided for the following properties: |
| * <dl> |
| * <dt><code>org.osgi.framework.version</code> |
| * <dd>The version of the framework. |
| * <dt><code>org.osgi.framework.vendor</code> |
| * <dd>The vendor of this framework implementation. |
| * <dt><code>org.osgi.framework.language</code> |
| * <dd>The language being used. See ISO 639 for possible values. |
| * <dt><code>org.osgi.framework.os.name</code> |
| * <dd>The name of the operating system of the hosting computer. |
| * <dt><code>org.osgi.framework.os.version</code> |
| * <dd>The version number of the operating system of the hosting computer. |
| * <dt><code>org.osgi.framework.processor</code> |
| * <dd>The name of the processor of the hosting computer. |
| * </dl> |
| * |
| * <p> |
| * Note: These last four properties are used by the <code>Bundle-NativeCode</code> |
| * manifest header's matching algorithm for selecting native code. |
| * |
| * @param key |
| * The name of the requested property. |
| * @return The value of the requested property, or <code>null</code> if |
| * the property is undefined. |
| */ |
| public String getProperty(String key) { |
| return properties.getProperty(key); |
| } |
| |
| /** |
| * Retrieve the value of the named environment property. Values are |
| * provided for the following properties: |
| * <dl> |
| * <dt><code>org.osgi.framework.version</code> |
| * <dd>The version of the framework. |
| * <dt><code>org.osgi.framework.vendor</code> |
| * <dd>The vendor of this framework implementation. |
| * <dt><code>org.osgi.framework.language</code> |
| * <dd>The language being used. See ISO 639 for possible values. |
| * <dt><code>org.osgi.framework.os.name</code> |
| * <dd>The name of the operating system of the hosting computer. |
| * <dt><code>org.osgi.framework.os.version</code> |
| * <dd>The version number of the operating system of the hosting computer. |
| * <dt><code>org.osgi.framework.processor</code> |
| * <dd>The name of the processor of the hosting computer. |
| * </dl> |
| * |
| * <p> |
| * Note: These last four properties are used by the <code>Bundle-NativeCode</code> |
| * manifest header's matching algorithm for selecting native code. |
| * |
| * @param key |
| * The name of the requested property. |
| * @param def |
| * A default value is the requested property is not present. |
| * @return The value of the requested property, or the default value if the |
| * property is undefined. |
| */ |
| protected String getProperty(String key, String def) { |
| return properties.getProperty(key, def); |
| } |
| |
| /** |
| * Set a system property. |
| * |
| * @param key |
| * The name of the property to set. |
| * @param value |
| * The value to set. |
| * @return The previous value of the property or null if the property was |
| * not previously set. |
| */ |
| protected Object setProperty(String key, String value) { |
| return properties.put(key, value); |
| } |
| |
| /** |
| * Install a bundle from an InputStream. |
| * |
| * @param location |
| * The location identifier of the bundle to install. |
| * @param in |
| * The InputStream from which the bundle will be read. If null |
| * then the location is used to get the bundle content. |
| * @return The Bundle of the installed bundle. |
| */ |
| protected AbstractBundle installBundle(final String location, final InputStream in) throws BundleException { |
| if (Debug.DEBUG_GENERAL) { |
| Debug.println("install from inputstream: " + location + ", " + in); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| final AccessControlContext callerContext = AccessController.getContext(); |
| return installWorker(location, new PrivilegedExceptionAction() { |
| public Object run() throws BundleException { |
| /* Map the InputStream or location to a URLConnection */ |
| URLConnection source = in != null ? new BundleSource(in) : adaptor.mapLocationToURLConnection(location); |
| /* call the worker to install the bundle */ |
| return installWorkerPrivileged(location, source, callerContext); |
| } |
| }); |
| } |
| |
| /** |
| * Worker method to install a bundle. It obtains the reservation for the |
| * location and calls the specified action. |
| * |
| * @param location |
| * The location identifier of the bundle to install. |
| * @param action |
| * A PrivilegedExceptionAction which calls the real worker. |
| * @return The {@link AbstractBundle}of the installed bundle. |
| * @exception BundleException |
| * If the action throws an error. |
| */ |
| protected AbstractBundle installWorker(String location, PrivilegedExceptionAction action) throws BundleException { |
| synchronized (installLock) { |
| while (true) { |
| /* Check that the bundle is not already installed. */ |
| AbstractBundle bundle = getBundleByLocation(location); |
| /* If already installed, return bundle object */ |
| if (bundle != null) { |
| return bundle; |
| } |
| Thread current = Thread.currentThread(); |
| /* Check for and make reservation */ |
| Thread reservation = (Thread) installLock.put(location, current); |
| /* if the location is not already reserved */ |
| if (reservation == null) { |
| /* we have made the reservation and can continue */ |
| break; |
| } |
| /* the location was already reserved */ |
| /* |
| * If the reservation is held by the current thread, we have |
| * recursed to install the same bundle! |
| */ |
| if (current.equals(reservation)) { |
| throw new BundleException(Msg.BUNDLE_INSTALL_RECURSION_EXCEPTION, BundleException.STATECHANGE_ERROR); |
| } |
| try { |
| /* wait for the reservation to be released */ |
| installLock.wait(); |
| } catch (InterruptedException e) { |
| Thread.currentThread().interrupt(); |
| throw new BundleException("Thread has been interrupted while waiting for the location lock.", e); //$NON-NLS-1$ |
| } |
| } |
| } |
| /* Don't call adaptor while holding the install lock */ |
| try { |
| AbstractBundle bundle = (AbstractBundle) AccessController.doPrivileged(action); |
| publishBundleEvent(BundleEvent.INSTALLED, bundle); |
| return bundle; |
| } catch (PrivilegedActionException e) { |
| if (e.getException() instanceof RuntimeException) |
| throw (RuntimeException) e.getException(); |
| throw (BundleException) e.getException(); |
| } finally { |
| synchronized (installLock) { |
| /* release reservation */ |
| installLock.remove(location); |
| /* wake up all waiters */ |
| installLock.notifyAll(); |
| } |
| } |
| } |
| |
| /** |
| * Worker method to install a bundle. It calls the FrameworkAdaptor object |
| * to install the bundle in persistent storage. |
| * |
| * @param location |
| * The location identifier of the bundle to install. |
| * @param source |
| * The URLConnection from which the bundle will be read. |
| * @return The {@link AbstractBundle}of the installed bundle. |
| * @exception BundleException |
| * If the provided stream cannot be read. |
| */ |
| protected AbstractBundle installWorkerPrivileged(String location, URLConnection source, AccessControlContext callerContext) throws BundleException { |
| BundleOperation storage = adaptor.installBundle(location, source); |
| final AbstractBundle bundle; |
| try { |
| BundleData bundledata = storage.begin(); |
| bundle = createAndVerifyBundle(bundledata, true); |
| |
| BundleWatcher bundleStats = adaptor.getBundleWatcher(); |
| if (bundleStats != null) |
| bundleStats.watchBundle(bundle, BundleWatcher.START_INSTALLING); |
| |
| try { |
| bundle.load(); |
| if (System.getSecurityManager() != null) { |
| final boolean extension = (bundledata.getType() & (BundleData.TYPE_BOOTCLASSPATH_EXTENSION | BundleData.TYPE_FRAMEWORK_EXTENSION | BundleData.TYPE_EXTCLASSPATH_EXTENSION)) != 0; |
| // must check for AllPermission before allow a bundle extension to be installed |
| if (extension && !bundle.hasPermission(new AllPermission())) |
| throw new BundleException(Msg.BUNDLE_EXTENSION_PERMISSION, BundleException.SECURITY_ERROR, new SecurityException(Msg.BUNDLE_EXTENSION_PERMISSION)); |
| try { |
| AccessController.doPrivileged(new PrivilegedExceptionAction() { |
| public Object run() throws Exception { |
| checkAdminPermission(bundle, AdminPermission.LIFECYCLE); |
| if (extension) // need special permission to install extension bundles |
| checkAdminPermission(bundle, AdminPermission.EXTENSIONLIFECYCLE); |
| return null; |
| } |
| }, callerContext); |
| } catch (PrivilegedActionException e) { |
| throw e.getException(); |
| } |
| } |
| storage.commit(false); |
| } catch (Throwable error) { |
| synchronized (bundles) { |
| bundle.unload(); |
| } |
| bundle.close(); |
| throw error; |
| } finally { |
| if (bundleStats != null) |
| bundleStats.watchBundle(bundle, BundleWatcher.END_INSTALLING); |
| } |
| /* bundle has been successfully installed */ |
| bundles.add(bundle); |
| } catch (Throwable t) { |
| try { |
| storage.undo(); |
| } catch (BundleException ee) { |
| publishFrameworkEvent(FrameworkEvent.ERROR, systemBundle, ee); |
| } |
| if (t instanceof SecurityException) |
| throw (SecurityException) t; |
| if (t instanceof BundleException) |
| throw (BundleException) t; |
| throw new BundleException(t.getMessage(), t); |
| } |
| return bundle; |
| } |
| |
| /** |
| * Retrieve the bundle that has the given unique identifier. |
| * |
| * @param id |
| * The identifier of the bundle to retrieve. |
| * @return A {@link AbstractBundle}object, or <code>null</code> if the |
| * identifier doesn't match any installed bundle. |
| */ |
| // changed visibility to gain access through the adaptor |
| public AbstractBundle getBundle(long id) { |
| synchronized (bundles) { |
| return bundles.getBundle(id); |
| } |
| } |
| |
| public BundleContextImpl getSystemBundleContext() { |
| if (systemBundle == null) |
| return null; |
| return systemBundle.context; |
| } |
| |
| public PackageAdminImpl getPackageAdmin() { |
| return packageAdmin; |
| } |
| |
| /** |
| * Retrieve the bundle that has the given symbolic name and version. |
| * |
| * @param symbolicName |
| * The symbolic name of the bundle to retrieve |
| * @param version The version of the bundle to retrieve |
| * @return A {@link AbstractBundle}object, or <code>null</code> if the |
| * identifier doesn't match any installed bundle. |
| */ |
| public AbstractBundle getBundleBySymbolicName(String symbolicName, Version version) { |
| synchronized (bundles) { |
| return bundles.getBundle(symbolicName, version); |
| } |
| } |
| |
| /** |
| * Retrieve the BundleRepository of all installed bundles. The list is |
| * valid at the time of the call to getBundles, but the framework is a very |
| * dynamic environment and bundles can be installed or uninstalled at |
| * anytime. |
| * |
| * @return The BundleRepository. |
| */ |
| protected BundleRepository getBundles() { |
| return (bundles); |
| } |
| |
| /** |
| * Retrieve a list of all installed bundles. The list is valid at the time |
| * of the call to getBundleAlls, but the framework is a very dynamic |
| * environment and bundles can be installed or uninstalled at anytime. |
| * |
| * @return An Array of {@link AbstractBundle}objects, one object per installed |
| * bundle. |
| */ |
| protected AbstractBundle[] getAllBundles() { |
| synchronized (bundles) { |
| List allBundles = bundles.getBundles(); |
| int size = allBundles.size(); |
| if (size == 0) { |
| return (null); |
| } |
| AbstractBundle[] bundlelist = new AbstractBundle[size]; |
| allBundles.toArray(bundlelist); |
| return (bundlelist); |
| } |
| } |
| |
| /** |
| * Resume a bundle. |
| * |
| * @param bundle |
| * Bundle to resume. |
| */ |
| protected void resumeBundle(AbstractBundle bundle) { |
| if (bundle.isActive()) { |
| // if bundle is active. |
| return; |
| } |
| try { |
| if (Debug.DEBUG_GENERAL) { |
| Debug.println("Trying to resume bundle " + bundle); //$NON-NLS-1$ |
| } |
| bundle.resume(); |
| } catch (BundleException be) { |
| if (Debug.DEBUG_GENERAL) { |
| Debug.println("Bundle resume exception: " + be.getMessage()); //$NON-NLS-1$ |
| Debug.printStackTrace(be.getNestedException() == null ? be : be.getNestedException()); |
| } |
| publishFrameworkEvent(FrameworkEvent.ERROR, bundle, be); |
| } |
| } |
| |
| /** |
| * Suspend a bundle. |
| * |
| * @param bundle |
| * Bundle to suspend. |
| * @param lock |
| * true if state change lock should be held when returning from |
| * this method. |
| * @return true if bundle was active and is now suspended. |
| */ |
| protected boolean suspendBundle(AbstractBundle bundle, boolean lock) { |
| boolean changed = false; |
| if (!bundle.isActive() || bundle.isFragment()) { |
| // if bundle is not active or is a fragment then do nothing. |
| return changed; |
| } |
| try { |
| if (Debug.DEBUG_GENERAL) { |
| Debug.println("Trying to suspend bundle " + bundle); //$NON-NLS-1$ |
| } |
| bundle.suspend(lock); |
| } catch (BundleException be) { |
| if (Debug.DEBUG_GENERAL) { |
| Debug.println("Bundle suspend exception: " + be.getMessage()); //$NON-NLS-1$ |
| Debug.printStackTrace(be.getNestedException() == null ? be : be.getNestedException()); |
| } |
| publishFrameworkEvent(FrameworkEvent.ERROR, bundle, be); |
| } |
| if (!bundle.isActive()) { |
| changed = true; |
| } |
| return (changed); |
| } |
| |
| /** |
| * Locate an installed bundle with a given identity. |
| * |
| * @param location |
| * string for the bundle |
| * @return Bundle object for bundle with the specified location or null if |
| * no bundle is installed with the specified location. |
| */ |
| protected AbstractBundle getBundleByLocation(String location) { |
| synchronized (bundles) { |
| // this is not optimized; do not think it will get called |
| // that much. |
| final String finalLocation = location; |
| |
| //Bundle.getLocation requires AdminPermission (metadata) |
| return (AbstractBundle) AccessController.doPrivileged(new PrivilegedAction() { |
| public Object run() { |
| List allBundles = bundles.getBundles(); |
| int size = allBundles.size(); |
| for (int i = 0; i < size; i++) { |
| AbstractBundle bundle = (AbstractBundle) allBundles.get(i); |
| if (finalLocation.equals(bundle.getLocation())) { |
| return (bundle); |
| } |
| } |
| return (null); |
| } |
| }); |
| } |
| } |
| |
| /** |
| * Locate an installed bundle with a given symbolic name |
| * |
| * @param symbolicName |
| * The symbolic name for the bundle |
| * @return Bundle object for bundle with the specified Unique or null if no |
| * bundle is installed with the specified location. |
| */ |
| protected AbstractBundle[] getBundleBySymbolicName(String symbolicName) { |
| synchronized (bundles) { |
| return bundles.getBundles(symbolicName); |
| } |
| } |
| |
| /** |
| * Creates a <code>File</code> object for a file in the persistent |
| * storage area provided for the bundle by the framework. If the adaptor |
| * does not have file system support, this method will return <code>null</code>. |
| * |
| * <p> |
| * A <code>File</code> object for the base directory of the persistent |
| * storage area provided for the context bundle by the framework can be |
| * obtained by calling this method with the empty string ("") as the |
| * parameter. |
| */ |
| protected File getDataFile(final AbstractBundle bundle, final String filename) { |
| return (File) AccessController.doPrivileged(new GetDataFileAction(bundle, filename)); |
| } |
| |
| /** |
| * Check for specific AdminPermission (RFC 73) |
| */ |
| protected void checkAdminPermission(Bundle bundle, String action) { |
| SecurityManager sm = System.getSecurityManager(); |
| if (sm != null) |
| sm.checkPermission(getAdminPermission(bundle, action)); |
| } |
| |
| // gets AdminPermission objects from a cache to reduce the number of AdminPermission |
| // objects that are created. |
| private AdminPermission getAdminPermission(Bundle bundle, String action) { |
| synchronized (adminPermissions) { |
| Long ID = new Long(bundle.getBundleId()); |
| HashMap bundlePermissions = (HashMap) adminPermissions.get(ID); |
| if (bundlePermissions == null) { |
| bundlePermissions = new HashMap(); |
| adminPermissions.put(ID, bundlePermissions); |
| } |
| AdminPermission result = (AdminPermission) bundlePermissions.get(action); |
| if (result == null) { |
| result = new AdminPermission(bundle, action); |
| bundlePermissions.put(action, result); |
| } |
| return result; |
| } |
| } |
| |
| /** |
| * This is necessary for running from a JXE, otherwise the SecurityManager |
| * is set much later than we would like! |
| */ |
| protected void installSecurityManager() { |
| String securityManager = FrameworkProperties.getProperty(PROP_EQUINOX_SECURITY, FrameworkProperties.getProperty("java.security.manager")); //$NON-NLS-1$ |
| if (securityManager != null) { |
| SecurityManager sm = System.getSecurityManager(); |
| if (sm == null) { |
| if (securityManager.length() == 0) |
| sm = new SecurityManager(); // use the default one from java |
| else if (securityManager.equals(SECURITY_OSGI)) |
| sm = new EquinoxSecurityManager(); // use an OSGi enabled manager that understands postponed conditions |
| else { |
| // try to use a specific classloader by classname |
| try { |
| Class clazz = Class.forName(securityManager); |
| sm = (SecurityManager) clazz.newInstance(); |
| } catch (ClassNotFoundException e) { |
| // do nothing |
| } catch (ClassCastException e) { |
| // do nothing |
| } catch (InstantiationException e) { |
| // do nothing |
| } catch (IllegalAccessException e) { |
| // do nothing |
| } |
| } |
| if (sm == null) |
| throw new NoClassDefFoundError(securityManager); |
| if (Debug.DEBUG_SECURITY) |
| Debug.println("Setting SecurityManager to: " + sm); //$NON-NLS-1$ |
| System.setSecurityManager(sm); |
| return; |
| } |
| } |
| } |
| |
| /** |
| * Deliver a FrameworkEvent. |
| * |
| * @param type |
| * FrameworkEvent type. |
| * @param bundle |
| * Affected bundle or null for system bundle. |
| * @param throwable |
| * Related exception or null. |
| */ |
| public void publishFrameworkEvent(int type, org.osgi.framework.Bundle bundle, Throwable throwable) { |
| if (frameworkEvent != null) { |
| if (bundle == null) |
| bundle = systemBundle; |
| final FrameworkEvent event = new FrameworkEvent(type, bundle, throwable); |
| if (System.getSecurityManager() == null) { |
| publishFrameworkEventPrivileged(event); |
| } else { |
| AccessController.doPrivileged(new PrivilegedAction() { |
| public Object run() { |
| publishFrameworkEventPrivileged(event); |
| return null; |
| } |
| }); |
| } |
| } |
| } |
| |
| public void publishFrameworkEventPrivileged(FrameworkEvent event) { |
| /* if the event is an error then it should be logged */ |
| if (event.getType() == FrameworkEvent.ERROR) { |
| FrameworkLog frameworkLog = adaptor.getFrameworkLog(); |
| if (frameworkLog != null) |
| frameworkLog.log(event); |
| } |
| /* queue to hold set of listeners */ |
| ListenerQueue listeners = new ListenerQueue(eventManager); |
| /* queue to hold set of BundleContexts w/ listeners */ |
| ListenerQueue contexts = new ListenerQueue(eventManager); |
| /* synchronize while building the listener list */ |
| synchronized (frameworkEvent) { |
| /* add set of BundleContexts w/ listeners to queue */ |
| contexts.queueListeners(frameworkEvent.entrySet(), this); |
| /* synchronously dispatch to populate listeners queue */ |
| contexts.dispatchEventSynchronous(FRAMEWORKEVENT, listeners); |
| } |
| /* dispatch event to set of listeners */ |
| listeners.dispatchEventAsynchronous(FRAMEWORKEVENT, event); |
| } |
| |
| /** |
| * Deliver a BundleEvent to SynchronousBundleListeners (synchronous). and |
| * BundleListeners (asynchronous). |
| * |
| * @param type |
| * BundleEvent type. |
| * @param bundle |
| * Affected bundle or null. |
| */ |
| public void publishBundleEvent(int type, org.osgi.framework.Bundle bundle) { |
| if ((bundleEventSync != null) || (bundleEvent != null)) { |
| final BundleEvent event = new BundleEvent(type, bundle); |
| if (System.getSecurityManager() == null) { |
| publishBundleEventPrivileged(event); |
| } else { |
| AccessController.doPrivileged(new PrivilegedAction() { |
| public Object run() { |
| publishBundleEventPrivileged(event); |
| return null; |
| } |
| }); |
| } |
| } |
| } |
| |
| public void publishBundleEventPrivileged(BundleEvent event) { |
| /* |
| * We must collect the snapshots of the sync and async listeners |
| * BEFORE we dispatch the event. |
| */ |
| /* Collect snapshot of SynchronousBundleListeners */ |
| ListenerQueue listenersSync = null; |
| if (bundleEventSync != null) { |
| /* queue to hold set of listeners */ |
| listenersSync = new ListenerQueue(eventManager); |
| /* queue to hold set of BundleContexts w/ listeners */ |
| ListenerQueue contexts = new ListenerQueue(eventManager); |
| /* synchronize while building the listener list */ |
| synchronized (bundleEventSync) { |
| /* add set of BundleContexts w/ listeners to queue */ |
| contexts.queueListeners(bundleEventSync.entrySet(), this); |
| /* synchronously dispatch to populate listeners queue */ |
| contexts.dispatchEventSynchronous(BUNDLEEVENTSYNC, listenersSync); |
| } |
| } |
| /* Collect snapshot of BundleListeners; only if the event is NOT STARTING or STOPPING or LAZY_ACTIVATION */ |
| ListenerQueue listenersAsync = null; |
| if (bundleEvent != null && (event.getType() & (BundleEvent.STARTING | BundleEvent.STOPPING | BundleEvent.LAZY_ACTIVATION)) == 0) { |
| /* queue to hold set of listeners */ |
| listenersAsync = new ListenerQueue(eventManager); |
| /* queue to hold set of BundleContexts w/ listeners */ |
| ListenerQueue contexts = new ListenerQueue(eventManager); |
| /* synchronize while building the listener list */ |
| synchronized (bundleEvent) { |
| /* add set of BundleContexts w/ listeners to queue */ |
| contexts.queueListeners(bundleEvent.entrySet(), this); |
| /* synchronously dispatch to populate listeners queue */ |
| contexts.dispatchEventSynchronous(BUNDLEEVENT, listenersAsync); |
| } |
| } |
| /* Dispatch BundleEvent to SynchronousBundleListeners */ |
| if (listenersSync != null) { |
| listenersSync.dispatchEventSynchronous(BUNDLEEVENTSYNC, event); |
| } |
| /* Dispatch BundleEvent to BundleListeners */ |
| if (listenersAsync != null) { |
| listenersAsync.dispatchEventAsynchronous(BUNDLEEVENT, event); |
| } |
| } |
| |
| public ListenerQueue newListenerQueue() { |
| return new ListenerQueue(eventManager); |
| } |
| |
| /** |
| * Top level event dispatcher for the framework. |
| * |
| * @param l |
| * BundleContext for receiving bundle |
| * @param lo |
| * BundleContext for receiving bundle |
| * @param action |
| * Event class type |
| * @param object |
| * ListenerQueue to populate |
| */ |
| public void dispatchEvent(Object l, Object lo, int action, Object object) { |
| try { |
| BundleContextImpl context = (BundleContextImpl) l; |
| if (context.isValid()) /* if context still valid */{ |
| ListenerQueue queue = (ListenerQueue) object; |
| switch (action) { |
| case BUNDLEEVENT : { |
| queue.queueListeners(context.bundleEvent.entrySet(), context); |
| break; |
| } |
| case BUNDLEEVENTSYNC : { |
| queue.queueListeners(context.bundleEventSync.entrySet(), context); |
| break; |
| } |
| case FRAMEWORKEVENT : { |
| queue.queueListeners(context.frameworkEvent.entrySet(), context); |
| break; |
| } |
| default : { |
| throw new InternalError(); |
| } |
| } |
| } |
| } catch (Throwable t) { |
| if (Debug.DEBUG_GENERAL) { |
| Debug.println("Exception in Top level event dispatcher: " + t.getMessage()); //$NON-NLS-1$ |
| Debug.printStackTrace(t); |
| } |
| // allow the adaptor to handle this unexpected error |
| adaptor.handleRuntimeError(t); |
| publisherror: { |
| if (action == FRAMEWORKEVENT) { |
| FrameworkEvent event = (FrameworkEvent) object; |
| if (event.getType() == FrameworkEvent.ERROR) { |
| break publisherror; /* avoid infinite loop */ |
| } |
| } |
| BundleContextImpl context = (BundleContextImpl) l; |
| publishFrameworkEvent(FrameworkEvent.ERROR, context.bundle, t); |
| } |
| } |
| } |
| |
| private void initializeContextFinder() { |
| Thread current = Thread.currentThread(); |
| try { |
| ClassLoader parent = null; |
| // check property for specified parent |
| String type = FrameworkProperties.getProperty(PROP_CONTEXTCLASSLOADER_PARENT); |
| if (CONTEXTCLASSLOADER_PARENT_APP.equals(type)) |
| parent = ClassLoader.getSystemClassLoader(); |
| else if (CONTEXTCLASSLOADER_PARENT_BOOT.equals(type)) |
| parent = null; |
| else if (CONTEXTCLASSLOADER_PARENT_FWK.equals(type)) |
| parent = Framework.class.getClassLoader(); |
| else if (CONTEXTCLASSLOADER_PARENT_EXT.equals(type)) { |
| ClassLoader appCL = ClassLoader.getSystemClassLoader(); |
| if (appCL != null) |
| parent = appCL.getParent(); |
| } else { // default is ccl (null or any other value will use ccl) |
| parent = current.getContextClassLoader(); |
| } |
| contextFinder = new ContextFinder(parent); |
| current.setContextClassLoader(contextFinder); |
| return; |
| } catch (Exception e) { |
| FrameworkLogEntry entry = new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.INFO, 0, NLS.bind(Msg.CANNOT_SET_CONTEXTFINDER, null), 0, e, null); |
| adaptor.getFrameworkLog().log(entry); |
| } |
| |
| } |
| |
| public static Field getField(Class clazz, Class type, boolean instance) { |
| Field[] fields = clazz.getDeclaredFields(); |
| for (int i = 0; i < fields.length; i++) { |
| boolean isStatic = Modifier.isStatic(fields[i].getModifiers()); |
| if (instance != isStatic && fields[i].getType().equals(type)) { |
| fields[i].setAccessible(true); |
| return fields[i]; |
| } |
| } |
| return null; |
| } |
| |
| private void installContentHandlerFactory(BundleContext context, FrameworkAdaptor frameworkAdaptor) { |
| ContentHandlerFactory chf = new ContentHandlerFactory(context, frameworkAdaptor); |
| try { |
| // first try the standard way |
| URLConnection.setContentHandlerFactory(chf); |
| } catch (Error err) { |
| // ok we failed now use more drastic means to set the factory |
| try { |
| forceContentHandlerFactory(chf); |
| } catch (Exception ex) { |
| // this is unexpected, log the exception and throw the original error |
| adaptor.getFrameworkLog().log(new FrameworkEvent(FrameworkEvent.ERROR, context.getBundle(), ex)); |
| throw err; |
| } |
| } |
| contentHandlerFactory = chf; |
| } |
| |
| private static void forceContentHandlerFactory(ContentHandlerFactory chf) throws Exception { |
| Field factoryField = getField(URLConnection.class, java.net.ContentHandlerFactory.class, false); |
| if (factoryField == null) |
| throw new Exception("Could not find ContentHandlerFactory field"); //$NON-NLS-1$ |
| synchronized (URLConnection.class) { |
| java.net.ContentHandlerFactory factory = (java.net.ContentHandlerFactory) factoryField.get(null); |
| // doing a null check here just in case, but it would be really strange if it was null, |
| // because we failed to set the factory normally!! |
| |
| if (factory != null) { |
| try { |
| factory.getClass().getMethod("isMultiplexing", (Class[]) null); //$NON-NLS-1$ |
| Method register = factory.getClass().getMethod("register", new Class[] {Object.class}); //$NON-NLS-1$ |
| register.invoke(factory, new Object[] {chf}); |
| } catch (NoSuchMethodException e) { |
| // current factory does not support multiplexing, ok we'll wrap it |
| chf.setParentFactory(factory); |
| factory = chf; |
| } |
| } |
| // null out the field so that we can successfully call setContentHandlerFactory |
| factoryField.set(null, null); |
| // always attempt to clear the handlers cache |
| // This allows an optomization for the single framework use-case |
| resetContentHandlers(); |
| URLConnection.setContentHandlerFactory(factory); |
| } |
| } |
| |
| private void uninstallContentHandlerFactory() { |
| try { |
| Field factoryField = getField(URLConnection.class, java.net.ContentHandlerFactory.class, false); |
| if (factoryField == null) |
| return; // oh well, we tried. |
| synchronized (URLConnection.class) { |
| java.net.ContentHandlerFactory factory = (java.net.ContentHandlerFactory) factoryField.get(null); |
| |
| if (factory == contentHandlerFactory) { |
| factory = (java.net.ContentHandlerFactory) contentHandlerFactory.designateSuccessor(); |
| } else { |
| Method unregister = factory.getClass().getMethod("unregister", new Class[] {Object.class}); //$NON-NLS-1$ |
| unregister.invoke(factory, new Object[] {contentHandlerFactory}); |
| } |
| // null out the field so that we can successfully call setContentHandlerFactory |
| factoryField.set(null, null); |
| // always attempt to clear the handlers cache |
| // This allows an optomization for the single framework use-case |
| // Note that the call to setContentHandlerFactory below may clear this cache |
| // but we want to be sure to clear it here just incase the parent is null. |
| // In this case the call below would not occur. |
| // Also it appears most java libraries actually do not clear the cache |
| // when setContentHandlerFactory is called, go figure!! |
| resetContentHandlers(); |
| if (factory != null) |
| URLConnection.setContentHandlerFactory(factory); |
| } |
| } catch (Exception e) { |
| // ignore and continue closing the framework |
| } |
| } |
| |
| private static void resetContentHandlers() throws IllegalAccessException { |
| Field handlersField = getField(URLConnection.class, Hashtable.class, false); |
| if (handlersField != null) { |
| Hashtable handlers = (Hashtable) handlersField.get(null); |
| if (handlers != null) |
| handlers.clear(); |
| } |
| } |
| |
| private void installURLStreamHandlerFactory(BundleContext context, FrameworkAdaptor frameworkAdaptor) { |
| StreamHandlerFactory shf = new StreamHandlerFactory(context, frameworkAdaptor); |
| try { |
| // first try the standard way |
| URL.setURLStreamHandlerFactory(shf); |
| } catch (Error err) { |
| try { |
| // ok we failed now use more drastic means to set the factory |
| forceURLStreamHandlerFactory(shf); |
| } catch (Exception ex) { |
| adaptor.getFrameworkLog().log(new FrameworkEvent(FrameworkEvent.ERROR, context.getBundle(), ex)); |
| throw err; |
| } |
| } |
| streamHandlerFactory = shf; |
| } |
| |
| private static void forceURLStreamHandlerFactory(StreamHandlerFactory shf) throws Exception { |
| Field factoryField = getField(URL.class, URLStreamHandlerFactory.class, false); |
| if (factoryField == null) |
| throw new Exception("Could not find URLStreamHandlerFactory field"); //$NON-NLS-1$ |
| // look for a lock to synchronize on |
| Object lock = getURLStreamHandlerFactoryLock(); |
| synchronized (lock) { |
| URLStreamHandlerFactory factory = (URLStreamHandlerFactory) factoryField.get(null); |
| // doing a null check here just in case, but it would be really strange if it was null, |
| // because we failed to set the factory normally!! |
| if (factory != null) { |
| try { |
| factory.getClass().getMethod("isMultiplexing", (Class[]) null); //$NON-NLS-1$ |
| Method register = factory.getClass().getMethod("register", new Class[] {Object.class}); //$NON-NLS-1$ |
| register.invoke(factory, new Object[] {shf}); |
| } catch (NoSuchMethodException e) { |
| // current factory does not support multiplexing, ok we'll wrap it |
| shf.setParentFactory(factory); |
| factory = shf; |
| } |
| } |
| factoryField.set(null, null); |
| // always attempt to clear the handlers cache |
| // This allows an optomization for the single framework use-case |
| resetURLStreamHandlers(); |
| URL.setURLStreamHandlerFactory(factory); |
| } |
| } |
| |
| private void uninstallURLStreamHandlerFactory() { |
| try { |
| Field factoryField = getField(URL.class, URLStreamHandlerFactory.class, false); |
| if (factoryField == null) |
| return; // oh well, we tried |
| Object lock = getURLStreamHandlerFactoryLock(); |
| synchronized (lock) { |
| URLStreamHandlerFactory factory = (URLStreamHandlerFactory) factoryField.get(null); |
| if (factory == streamHandlerFactory) { |
| factory = (URLStreamHandlerFactory) streamHandlerFactory.designateSuccessor(); |
| } else { |
| Method unregister = factory.getClass().getMethod("unregister", new Class[] {Object.class}); //$NON-NLS-1$ |
| unregister.invoke(factory, new Object[] {streamHandlerFactory}); |
| } |
| factoryField.set(null, null); |
| // always attempt to clear the handlers cache |
| // This allows an optomization for the single framework use-case |
| // Note that the call to setURLStreamHandlerFactory below may clear this cache |
| // but we want to be sure to clear it here just in case the parent is null. |
| // In this case the call below would not occur. |
| resetURLStreamHandlers(); |
| if (factory != null) |
| URL.setURLStreamHandlerFactory(factory); |
| } |
| } catch (Exception e) { |
| // ignore and continue closing the framework |
| } |
| } |
| |
| private static Object getURLStreamHandlerFactoryLock() throws IllegalAccessException { |
| Object lock; |
| try { |
| Field streamHandlerLockField = URL.class.getDeclaredField("streamHandlerLock"); //$NON-NLS-1$ |
| streamHandlerLockField.setAccessible(true); |
| lock = streamHandlerLockField.get(null); |
| } catch (NoSuchFieldException noField) { |
| // could not find the lock, lets sync on the class object |
| lock = URL.class; |
| } |
| return lock; |
| } |
| |
| private static void resetURLStreamHandlers() throws IllegalAccessException { |
| Field handlersField = getField(URL.class, Hashtable.class, false); |
| if (handlersField != null) { |
| Hashtable handlers = (Hashtable) handlersField.get(null); |
| if (handlers != null) |
| handlers.clear(); |
| } |
| } |
| |
| /* |
| * (non-Javadoc) |
| * @see java.lang.Runnable#run() |
| * This thread monitors the framework active status and terminates when the framework is |
| * shutdown. This is needed to ensure the VM does not exist because of the lack of a |
| * non-daemon thread (bug 215730) |
| */ |
| public void run() { |
| synchronized (this) { |
| while (active) |
| try { |
| this.wait(1000); |
| } catch (InterruptedException e) { |
| // do nothing |
| } |
| } |
| } |
| |
| void setForcedRestart(boolean forcedRestart) { |
| this.forcedRestart = forcedRestart; |
| } |
| |
| boolean isForcedRestart() { |
| return forcedRestart; |
| } |
| |
| public FrameworkEvent waitForStop(long timeout) throws InterruptedException { |
| boolean waitForEver = timeout == 0; |
| long start = System.currentTimeMillis(); |
| long timeLeft = timeout; |
| synchronized (this) { |
| FrameworkEvent[] event = shutdownEvent; |
| while (event != null && event[0] == null) { |
| this.wait(timeLeft); |
| if (!waitForEver) { |
| timeLeft = start + timeout - System.currentTimeMillis(); |
| // break if we are passed the timeout |
| if (timeLeft <= 0) |
| break; |
| } |
| } |
| if (event == null || event[0] == null) |
| return new FrameworkEvent(FrameworkEvent.WAIT_TIMEDOUT, systemBundle, null); |
| return event[0]; |
| } |
| } |
| |
| /** |
| * Used by ServiceReferenceImpl for isAssignableTo |
| * @param registrant Bundle registering service |
| * @param client Bundle desiring to use service |
| * @param className class name to use |
| * @param serviceClass class of original service object |
| * @return true if assignable given package wiring |
| */ |
| public boolean isServiceAssignableTo(Bundle registrant, Bundle client, String className, Class serviceClass) { |
| // always return false for fragments |
| AbstractBundle consumer = (AbstractBundle) client; |
| if (consumer.isFragment()) |
| return false; |
| // 1) if the registrant == consumer always return true |
| AbstractBundle producer = (AbstractBundle) registrant; |
| if (consumer == producer) |
| return true; |
| // 2) get the package name from the specified className |
| String pkgName = BundleLoader.getPackageName(className); |
| if (pkgName.startsWith("java.")) //$NON-NLS-1$ |
| return true; |
| BundleLoader producerBL = producer.getBundleLoader(); |
| if (producerBL == null) |
| return false; |
| BundleLoader consumerBL = consumer.getBundleLoader(); |
| if (consumerBL == null) |
| return false; |
| // 3) for the specified bundle, find the wiring for the package. If no wiring is found return true |
| PackageSource consumerSource = consumerBL.getPackageSource(pkgName); |
| if (consumerSource == null) |
| return true; |
| // work around the issue when the package is in the EE and we delegate to boot for that package |
| if (isBootDelegationPackage(pkgName)) { |
| SystemBundleLoader systemLoader = (SystemBundleLoader) systemBundle.getBundleLoader(); |
| if (systemLoader.isEEPackage(pkgName)) |
| return true; // in this case we have a common source from the EE |
| } |
| // 4) For the registrant bundle, find the wiring for the package. |
| PackageSource producerSource = producerBL.getPackageSource(pkgName); |
| if (producerSource == null) { |
| // 5) If no wiring is found for the registrant bundle then find the wiring for the classloader of the service object. If no wiring is found return false. |
| producerSource = getPackageSource(serviceClass, pkgName); |
| if (producerSource == null) |
| return false; |
| } |
| // 6) If the two wirings found are equal then return true; otherwise return false. |
| return producerSource.hasCommonSource(consumerSource); |
| } |
| |
| private PackageSource getPackageSource(Class serviceClass, String pkgName) { |
| if (serviceClass == null) |
| return null; |
| AbstractBundle serviceBundle = (AbstractBundle) packageAdmin.getBundle(serviceClass); |
| if (serviceBundle == null) |
| return null; |
| BundleLoader producerBL = serviceBundle.getBundleLoader(); |
| if (producerBL == null) |
| return null; |
| PackageSource producerSource = producerBL.getPackageSource(pkgName); |
| if (producerSource != null) |
| return producerSource; |
| // try the interfaces |
| Class[] interfaces = serviceClass.getInterfaces(); |
| // note that getInterfaces never returns null |
| for (int i = 0; i < interfaces.length; i++) { |
| producerSource = getPackageSource(interfaces[i], pkgName); |
| if (producerSource != null) |
| return producerSource; |
| } |
| // try super class |
| return getPackageSource(serviceClass.getSuperclass(), pkgName); |
| } |
| |
| public boolean isBootDelegationPackage(String name) { |
| if (bootDelegateAll) |
| return true; |
| if (bootDelegation != null) |
| for (int i = 0; i < bootDelegation.length; i++) |
| if (name.equals(bootDelegation[i])) |
| return true; |
| if (bootDelegationStems != null) |
| for (int i = 0; i < bootDelegationStems.length; i++) |
| if (name.startsWith(bootDelegationStems[i])) |
| return true; |
| return false; |
| } |
| |
| SignedContentFactory getSignedContentFactory() { |
| ServiceTracker currentTracker = signedContentFactory; |
| return (SignedContentFactory) (currentTracker == null ? null : currentTracker.getService()); |
| } |
| |
| ContextFinder getContextFinder() { |
| return contextFinder; |
| } |
| } |