Skip to main content

This CGIT instance is deprecated, and repositories have been moved to Gitlab or Github. See the repository descriptions for specific locations.

aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core')
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/AbstractBundle.java1545
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/AliasMapper.java155
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/BundleContextImpl.java974
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/BundleFragment.java334
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/BundleHost.java686
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/BundleRepository.java196
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/BundleResourceHandler.java304
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/BundleSource.java45
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/BundleURLConnection.java131
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/ConsoleManager.java82
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/Constants.java254
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/CoreResolverHookFactory.java216
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/EquinoxLauncher.java347
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/ExportedPackageImpl.java99
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/FilterImpl.java1751
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/Framework.java2007
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/InternalSystemBundle.java433
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/ManifestLocalization.java241
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/PackageAdminImpl.java766
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/ReferenceInputStream.java38
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/StartLevelEvent.java82
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/StartLevelManager.java678
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/SystemBundleActivator.java110
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/UniversalUniqueIdentifier.java270
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/Util.java208
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/osname.aliases46
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/processor.aliases28
27 files changed, 12026 insertions, 0 deletions
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/AbstractBundle.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/AbstractBundle.java
new file mode 100644
index 000000000..d2b51fd00
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/AbstractBundle.java
@@ -0,0 +1,1545 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2012 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.net.URL;
+import java.net.URLConnection;
+import java.security.*;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.util.*;
+import org.eclipse.osgi.framework.adaptor.*;
+import org.eclipse.osgi.framework.debug.Debug;
+import org.eclipse.osgi.framework.util.KeyedElement;
+import org.eclipse.osgi.internal.loader.BundleLoader;
+import org.eclipse.osgi.internal.permadmin.EquinoxSecurityManager;
+import org.eclipse.osgi.service.resolver.BundleDescription;
+import org.eclipse.osgi.service.resolver.ResolverError;
+import org.eclipse.osgi.signedcontent.*;
+import org.eclipse.osgi.util.NLS;
+import org.osgi.framework.*;
+import org.osgi.framework.hooks.bundle.CollisionHook;
+import org.osgi.framework.startlevel.BundleStartLevel;
+import org.osgi.framework.wiring.*;
+
+/**
+ * This object is given out to bundles and wraps the internal Bundle object. It
+ * is destroyed when a bundle is uninstalled and reused if a bundle is updated.
+ * This class is abstract and is extended by BundleHost and BundleFragment.
+ */
+public abstract class AbstractBundle implements Bundle, Comparable<Bundle>, KeyedElement, BundleStartLevel, BundleReference, BundleRevisions {
+ private final static long STATE_CHANGE_TIMEOUT;
+ static {
+ long stateChangeWait = 5000;
+ try {
+ String prop = FrameworkProperties.getProperty("equinox.statechange.timeout"); //$NON-NLS-1$
+ if (prop != null)
+ stateChangeWait = Long.parseLong(prop);
+ } catch (Throwable t) {
+ // use default 5000
+ stateChangeWait = 5000;
+ }
+ STATE_CHANGE_TIMEOUT = stateChangeWait;
+ }
+ /** The Framework this bundle is part of */
+ protected final Framework framework;
+ /** The state of the bundle. */
+ protected volatile int state;
+ /** A flag to denote whether a bundle state change is in progress */
+ protected volatile Thread stateChanging;
+ /** Bundle's BundleData object */
+ protected BundleData bundledata;
+ /** Internal object used for state change synchronization */
+ protected final Object statechangeLock = new Object();
+ /** ProtectionDomain for the bundle */
+ protected BundleProtectionDomain domain;
+
+ volatile protected ManifestLocalization manifestLocalization = null;
+
+ /**
+ * Bundle object constructor. This constructor should not perform any real
+ * work.
+ *
+ * @param bundledata
+ * BundleData for this bundle
+ * @param framework
+ * Framework this bundle is running in
+ */
+ protected static AbstractBundle createBundle(BundleData bundledata, Framework framework, boolean setBundle) throws BundleException {
+ AbstractBundle result;
+ if ((bundledata.getType() & BundleData.TYPE_FRAGMENT) > 0)
+ result = new BundleFragment(bundledata, framework);
+ else
+ result = new BundleHost(bundledata, framework);
+ if (setBundle)
+ bundledata.setBundle(result);
+ return result;
+ }
+
+ /**
+ * Bundle object constructor. This constructor should not perform any real
+ * work.
+ *
+ * @param bundledata
+ * BundleData for this bundle
+ * @param framework
+ * Framework this bundle is running in
+ */
+ protected AbstractBundle(BundleData bundledata, Framework framework) {
+ state = INSTALLED;
+ stateChanging = null;
+ this.bundledata = bundledata;
+ this.framework = framework;
+ }
+
+ /**
+ * Load the bundle.
+ */
+ protected abstract void load();
+
+ /**
+ * Reload from a new bundle. This method must be called while holding the
+ * bundles lock.
+ *
+ * @param newBundle
+ * Dummy Bundle which contains new data.
+ * @return true if an exported package is "in use". i.e. it has been
+ * imported by a bundle
+ */
+ protected abstract boolean reload(AbstractBundle newBundle);
+
+ /**
+ * Refresh the bundle. This is called by Framework.refreshPackages. This
+ * method must be called while holding the bundles lock.
+ * this.loader.unimportPackages must have already been called before
+ * calling this method!
+ */
+ protected abstract void refresh();
+
+ /**
+ * Unload the bundle. This method must be called while holding the bundles
+ * lock.
+ *
+ * @return true if an exported package is "in use". i.e. it has been
+ * imported by a bundle
+ */
+ protected abstract boolean unload();
+
+ /**
+ * Close the the Bundle's file.
+ *
+ */
+ protected void close() {
+ if (Debug.DEBUG_GENERAL) {
+ if ((state & (INSTALLED)) == 0) {
+ Debug.println("Bundle.close called when state != INSTALLED: " + this); //$NON-NLS-1$
+ Debug.printStackTrace(new Exception("Stack trace")); //$NON-NLS-1$
+ }
+ }
+ state = UNINSTALLED;
+ }
+
+ /**
+ * Load and instantiate bundle's BundleActivator class
+ */
+ protected BundleActivator loadBundleActivator() throws BundleException {
+ /* load Bundle's BundleActivator if it has one */
+ String activatorClassName = bundledata.getActivator();
+ if (activatorClassName != null) {
+ try {
+ Class<?> activatorClass = loadClass(activatorClassName, false);
+ /* Create the activator for the bundle */
+ return (BundleActivator) (activatorClass.newInstance());
+ } catch (Throwable t) {
+ if (Debug.DEBUG_GENERAL) {
+ Debug.printStackTrace(t);
+ }
+ throw new BundleException(NLS.bind(Msg.BUNDLE_INVALID_ACTIVATOR_EXCEPTION, activatorClassName, bundledata.getSymbolicName()), BundleException.ACTIVATOR_ERROR, t);
+ }
+ }
+ return (null);
+ }
+
+ /**
+ * This method loads a class from the bundle.
+ *
+ * @param name
+ * the name of the desired Class.
+ * @param checkPermission
+ * indicates whether a permission check should be done.
+ * @return the resulting Class
+ * @exception java.lang.ClassNotFoundException
+ * if the class definition was not found.
+ */
+ protected abstract Class<?> loadClass(String name, boolean checkPermission) throws ClassNotFoundException;
+
+ /**
+ * Returns the current state of the bundle.
+ *
+ * A bundle can only be in one state at any time.
+ *
+ * @return bundle's state.
+ */
+ public int getState() {
+ return (state);
+ }
+
+ public Framework getFramework() {
+ return framework;
+ }
+
+ /**
+ * Return true if the bundle is starting or active.
+ *
+ */
+ protected boolean isActive() {
+ return ((state & (ACTIVE | STARTING)) != 0);
+ }
+
+ boolean isLazyStart() {
+ int status = bundledata.getStatus();
+ return (status & Constants.BUNDLE_ACTIVATION_POLICY) != 0 && (status & Constants.BUNDLE_LAZY_START) != 0;
+ }
+
+ /**
+ * Return true if the bundle is resolved.
+ *
+ */
+ public boolean isResolved() {
+ return (state & (INSTALLED | UNINSTALLED)) == 0;
+ }
+
+ /**
+ * Start this bundle.
+ *
+ * If the current start level is less than this bundle's start level, then
+ * the Framework must persistently mark this bundle as started and delay
+ * the starting of this bundle until the Framework's current start level
+ * becomes equal or more than the bundle's start level.
+ * <p>
+ * Otherwise, the following steps are required to start a bundle:
+ * <ol>
+ * <li>If the bundle is {@link #UNINSTALLED}then an <code>IllegalStateException</code>
+ * is thrown.
+ * <li>If the bundle is {@link #ACTIVE}or {@link #STARTING}then this
+ * method returns immediately.
+ * <li>If the bundle is {@link #STOPPING}then this method may wait for
+ * the bundle to return to the {@link #RESOLVED}state before continuing.
+ * If this does not occur in a reasonable time, a {@link BundleException}
+ * is thrown to indicate the bundle was unable to be started.
+ * <li>If the bundle is not {@link #RESOLVED}, an attempt is made to
+ * resolve the bundle. If the bundle cannot be resolved, a
+ * {@link BundleException}is thrown.
+ * <li>The state of the bundle is set to {@link #STARTING}.
+ * <li>The {@link BundleActivator#start(BundleContext) start}method of the bundle's
+ * {@link BundleActivator}, if one is specified, is called. If the
+ * {@link BundleActivator}is invalid or throws an exception, the state of
+ * the bundle is set back to {@link #RESOLVED}, the bundle's listeners, if
+ * any, are removed, service's registered by the bundle, if any, are
+ * unregistered, and service's used by the bundle, if any, are released. A
+ * {@link BundleException}is then thrown.
+ * <li>It is recorded that this bundle has been started, so that when the
+ * framework is restarted, this bundle will be automatically started.
+ * <li>The state of the bundle is set to {@link #ACTIVE}.
+ * <li>A {@link BundleEvent}of type {@link BundleEvent#STARTED}is
+ * broadcast.
+ * </ol>
+ *
+ * <h5>Preconditons</h5>
+ * <ul>
+ * <li>getState() in {{@link #INSTALLED},{@link #RESOLVED}}.
+ * </ul>
+ * <h5>Postconditons, no exceptions thrown</h5>
+ * <ul>
+ * <li>getState() in {{@link #ACTIVE}}.
+ * <li>{@link BundleActivator#start(BundleContext) BundleActivator.start}has been called
+ * and did not throw an exception.
+ * </ul>
+ * <h5>Postconditions, when an exception is thrown</h5>
+ * <ul>
+ * <li>getState() not in {{@link #STARTING},{@link #ACTIVE}}.
+ * </ul>
+ *
+ * @exception BundleException
+ * If the bundle couldn't be started. This could be because
+ * a code dependency could not be resolved or the specified
+ * BundleActivator could not be loaded or threw an
+ * exception.
+ * @exception java.lang.IllegalStateException
+ * If the bundle has been uninstalled or the bundle tries to
+ * change its own state.
+ * @exception java.lang.SecurityException
+ * If the caller does not have {@link AdminPermission}
+ * permission and the Java runtime environment supports
+ * permissions.
+ */
+ public void start() throws BundleException {
+ start(0);
+ }
+
+ public void start(int options) throws BundleException {
+ framework.checkAdminPermission(this, AdminPermission.EXECUTE);
+ checkValid();
+ beginStateChange();
+ try {
+ startWorker(options);
+ } finally {
+ completeStateChange();
+ }
+ }
+
+ /**
+ * Internal worker to start a bundle.
+ *
+ * @param options
+ */
+ protected abstract void startWorker(int options) throws BundleException;
+
+ /**
+ * This method does the following
+ * <ol>
+ * <li> Return false if the bundle is a fragment
+ * <li> Return false if the bundle is not at the correct start-level
+ * <li> Return false if the bundle is not persistently marked for start
+ * <li> Return true if the bundle's activation policy is persistently ignored
+ * <li> Return true if the bundle does not define an activation policy
+ * <li> Transition to STARTING state and Fire LAZY_ACTIVATION event
+ * <li> Return false
+ * </ol>
+ * @return true if the bundle should be resumed
+ */
+ protected boolean readyToResume() {
+ return false;
+ }
+
+ /**
+ * Start this bundle w/o marking is persistently started.
+ *
+ * <p>
+ * The following steps are followed to start a bundle:
+ * <ol>
+ * <li>If the bundle is {@link #UNINSTALLED}then an <code>IllegalStateException</code>
+ * is thrown.
+ * <li>If the bundle is {@link #ACTIVE}or {@link #STARTING}then this
+ * method returns immediately.
+ * <li>If the bundle is {@link #STOPPING}then this method may wait for
+ * the bundle to return to the {@link #RESOLVED}state before continuing.
+ * If this does not occur in a reasonable time, a {@link BundleException}
+ * is thrown to indicate the bundle was unable to be started.
+ * <li>If the bundle is not {@link #RESOLVED}, an attempt is made to
+ * resolve the bundle. If the bundle cannot be resolved, a
+ * {@link BundleException}is thrown.
+ * <li>The state of the bundle is set to {@link #STARTING}.
+ * <li>The {@link BundleActivator#start(BundleContext) start}method of the bundle's
+ * {@link BundleActivator}, if one is specified, is called. If the
+ * {@link BundleActivator}is invalid or throws an exception, the state of
+ * the bundle is set back to {@link #RESOLVED}, the bundle's listeners, if
+ * any, are removed, service's registered by the bundle, if any, are
+ * unregistered, and service's used by the bundle, if any, are released. A
+ * {@link BundleException}is then thrown.
+ * <li>The state of the bundle is set to {@link #ACTIVE}.
+ * <li>A {@link BundleEvent}of type {@link BundleEvent#STARTED}is
+ * broadcast.
+ * </ol>
+ *
+ * <h5>Preconditons</h5>
+ * <ul>
+ * <li>getState() in {{@link #INSTALLED},{@link #RESOLVED}}.
+ * </ul>
+ * <h5>Postconditons, no exceptions thrown</h5>
+ * <ul>
+ * <li>getState() in {{@link #ACTIVE}}.
+ * <li>{@link BundleActivator#start(BundleContext) BundleActivator.start}has been called
+ * and did not throw an exception.
+ * </ul>
+ * <h5>Postconditions, when an exception is thrown</h5>
+ * <ul>
+ * <li>getState() not in {{@link #STARTING},{@link #ACTIVE}}.
+ * </ul>
+ *
+ * @exception BundleException
+ * If the bundle couldn't be started. This could be because
+ * a code dependency could not be resolved or the specified
+ * BundleActivator could not be loaded or threw an
+ * exception.
+ * @exception java.lang.IllegalStateException
+ * If the bundle tries to change its own state.
+ */
+ protected void resume() throws BundleException {
+ if (state == UNINSTALLED) {
+ return;
+ }
+ beginStateChange();
+ try {
+ if (readyToResume())
+ startWorker(START_TRANSIENT);
+ } finally {
+ completeStateChange();
+ }
+ }
+
+ /**
+ * Stop this bundle.
+ *
+ * Any services registered by this bundle will be unregistered. Any
+ * services used by this bundle will be released. Any listeners registered
+ * by this bundle will be removed.
+ *
+ * <p>
+ * The following steps are followed to stop a bundle:
+ * <ol>
+ * <li>If the bundle is {@link #UNINSTALLED}then an <code>IllegalStateException</code>
+ * is thrown.
+ * <li>If the bundle is {@link #STOPPING},{@link #RESOLVED}, or
+ * {@link #INSTALLED}then this method returns immediately.
+ * <li>If the bundle is {@link #STARTING}then this method may wait for
+ * the bundle to reach the {@link #ACTIVE}state before continuing. If this
+ * does not occur in a reasonable time, a {@link BundleException}is thrown
+ * to indicate the bundle was unable to be stopped.
+ * <li>The state of the bundle is set to {@link #STOPPING}.
+ * <li>It is recorded that this bundle has been stopped, so that when the
+ * framework is restarted, this bundle will not be automatically started.
+ * <li>The {@link BundleActivator#stop(BundleContext) stop}method of the bundle's
+ * {@link BundleActivator}, if one is specified, is called. If the
+ * {@link BundleActivator}throws an exception, this method will continue
+ * to stop the bundle. A {@link BundleException}will be thrown after
+ * completion of the remaining steps.
+ * <li>The bundle's listeners, if any, are removed, service's registered
+ * by the bundle, if any, are unregistered, and service's used by the
+ * bundle, if any, are released.
+ * <li>The state of the bundle is set to {@link #RESOLVED}.
+ * <li>A {@link BundleEvent}of type {@link BundleEvent#STOPPED}is
+ * broadcast.
+ * </ol>
+ *
+ * <h5>Preconditons</h5>
+ * <ul>
+ * <li>getState() in {{@link #ACTIVE}}.
+ * </ul>
+ * <h5>Postconditons, no exceptions thrown</h5>
+ * <ul>
+ * <li>getState() not in {{@link #ACTIVE},{@link #STOPPING}}.
+ * <li>{@link BundleActivator#stop(BundleContext) BundleActivator.stop}has been called
+ * and did not throw an exception.
+ * </ul>
+ * <h5>Postconditions, when an exception is thrown</h5>
+ * <ul>
+ * <li>None.
+ * </ul>
+ *
+ * @exception BundleException
+ * If the bundle's BundleActivator could not be loaded or
+ * threw an exception.
+ * @exception java.lang.IllegalStateException
+ * If the bundle has been uninstalled or the bundle tries to
+ * change its own state.
+ * @exception java.lang.SecurityException
+ * If the caller does not have {@link AdminPermission}
+ * permission and the Java runtime environment supports
+ * permissions.
+ */
+ public void stop() throws BundleException {
+ stop(0);
+ }
+
+ public void stop(int options) throws BundleException {
+ framework.checkAdminPermission(this, AdminPermission.EXECUTE);
+ checkValid();
+ beginStateChange();
+ try {
+ stopWorker(options);
+ } finally {
+ completeStateChange();
+ }
+ }
+
+ /**
+ * Internal worker to stop a bundle.
+ *
+ * @param options
+ */
+ protected abstract void stopWorker(int options) throws BundleException;
+
+ /**
+ * Set the persistent status bit for the bundle.
+ *
+ * @param mask
+ * Mask for bit to set/clear
+ * @param state
+ * true to set bit, false to clear bit
+ */
+ protected void setStatus(final int mask, final boolean state) {
+ try {
+ AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
+ public Object run() throws IOException {
+ int status = bundledata.getStatus();
+ boolean test = ((status & mask) != 0);
+ if (test != state) {
+ bundledata.setStatus(state ? (status | mask) : (status & ~mask));
+ bundledata.save();
+ }
+ return null;
+ }
+ });
+ } catch (PrivilegedActionException pae) {
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, this, pae.getException());
+ }
+ }
+
+ /**
+ * Stop this bundle w/o marking is persistently stopped.
+ *
+ * Any services registered by this bundle will be unregistered. Any
+ * services used by this bundle will be released. Any listeners registered
+ * by this bundle will be removed.
+ *
+ * <p>
+ * The following steps are followed to stop a bundle:
+ * <ol>
+ * <li>If the bundle is {@link #UNINSTALLED}then an <code>IllegalStateException</code>
+ * is thrown.
+ * <li>If the bundle is {@link #STOPPING},{@link #RESOLVED}, or
+ * {@link #INSTALLED}then this method returns immediately.
+ * <li>If the bundle is {@link #STARTING}then this method may wait for
+ * the bundle to reach the {@link #ACTIVE}state before continuing. If this
+ * does not occur in a reasonable time, a {@link BundleException}is thrown
+ * to indicate the bundle was unable to be stopped.
+ * <li>The state of the bundle is set to {@link #STOPPING}.
+ * <li>The {@link BundleActivator#stop(BundleContext) stop}method of the bundle's
+ * {@link BundleActivator}, if one is specified, is called. If the
+ * {@link BundleActivator}throws an exception, this method will continue
+ * to stop the bundle. A {@link BundleException}will be thrown after
+ * completion of the remaining steps.
+ * <li>The bundle's listeners, if any, are removed, service's registered
+ * by the bundle, if any, are unregistered, and service's used by the
+ * bundle, if any, are released.
+ * <li>The state of the bundle is set to {@link #RESOLVED}.
+ * <li>A {@link BundleEvent}of type {@link BundleEvent#STOPPED}is
+ * broadcast.
+ * </ol>
+ *
+ * <h5>Preconditons</h5>
+ * <ul>
+ * <li>getState() in {{@link #ACTIVE}}.
+ * </ul>
+ * <h5>Postconditons, no exceptions thrown</h5>
+ * <ul>
+ * <li>getState() not in {{@link #ACTIVE},{@link #STOPPING}}.
+ * <li>{@link BundleActivator#stop(BundleContext) BundleActivator.stop}has been called
+ * and did not throw an exception.
+ * </ul>
+ * <h5>Postconditions, when an exception is thrown</h5>
+ * <ul>
+ * <li>None.
+ * </ul>
+ *
+ * @param lock
+ * true if state change lock should be held when returning from
+ * this method.
+ * @exception BundleException
+ * If the bundle's BundleActivator could not be loaded or
+ * threw an exception.
+ * @exception java.lang.IllegalStateException
+ * If the bundle tries to change its own state.
+ */
+ protected void suspend(boolean lock) throws BundleException {
+ if (state == UNINSTALLED) {
+ return;
+ }
+ beginStateChange();
+ try {
+ stopWorker(STOP_TRANSIENT);
+ } finally {
+ if (!lock) {
+ completeStateChange();
+ }
+ }
+ }
+
+ public void update() throws BundleException {
+ update(null);
+ }
+
+ public void update(final InputStream in) throws BundleException {
+ if (Debug.DEBUG_GENERAL) {
+ Debug.println("update location " + bundledata.getLocation()); //$NON-NLS-1$
+ Debug.println(" from: " + in); //$NON-NLS-1$
+ }
+ framework.checkAdminPermission(this, AdminPermission.LIFECYCLE);
+ if ((bundledata.getType() & (BundleData.TYPE_BOOTCLASSPATH_EXTENSION | BundleData.TYPE_FRAMEWORK_EXTENSION | BundleData.TYPE_EXTCLASSPATH_EXTENSION)) != 0)
+ // need special permission to update extensions
+ framework.checkAdminPermission(this, AdminPermission.EXTENSIONLIFECYCLE);
+ checkValid();
+ beginStateChange();
+ try {
+ final AccessControlContext callerContext = AccessController.getContext();
+ //note AdminPermission is checked again after updated bundle is loaded
+ updateWorker(new PrivilegedExceptionAction<Object>() {
+ public Object run() throws BundleException {
+ /* compute the update location */
+ URLConnection source = null;
+ if (in == null) {
+ String updateLocation = bundledata.getManifest().get(Constants.BUNDLE_UPDATELOCATION);
+ if (updateLocation == null)
+ updateLocation = bundledata.getLocation();
+ if (Debug.DEBUG_GENERAL)
+ Debug.println(" from location: " + updateLocation); //$NON-NLS-1$
+ /* Map the update location to a URLConnection */
+ source = framework.adaptor.mapLocationToURLConnection(updateLocation);
+ } else {
+ /* Map the InputStream to a URLConnection */
+ source = new BundleSource(in);
+ }
+ /* call the worker */
+ updateWorkerPrivileged(source, callerContext);
+ return null;
+ }
+ });
+ } finally {
+ completeStateChange();
+ }
+ }
+
+ /**
+ * Update worker. Assumes the caller has the state change lock.
+ */
+ protected void updateWorker(PrivilegedExceptionAction<Object> action) throws BundleException {
+ int previousState = 0;
+ if (!isFragment())
+ previousState = state;
+ if ((previousState & (ACTIVE | STARTING)) != 0) {
+ try {
+ stopWorker(STOP_TRANSIENT);
+ } catch (BundleException e) {
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, this, e);
+ if ((state & (ACTIVE | STARTING)) != 0) /* if the bundle is still active */{
+ throw e;
+ }
+ }
+ }
+ try {
+ AccessController.doPrivileged(action);
+ framework.publishBundleEvent(BundleEvent.UPDATED, this);
+ } catch (PrivilegedActionException pae) {
+ if (pae.getException() instanceof RuntimeException)
+ throw (RuntimeException) pae.getException();
+ throw (BundleException) pae.getException();
+ } finally {
+ if ((previousState & (ACTIVE | STARTING)) != 0) {
+ try {
+ startWorker(START_TRANSIENT | ((previousState & STARTING) != 0 ? START_ACTIVATION_POLICY : 0));
+ } catch (BundleException e) {
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, this, e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Update worker. Assumes the caller has the state change lock.
+ */
+ protected void updateWorkerPrivileged(URLConnection source, AccessControlContext callerContext) throws BundleException {
+ AbstractBundle oldBundle = AbstractBundle.createBundle(bundledata, framework, false);
+ boolean reloaded = false;
+ BundleOperation storage = framework.adaptor.updateBundle(this.bundledata, source);
+ BundleRepository bundles = framework.getBundles();
+ try {
+ BundleData newBundleData = storage.begin();
+ // Must call framework createBundle to check execution environment.
+ final AbstractBundle newBundle = framework.createAndVerifyBundle(CollisionHook.UPDATING, this, newBundleData, false);
+ boolean exporting;
+ int st = getState();
+ synchronized (bundles) {
+ String oldBSN = this.getSymbolicName();
+ exporting = reload(newBundle);
+ // update this to flush the old BSN/version etc.
+ bundles.update(oldBSN, this);
+ manifestLocalization = null;
+ }
+ // indicate we have loaded from the new version of the bundle
+ reloaded = true;
+ 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 updated
+ if (extension && !hasPermission(new AllPermission()))
+ throw new BundleException(Msg.BUNDLE_EXTENSION_PERMISSION, BundleException.SECURITY_ERROR, new SecurityException(Msg.BUNDLE_EXTENSION_PERMISSION));
+ try {
+ AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
+ public Object run() throws Exception {
+ framework.checkAdminPermission(newBundle, AdminPermission.LIFECYCLE);
+ if (extension) // need special permission to update extension bundles
+ framework.checkAdminPermission(newBundle, AdminPermission.EXTENSIONLIFECYCLE);
+ return null;
+ }
+ }, callerContext);
+ } catch (PrivilegedActionException e) {
+ throw e.getException();
+ }
+ }
+ // send out unresolved events outside synch block (defect #80610)
+ if (st == RESOLVED)
+ framework.publishBundleEvent(BundleEvent.UNRESOLVED, this);
+ storage.commit(exporting);
+ } catch (Throwable t) {
+ try {
+ storage.undo();
+ if (reloaded)
+ /*
+ * if we loaded from the new version of the
+ * bundle
+ */{
+ synchronized (bundles) {
+ String oldBSN = this.getSymbolicName();
+ reload(oldBundle);
+ // update this to flush the new BSN/version back to the old one etc.
+ bundles.update(oldBSN, this);
+ }
+ }
+ } catch (BundleException ee) {
+ /* if we fail to revert then we are in big trouble */
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, this, ee);
+ }
+ if (t instanceof SecurityException)
+ throw (SecurityException) t;
+ if (t instanceof BundleException)
+ throw (BundleException) t;
+ throw new BundleException(t.getMessage(), t);
+ }
+ }
+
+ /**
+ * Uninstall this bundle.
+ * <p>
+ * This method removes all traces of the bundle, including any data in the
+ * persistent storage area provided for the bundle by the framework.
+ *
+ * <p>
+ * The following steps are followed to uninstall a bundle:
+ * <ol>
+ * <li>If the bundle is {@link #UNINSTALLED}then an <code>IllegalStateException</code>
+ * is thrown.
+ * <li>If the bundle is {@link #ACTIVE}or {@link #STARTING}, the bundle
+ * is stopped as described in the {@link #stop()}method. If {@link #stop()}
+ * throws an exception, a {@link FrameworkEvent}of type
+ * {@link FrameworkEvent#ERROR}is broadcast containing the exception.
+ * <li>A {@link BundleEvent}of type {@link BundleEvent#UNINSTALLED}is
+ * broadcast.
+ * <li>The state of the bundle is set to {@link #UNINSTALLED}.
+ * <li>The bundle and the persistent storage area provided for the bundle
+ * by the framework, if any, is removed.
+ * </ol>
+ *
+ * <h5>Preconditions</h5>
+ * <ul>
+ * <li>getState() not in {{@link #UNINSTALLED}}.
+ * </ul>
+ * <h5>Postconditons, no exceptions thrown</h5>
+ * <ul>
+ * <li>getState() in {{@link #UNINSTALLED}}.
+ * <li>The bundle has been uninstalled.
+ * </ul>
+ * <h5>Postconditions, when an exception is thrown</h5>
+ * <ul>
+ * <li>getState() not in {{@link #UNINSTALLED}}.
+ * <li>The Bundle has not been uninstalled.
+ * </ul>
+ *
+ * @exception BundleException
+ * If the uninstall failed.
+ * @exception java.lang.IllegalStateException
+ * If the bundle has been uninstalled or the bundle tries to
+ * change its own state.
+ * @exception java.lang.SecurityException
+ * If the caller does not have {@link AdminPermission}
+ * permission and the Java runtime environment supports
+ * permissions.
+ * @see #stop()
+ */
+ public void uninstall() throws BundleException {
+ if (Debug.DEBUG_GENERAL) {
+ Debug.println("uninstall location: " + bundledata.getLocation()); //$NON-NLS-1$
+ }
+ framework.checkAdminPermission(this, AdminPermission.LIFECYCLE);
+ if ((bundledata.getType() & (BundleData.TYPE_BOOTCLASSPATH_EXTENSION | BundleData.TYPE_FRAMEWORK_EXTENSION | BundleData.TYPE_EXTCLASSPATH_EXTENSION)) != 0)
+ // need special permission to uninstall extensions
+ framework.checkAdminPermission(this, AdminPermission.EXTENSIONLIFECYCLE);
+ checkValid();
+ beginStateChange();
+ try {
+ uninstallWorker(new PrivilegedExceptionAction<Object>() {
+ public Object run() throws BundleException {
+ uninstallWorkerPrivileged();
+ return null;
+ }
+ });
+ } finally {
+ completeStateChange();
+ }
+ }
+
+ /**
+ * Uninstall worker. Assumes the caller has the state change lock.
+ */
+ protected void uninstallWorker(PrivilegedExceptionAction<Object> action) throws BundleException {
+ boolean bundleActive = false;
+ if (!isFragment())
+ bundleActive = (state & (ACTIVE | STARTING)) != 0;
+ if (bundleActive) {
+ try {
+ stopWorker(STOP_TRANSIENT);
+ } catch (BundleException e) {
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, this, e);
+ }
+ }
+ try {
+ AccessController.doPrivileged(action);
+ } catch (PrivilegedActionException pae) {
+ if (bundleActive) /* if we stopped the bundle */{
+ try {
+ startWorker(START_TRANSIENT);
+ } catch (BundleException e) {
+ /*
+ * if we fail to start the original bundle then we are in
+ * big trouble
+ */
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, this, e);
+ }
+ }
+ throw (BundleException) pae.getException();
+ }
+ framework.publishBundleEvent(BundleEvent.UNINSTALLED, this);
+ }
+
+ /**
+ * Uninstall worker. Assumes the caller has the state change lock.
+ */
+ protected void uninstallWorkerPrivileged() throws BundleException {
+ BundleWatcher bundleStats = framework.adaptor.getBundleWatcher();
+ if (bundleStats != null)
+ bundleStats.watchBundle(this, BundleWatcher.START_UNINSTALLING);
+ boolean unloaded = false;
+ //cache the bundle's headers
+ getHeaders();
+ BundleOperation storage = framework.adaptor.uninstallBundle(this.bundledata);
+ BundleRepository bundles = framework.getBundles();
+ try {
+ storage.begin();
+ boolean exporting;
+ int st = getState();
+ synchronized (bundles) {
+ bundles.remove(this); /* remove before calling unload */
+ exporting = unload();
+ }
+ // send out unresolved events outside synch block (defect #80610)
+ if (st == RESOLVED)
+ framework.publishBundleEvent(BundleEvent.UNRESOLVED, this);
+ unloaded = true;
+ storage.commit(exporting);
+ close();
+ } catch (BundleException e) {
+ try {
+ storage.undo();
+ if (unloaded) /* if we unloaded the bundle */{
+ synchronized (bundles) {
+ load(); /* reload the bundle */
+ bundles.add(this);
+ }
+ }
+ } catch (BundleException ee) {
+ /*
+ * if we fail to load the original bundle then we are in big
+ * trouble
+ */
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, this, ee);
+ }
+ throw e;
+ } finally {
+ if (bundleStats != null)
+ bundleStats.watchBundle(this, BundleWatcher.END_UNINSTALLING);
+ }
+ }
+
+ /**
+ * Return the bundle's manifest headers and values from the manifest's
+ * preliminary section. That is all the manifest's headers and values prior
+ * to the first blank line.
+ *
+ * <p>
+ * Manifest header names are case-insensitive. The methods of the returned
+ * <code>Dictionary</code> object will operate on header names in a
+ * case-insensitive manner.
+ *
+ * <p>
+ * For example, the following manifest headers and values are included if
+ * they are present in the manifest:
+ *
+ * <pre>
+ * Bundle-Name
+ * Bundle-Vendor
+ * Bundle-Version
+ * Bundle-Description
+ * Bundle-DocURL
+ * Bundle-ContactAddress
+ * </pre>
+ *
+ * <p>
+ * This method will continue to return this information when the bundle is
+ * in the {@link #UNINSTALLED}state.
+ *
+ * @return A <code>Dictionary</code> object containing the bundle's
+ * manifest headers and values.
+ * @exception java.lang.SecurityException
+ * If the caller does not have {@link AdminPermission}
+ * permission and the Java runtime environment supports
+ * permissions.
+ */
+ public Dictionary<String, String> getHeaders() {
+ return getHeaders(null);
+ }
+
+ /**
+ * Returns this bundle's Manifest headers and values. This method returns
+ * all the Manifest headers and values from the main section of the
+ * bundle's Manifest file; that is, all lines prior to the first blank
+ * line.
+ *
+ * <p>
+ * Manifest header names are case-insensitive. The methods of the returned
+ * <tt>Dictionary</tt> object will operate on header names in a
+ * case-insensitive manner.
+ *
+ * If a Manifest header begins with a '%', it will be evaluated with the
+ * specified properties file for the specied Locale.
+ *
+ * <p>
+ * For example, the following Manifest headers and values are included if
+ * they are present in the Manifest file:
+ *
+ * <pre>
+ * Bundle-Name
+ * Bundle-Vendor
+ * Bundle-Version
+ * Bundle-Description
+ * Bundle-DocURL
+ * Bundle-ContactAddress
+ * </pre>
+ *
+ * <p>
+ * This method will continue to return Manifest header information while
+ * this bundle is in the <tt>UNINSTALLED</tt> state.
+ *
+ * @return A <tt>Dictionary</tt> object containing this bundle's Manifest
+ * headers and values.
+ *
+ * @exception java.lang.SecurityException
+ * If the caller does not have the <tt>AdminPermission</tt>,
+ * and the Java Runtime Environment supports permissions.
+ */
+ public Dictionary<String, String> getHeaders(String localeString) {
+ framework.checkAdminPermission(this, AdminPermission.METADATA);
+ ManifestLocalization localization;
+ try {
+ localization = getManifestLocalization();
+ } catch (BundleException e) {
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, this, e);
+ // return an empty dictinary.
+ return new Hashtable<String, String>();
+ }
+ if (localeString == null)
+ localeString = Locale.getDefault().toString();
+ return localization.getHeaders(localeString);
+ }
+
+ /**
+ * Retrieve the bundle's unique identifier, which the framework assigned to
+ * this bundle when it was installed.
+ *
+ * <p>
+ * The unique identifier has the following attributes:
+ * <ul>
+ * <li>It is unique and persistent.
+ * <li>The identifier is a long.
+ * <li>Once its value is assigned to a bundle, that value is not reused
+ * for another bundle, even after the bundle is uninstalled.
+ * <li>Its value does not change as long as the bundle remains installed.
+ * <li>Its value does not change when the bundle is updated
+ * </ul>
+ *
+ * <p>
+ * This method will continue to return the bundle's unique identifier when
+ * the bundle is in the {@link #UNINSTALLED}state.
+ *
+ * @return This bundle's unique identifier.
+ */
+ public long getBundleId() {
+ return (bundledata.getBundleID());
+ }
+
+ /**
+ * Retrieve the location identifier of the bundle. This is typically the
+ * location passed to
+ * {@link BundleContextImpl#installBundle(String) BundleContext.installBundle}when the
+ * bundle was installed. The location identifier of the bundle may change
+ * during bundle update. Calling this method while framework is updating
+ * the bundle results in undefined behavior.
+ *
+ * <p>
+ * This method will continue to return the bundle's location identifier
+ * when the bundle is in the {@link #UNINSTALLED}state.
+ *
+ * @return A string that is the location identifier of the bundle.
+ * @exception java.lang.SecurityException
+ * If the caller does not have {@link AdminPermission}
+ * permission and the Java runtime environment supports
+ * permissions.
+ */
+ public String getLocation() {
+ framework.checkAdminPermission(this, AdminPermission.METADATA);
+ return (bundledata.getLocation());
+ }
+
+ /**
+ * Determine whether the bundle has the requested permission.
+ *
+ * <p>
+ * If the Java runtime environment does not supports permissions this
+ * method always returns <code>true</code>. The permission parameter is
+ * of type <code>Object</code> to avoid referencing the <code>java.security.Permission</code>
+ * class directly. This is to allow the framework to be implemented in Java
+ * environments which do not support permissions.
+ *
+ * @param permission
+ * The requested permission.
+ * @return <code>true</code> if the bundle has the requested permission
+ * or <code>false</code> if the bundle does not have the
+ * permission or the permission parameter is not an <code>instanceof java.security.Permission</code>.
+ * @exception java.lang.IllegalStateException
+ * If the bundle has been uninstalled.
+ */
+ public boolean hasPermission(Object permission) {
+ checkValid();
+ if (domain != null) {
+ if (permission instanceof Permission) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm instanceof EquinoxSecurityManager) {
+ /*
+ * If the FrameworkSecurityManager is active, we need to do checks the "right" way.
+ * We can exploit our knowledge that the security context of FrameworkSecurityManager
+ * is an AccessControlContext to invoke it properly with the ProtectionDomain.
+ */
+ AccessControlContext acc = getAccessControlContext();
+ try {
+ sm.checkPermission((Permission) permission, acc);
+ return true;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+ return domain.implies((Permission) permission);
+ }
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * This method marks the bundle's state as changing so that other calls to
+ * start/stop/suspend/update/uninstall can wait until the state change is
+ * complete. If stateChanging is non-null when this method is called, we
+ * will wait for the state change to complete. If the timeout expires
+ * without changing state (this may happen if the state change is back up
+ * our call stack), a BundleException is thrown so that we don't wait
+ * forever.
+ *
+ * A call to this method should be immediately followed by a try block
+ * whose finally block calls completeStateChange().
+ *
+ * beginStateChange(); try { // change the bundle's state here... } finally {
+ * completeStateChange(); }
+ *
+ * @exception org.osgi.framework.BundleException
+ * if the bundles state is still changing after waiting for
+ * the timeout.
+ */
+ protected void beginStateChange() throws BundleException {
+ synchronized (statechangeLock) {
+ boolean doubleFault = false;
+ while (true) {
+ if (stateChanging == null) {
+ stateChanging = Thread.currentThread();
+ return;
+ }
+ if (doubleFault || (stateChanging == Thread.currentThread())) {
+ throw new BundleException(NLS.bind(Msg.BUNDLE_STATE_CHANGE_EXCEPTION, getBundleData().getLocation(), stateChanging.getName()), BundleException.STATECHANGE_ERROR, new BundleStatusException(null, StatusException.CODE_WARNING, stateChanging));
+ }
+ try {
+ long start = 0;
+ if (Debug.DEBUG_GENERAL) {
+ Debug.println(" Waiting for state to change in bundle " + this); //$NON-NLS-1$
+ start = System.currentTimeMillis();
+ }
+ statechangeLock.wait(STATE_CHANGE_TIMEOUT);
+ /*
+ * wait for other thread to
+ * finish changing state
+ */
+ if (Debug.DEBUG_GENERAL) {
+ long end = System.currentTimeMillis();
+ if (end - start > 0)
+ System.out.println("Waiting... : " + getSymbolicName() + ' ' + (end - start)); //$NON-NLS-1$
+ }
+ } catch (InterruptedException e) {
+ //Nothing to do
+ }
+ doubleFault = true;
+ }
+ }
+ }
+
+ /**
+ * This method completes the bundle state change by setting stateChanging
+ * to null and notifying one waiter that the state change has completed.
+ */
+ protected void completeStateChange() {
+ synchronized (statechangeLock) {
+ if (stateChanging == Thread.currentThread()) {
+ stateChanging = null;
+ statechangeLock.notify();
+ /*
+ * notify one waiting thread that the
+ * state change is complete
+ */
+ }
+ }
+ }
+
+ /**
+ * Return a string representation of this bundle.
+ *
+ * @return String
+ */
+ public String toString() {
+ String name = bundledata.getSymbolicName();
+ if (name == null)
+ name = "unknown"; //$NON-NLS-1$
+ return (name + '_' + bundledata.getVersion() + " [" + getBundleId() + "]"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ /**
+ * Answers an integer indicating the relative positions of the receiver and
+ * the argument in the natural order of elements of the receiver's class.
+ *
+ * @return int which should be <0 if the receiver should sort before the
+ * argument, 0 if the receiver should sort in the same position as
+ * the argument, and >0 if the receiver should sort after the
+ * argument.
+ * @param obj
+ * another Bundle an object to compare the receiver to
+ * @exception ClassCastException
+ * if the argument can not be converted into something
+ * comparable with the receiver.
+ */
+ public int compareTo(Bundle obj) {
+ int slcomp = getInternalStartLevel() - ((AbstractBundle) obj).getInternalStartLevel();
+ if (slcomp != 0) {
+ return slcomp;
+ }
+ long idcomp = getBundleId() - ((AbstractBundle) obj).getBundleId();
+ return (idcomp < 0L) ? -1 : ((idcomp > 0L) ? 1 : 0);
+ }
+
+ /**
+ * This method checks that the bundle is not uninstalled. If the bundle is
+ * uninstalled, an IllegalStateException is thrown.
+ *
+ * @exception java.lang.IllegalStateException
+ * If the bundle is uninstalled.
+ */
+ protected void checkValid() {
+ if (state == UNINSTALLED) {
+ throw new IllegalStateException(NLS.bind(Msg.BUNDLE_UNINSTALLED_EXCEPTION, getBundleData().getLocation()));
+ }
+ }
+
+ /**
+ * Get the bundle's ProtectionDomain.
+ *
+ * @return bundle's ProtectionDomain.
+ */
+ public BundleProtectionDomain getProtectionDomain() {
+ return domain;
+ }
+
+ private AccessControlContext getAccessControlContext() {
+ return new AccessControlContext(new ProtectionDomain[] {domain});
+ }
+
+ protected BundleFragment[] getFragments() {
+ checkValid();
+ return null;
+ }
+
+ protected boolean isFragment() {
+ return false;
+ }
+
+ BundleHost[] getHosts() {
+ checkValid();
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.osgi.framework.Bundle#findClass(java.lang.String)
+ */
+ public Class<?> loadClass(String classname) throws ClassNotFoundException {
+ return loadClass(classname, true);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.osgi.framework.Bundle#getResourcePaths(java.lang.String)
+ */
+ public Enumeration<String> getEntryPaths(final String path) {
+ try {
+ framework.checkAdminPermission(this, AdminPermission.RESOURCE);
+ } catch (SecurityException e) {
+ return null;
+ }
+ checkValid();
+ // TODO this doPrivileged is probably not needed. The adaptor isolates callers from disk access
+ return AccessController.doPrivileged(new PrivilegedAction<Enumeration<String>>() {
+ public Enumeration<String> run() {
+ return bundledata.getEntryPaths(path);
+ }
+ });
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.osgi.framework.Bundle#getFile(java.lang.String)
+ */
+ public URL getEntry(String fileName) {
+ try {
+ framework.checkAdminPermission(this, AdminPermission.RESOURCE);
+ } catch (SecurityException e) {
+ return null;
+ }
+ return getEntry0(fileName);
+ }
+
+ URL getEntry0(String fileName) {
+ checkValid();
+ return bundledata.getEntry(fileName);
+ }
+
+ public String getSymbolicName() {
+ return bundledata.getSymbolicName();
+ }
+
+ public long getLastModified() {
+ return bundledata.getLastModified();
+ }
+
+ public BundleData getBundleData() {
+ return bundledata;
+ }
+
+ public Version getVersion() {
+ return bundledata.getVersion();
+ }
+
+ public BundleDescription getBundleDescription() {
+ return framework.adaptor.getState().getBundle(getBundleId());
+ }
+
+ int getInternalStartLevel() {
+ return bundledata.getStartLevel();
+ }
+
+ protected abstract BundleLoader getBundleLoader();
+
+ /**
+ * Mark this bundle as resolved.
+ */
+ protected void resolve() {
+ if (Debug.DEBUG_GENERAL) {
+ if ((state & (INSTALLED)) == 0) {
+ Debug.println("Bundle.resolve called when state != INSTALLED: " + this); //$NON-NLS-1$
+ Debug.printStackTrace(new Exception("Stack trace")); //$NON-NLS-1$
+ }
+ }
+ if (state == INSTALLED) {
+ state = RESOLVED;
+ // Do not publish RESOLVED event here. This is done by caller
+ // to resolve if appropriate.
+ }
+ }
+
+ public BundleContext getBundleContext() {
+ framework.checkAdminPermission(this, AdminPermission.CONTEXT);
+ return getContext();
+ }
+
+ /**
+ * Return the current context for this bundle.
+ *
+ * @return BundleContext for this bundle.
+ */
+ abstract protected BundleContextImpl getContext();
+
+ public BundleException getResolutionFailureException() {
+ BundleDescription bundleDescription = getBundleDescription();
+ if (bundleDescription == null)
+ return new BundleException(NLS.bind(Msg.BUNDLE_UNRESOLVED_EXCEPTION, this.toString()), BundleException.RESOLVE_ERROR);
+ // just a sanity check - this would be an inconsistency between the framework and the state
+ if (bundleDescription.isResolved())
+ return new BundleException(Msg.BUNDLE_UNRESOLVED_STATE_CONFLICT, BundleException.RESOLVE_ERROR);
+ return getResolverError(bundleDescription);
+ }
+
+ private BundleException getResolverError(BundleDescription bundleDesc) {
+ ResolverError[] errors = framework.adaptor.getState().getResolverErrors(bundleDesc);
+ if (errors == null || errors.length == 0)
+ return new BundleException(NLS.bind(Msg.BUNDLE_UNRESOLVED_EXCEPTION, this.toString()), BundleException.RESOLVE_ERROR);
+ StringBuffer message = new StringBuffer();
+ int errorType = BundleException.RESOLVE_ERROR;
+ for (int i = 0; i < errors.length; i++) {
+ if ((errors[i].getType() & ResolverError.INVALID_NATIVECODE_PATHS) != 0)
+ errorType = BundleException.NATIVECODE_ERROR;
+ message.append(errors[i].toString());
+ if (i < errors.length - 1)
+ message.append(", "); //$NON-NLS-1$
+ }
+ return new BundleException(NLS.bind(Msg.BUNDLE_UNRESOLVED_UNSATISFIED_CONSTRAINT_EXCEPTION, this.toString(), message.toString()), errorType);
+ }
+
+ public int getKeyHashCode() {
+ long id = getBundleId();
+ return (int) (id ^ (id >>> 32));
+ }
+
+ public boolean compare(KeyedElement other) {
+ return getBundleId() == ((AbstractBundle) other).getBundleId();
+ }
+
+ public Object getKey() {
+ return new Long(getBundleId());
+ }
+
+ /* This method is used by the Bundle Localization Service to obtain
+ * a ResourceBundle that resides in a bundle. This is not an OSGi
+ * defined method for org.osgi.framework.Bundle
+ *
+ */
+ public ResourceBundle getResourceBundle(String localeString) {
+ ManifestLocalization localization;
+ try {
+ localization = getManifestLocalization();
+ } catch (BundleException ex) {
+ return (null);
+ }
+ String defaultLocale = Locale.getDefault().toString();
+ if (localeString == null) {
+ localeString = defaultLocale;
+ }
+ return localization.getResourceBundle(localeString, defaultLocale.equals(localeString));
+ }
+
+ private synchronized ManifestLocalization getManifestLocalization() throws BundleException {
+ ManifestLocalization currentLocalization = manifestLocalization;
+ if (currentLocalization == null) {
+ Dictionary<String, String> rawHeaders = bundledata.getManifest();
+ manifestLocalization = currentLocalization = new ManifestLocalization(this, rawHeaders);
+ }
+ return currentLocalization;
+ }
+
+ public boolean testStateChanging(Object thread) {
+ return stateChanging == thread;
+ }
+
+ public Thread getStateChanging() {
+ return stateChanging;
+ }
+
+ public Enumeration<URL> findEntries(String path, String filePattern, boolean recurse) {
+ try {
+ framework.checkAdminPermission(this, AdminPermission.RESOURCE);
+ } catch (SecurityException e) {
+ return null;
+ }
+ checkValid();
+ // check to see if the bundle is resolved
+ if (!isResolved())
+ framework.packageAdmin.resolveBundles(new Bundle[] {this});
+
+ // if this bundle is a host to fragments then search the fragments
+ BundleFragment[] fragments = getFragments();
+ List<BundleData> datas = new ArrayList<BundleData>((fragments == null ? 0 : fragments.length) + 1);
+ datas.add(getBundleData());
+ if (fragments != null)
+ for (BundleFragment fragment : fragments)
+ datas.add(fragment.getBundleData());
+ int options = recurse ? BundleWiring.FINDENTRIES_RECURSE : 0;
+ return framework.getAdaptor().findEntries(datas, path, filePattern, options);
+ }
+
+ class BundleStatusException extends Throwable implements StatusException {
+ private static final long serialVersionUID = 7201911791818929100L;
+ private int code;
+ private transient Object status;
+
+ BundleStatusException(String message, int code, Object status) {
+ super(message);
+ this.code = code;
+ this.status = status;
+ }
+
+ public Object getStatus() {
+ return status;
+ }
+
+ public int getStatusCode() {
+ return code;
+ }
+
+ }
+
+ public Map<X509Certificate, List<X509Certificate>> getSignerCertificates(int signersType) {
+ @SuppressWarnings("unchecked")
+ final Map<X509Certificate, List<X509Certificate>> empty = Collections.EMPTY_MAP;
+ if (signersType != SIGNERS_ALL && signersType != SIGNERS_TRUSTED)
+ throw new IllegalArgumentException("Invalid signers type: " + signersType); //$NON-NLS-1$
+ if (framework == null)
+ return empty;
+ SignedContentFactory factory = framework.getSignedContentFactory();
+ if (factory == null)
+ return empty;
+ try {
+ SignedContent signedContent = factory.getSignedContent(this);
+ SignerInfo[] infos = signedContent.getSignerInfos();
+ if (infos.length == 0)
+ return empty;
+ Map<X509Certificate, List<X509Certificate>> results = new HashMap<X509Certificate, List<X509Certificate>>(infos.length);
+ for (int i = 0; i < infos.length; i++) {
+ if (signersType == SIGNERS_TRUSTED && !infos[i].isTrusted())
+ continue;
+ Certificate[] certs = infos[i].getCertificateChain();
+ if (certs == null || certs.length == 0)
+ continue;
+ List<X509Certificate> certChain = new ArrayList<X509Certificate>();
+ for (int j = 0; j < certs.length; j++)
+ certChain.add((X509Certificate) certs[j]);
+ results.put((X509Certificate) certs[0], certChain);
+ }
+ return results;
+ } catch (Exception e) {
+ return empty;
+ }
+ }
+
+ public final <A> A adapt(Class<A> adapterType) {
+ checkAdaptPermission(adapterType);
+ return adapt0(adapterType);
+ }
+
+ public List<BundleRevision> getRevisions() {
+ List<BundleRevision> revisions = new ArrayList<BundleRevision>();
+ BundleDescription current = getBundleDescription();
+ if (current != null)
+ revisions.add(current);
+ BundleDescription[] removals = framework.adaptor.getState().getRemovalPending();
+ for (BundleDescription removed : removals) {
+ if (removed.getBundleId() == getBundleId() && removed != current) {
+ revisions.add(removed);
+ }
+ }
+ return revisions;
+ }
+
+ @SuppressWarnings("unchecked")
+ protected <A> A adapt0(Class<A> adapterType) {
+ if (adapterType.isInstance(this))
+ return (A) this;
+ if (BundleContext.class.equals(adapterType)) {
+ try {
+ return (A) getBundleContext();
+ } catch (SecurityException e) {
+ return null;
+ }
+ }
+
+ if (AccessControlContext.class.equals(adapterType)) {
+ return (A) getAccessControlContext();
+ }
+
+ if (BundleWiring.class.equals(adapterType)) {
+ if (state == UNINSTALLED)
+ return null;
+ BundleDescription description = getBundleDescription();
+ return (A) description.getWiring();
+ }
+
+ if (BundleRevision.class.equals(adapterType)) {
+ if (state == UNINSTALLED)
+ return null;
+ return (A) getBundleDescription();
+ }
+ return null;
+ }
+
+ /**
+ * Check for permission to get a service.
+ */
+ private <A> void checkAdaptPermission(Class<A> adapterType) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm == null) {
+ return;
+ }
+ sm.checkPermission(new AdaptPermission(adapterType.getName(), this, AdaptPermission.ADAPT));
+ }
+
+ public File getDataFile(String filename) {
+ return framework.getDataFile(this, filename);
+ }
+
+ public Bundle getBundle() {
+ return this;
+ }
+
+ public int getStartLevel() {
+ if (getState() == Bundle.UNINSTALLED)
+ throw new IllegalArgumentException(NLS.bind(Msg.BUNDLE_UNINSTALLED_EXCEPTION, getBundleData().getLocation()));
+ return getInternalStartLevel();
+ }
+
+ public void setStartLevel(int startlevel) {
+ framework.startLevelManager.setBundleStartLevel(this, startlevel);
+ }
+
+ public boolean isPersistentlyStarted() {
+ if (getState() == Bundle.UNINSTALLED)
+ throw new IllegalArgumentException(NLS.bind(Msg.BUNDLE_UNINSTALLED_EXCEPTION, getBundleData().getLocation()));
+ return (getBundleData().getStatus() & Constants.BUNDLE_STARTED) != 0;
+ }
+
+ public boolean isActivationPolicyUsed() {
+ if (getState() == Bundle.UNINSTALLED)
+ throw new IllegalArgumentException(NLS.bind(Msg.BUNDLE_UNINSTALLED_EXCEPTION, getBundleData().getLocation()));
+ return (getBundleData().getStatus() & Constants.BUNDLE_ACTIVATION_POLICY) != 0;
+ }
+
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/AliasMapper.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/AliasMapper.java
new file mode 100644
index 000000000..728745362
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/AliasMapper.java
@@ -0,0 +1,155 @@
+/*******************************************************************************
+ * 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.util.*;
+import org.eclipse.osgi.framework.debug.Debug;
+
+/**
+ * This class maps aliases.
+ */
+public class AliasMapper {
+ private static Map<String, Object> processorAliasTable;
+ private static Map<String, Object> osnameAliasTable;
+
+ // Safe lazy initialization
+ private static synchronized Map<String, Object> getProcessorAliasTable() {
+ if (processorAliasTable == null) {
+ InputStream in = AliasMapper.class.getResourceAsStream(Constants.OSGI_PROCESSOR_ALIASES);
+ if (in != null) {
+ try {
+ processorAliasTable = initAliases(in);
+ } finally {
+ try {
+ in.close();
+ } catch (IOException ee) {
+ // nothing
+ }
+ }
+ }
+ }
+ return processorAliasTable;
+ }
+
+ // Safe lazy initialization
+ private static synchronized Map<String, Object> getOSNameAliasTable() {
+ if (osnameAliasTable == null) {
+ InputStream in = AliasMapper.class.getResourceAsStream(Constants.OSGI_OSNAME_ALIASES);
+ if (in != null) {
+ try {
+ osnameAliasTable = initAliases(in);
+ } finally {
+ try {
+ in.close();
+ } catch (IOException ee) {
+ // nothing
+ }
+ }
+ }
+ }
+ return osnameAliasTable;
+ }
+
+ /**
+ * Return the master alias for the processor.
+ *
+ * @param processor Input name
+ * @return aliased name (if any)
+ */
+ public String aliasProcessor(String processor) {
+ processor = processor.toLowerCase();
+ Map<String, Object> aliases = getProcessorAliasTable();
+ if (aliases != null) {
+ String alias = (String) aliases.get(processor);
+ if (alias != null) {
+ processor = alias;
+ }
+ }
+ return processor;
+ }
+
+ /**
+ * Return the master alias for the osname.
+ *
+ * @param osname Input name
+ * @return aliased name (if any)
+ */
+ public Object aliasOSName(String osname) {
+ osname = osname.toLowerCase();
+ Map<String, Object> aliases = getOSNameAliasTable();
+ if (aliases != null) {
+ Object aliasObject = aliases.get(osname);
+ //String alias = (String) osnameAliasTable.get(osname);
+ if (aliasObject != null)
+ if (aliasObject instanceof String) {
+ osname = (String) aliasObject;
+ } else {
+ return aliasObject;
+ }
+ }
+ return osname;
+ }
+
+ /**
+ * Read alias data and populate a Map.
+ *
+ * @param in InputStream from which to read alias data.
+ * @return Map of aliases.
+ */
+ protected static Map<String, Object> initAliases(InputStream in) {
+ Map<String, Object> aliases = new HashMap<String, Object>(37);
+ try {
+ BufferedReader br;
+ try {
+ br = new BufferedReader(new InputStreamReader(in, "UTF8")); //$NON-NLS-1$
+ } catch (UnsupportedEncodingException e) {
+ br = new BufferedReader(new InputStreamReader(in));
+ }
+ while (true) {
+ String line = br.readLine();
+ if (line == null) /* EOF */{
+ break; /* done */
+ }
+ Tokenizer tokenizer = new Tokenizer(line);
+ String master = tokenizer.getString("# \t"); //$NON-NLS-1$
+ if (master != null) {
+ aliases.put(master.toLowerCase(), master);
+ parseloop: while (true) {
+ String alias = tokenizer.getString("# \t"); //$NON-NLS-1$
+ if (alias == null) {
+ break parseloop;
+ }
+ String lowerCaseAlias = alias.toLowerCase();
+ Object storedMaster = aliases.get(lowerCaseAlias);
+ if (storedMaster == null) {
+ aliases.put(lowerCaseAlias, master);
+ } else if (storedMaster instanceof String) {
+ List<String> newMaster = new ArrayList<String>();
+ newMaster.add((String) storedMaster);
+ newMaster.add(master);
+ aliases.put(lowerCaseAlias, newMaster);
+ } else {
+ @SuppressWarnings("unchecked")
+ List<String> newMaster = ((List<String>) storedMaster);
+ newMaster.add(master);
+ }
+ }
+ }
+ }
+ } catch (IOException e) {
+ if (Debug.DEBUG_GENERAL) {
+ Debug.printStackTrace(e);
+ }
+ }
+ return aliases;
+ }
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/BundleContextImpl.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/BundleContextImpl.java
new file mode 100644
index 000000000..a6581cd2d
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/BundleContextImpl.java
@@ -0,0 +1,974 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2011 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.File;
+import java.io.InputStream;
+import java.security.*;
+import java.util.*;
+import org.eclipse.osgi.event.BatchBundleListener;
+import org.eclipse.osgi.framework.debug.Debug;
+import org.eclipse.osgi.framework.eventmgr.EventDispatcher;
+import org.eclipse.osgi.internal.profile.Profile;
+import org.eclipse.osgi.internal.serviceregistry.*;
+import org.eclipse.osgi.util.NLS;
+import org.osgi.framework.*;
+
+/**
+ * Bundle's execution context.
+ *
+ * This object is given out to bundles and provides the
+ * implementation to the BundleContext for a host bundle.
+ * It is destroyed when a bundle is stopped.
+ */
+
+public class BundleContextImpl implements BundleContext, EventDispatcher<Object, Object, Object> {
+ private static boolean SET_TCCL = "true".equals(FrameworkProperties.getProperty("eclipse.bundle.setTCCL", "true")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ /** true if the bundle context is still valid */
+ private volatile boolean valid;
+
+ /** Bundle object this context is associated with. */
+ // This slot is accessed directly by the Framework instead of using
+ // the getBundle() method because the Framework needs access to the bundle
+ // even when the context is invalid while the close method is being called.
+ final BundleHost bundle;
+
+ /** Internal framework object. */
+ final Framework framework;
+
+ /** Services that bundle is using. Key is ServiceRegistrationImpl,
+ Value is ServiceUse */
+ /* @GuardedBy("contextLock") */
+ private HashMap<ServiceRegistrationImpl<?>, ServiceUse<?>> servicesInUse;
+
+ /** The current instantiation of the activator. */
+ protected BundleActivator activator;
+
+ /** private object for locking */
+ private final Object contextLock = new Object();
+
+ /**
+ * Construct a BundleContext which wrappers the framework for a
+ * bundle
+ *
+ * @param bundle The bundle we are wrapping.
+ */
+ protected BundleContextImpl(BundleHost bundle) {
+ this.bundle = bundle;
+ valid = true;
+ framework = bundle.framework;
+ synchronized (contextLock) {
+ servicesInUse = null;
+ }
+ activator = null;
+ }
+
+ /**
+ * Destroy the wrapper. This is called when the bundle is stopped.
+ *
+ */
+ protected void close() {
+ valid = false; /* invalidate context */
+
+ final ServiceRegistry registry = framework.getServiceRegistry();
+
+ registry.removeAllServiceListeners(this);
+ framework.removeAllListeners(this);
+
+ /* service's registered by the bundle, if any, are unregistered. */
+ registry.unregisterServices(this);
+
+ /* service's used by the bundle, if any, are released. */
+ registry.releaseServicesInUse(this);
+
+ synchronized (contextLock) {
+ servicesInUse = null;
+ }
+ }
+
+ /**
+ * Retrieve the value of the named environment property.
+ *
+ * @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) {
+ SecurityManager sm = System.getSecurityManager();
+
+ if (sm != null) {
+ sm.checkPropertyAccess(key);
+ }
+
+ return (framework.getProperty(key));
+ }
+
+ /**
+ * Retrieve the Bundle object for the context bundle.
+ *
+ * @return The context bundle's Bundle object.
+ */
+ public Bundle getBundle() {
+ checkValid();
+
+ return getBundleImpl();
+ }
+
+ public AbstractBundle getBundleImpl() {
+ return bundle;
+ }
+
+ public Bundle installBundle(String location) throws BundleException {
+ return installBundle(location, null);
+ }
+
+ public Bundle installBundle(String location, InputStream in) throws BundleException {
+ checkValid();
+ //note AdminPermission is checked after bundle is loaded
+ return framework.installBundle(location, in, this);
+ }
+
+ /**
+ * Retrieve the bundle that has the given unique identifier.
+ *
+ * @param id The identifier of the bundle to retrieve.
+ * @return A Bundle object, or <code>null</code>
+ * if the identifier doesn't match any installed bundle.
+ */
+ public Bundle getBundle(long id) {
+ return framework.getBundle(this, id);
+ }
+
+ public Bundle getBundle(String location) {
+ return framework.getBundleByLocation(location);
+ }
+
+ /**
+ * Retrieve the bundle that has the given location.
+ *
+ * @param location The location string of the bundle to retrieve.
+ * @return A Bundle object, or <code>null</code>
+ * if the location doesn't match any installed bundle.
+ */
+ public AbstractBundle getBundleByLocation(String location) {
+ return (framework.getBundleByLocation(location));
+ }
+
+ /**
+ * Retrieve a list 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 An array of {@link AbstractBundle} objects, one
+ * object per installed bundle.
+ */
+ public Bundle[] getBundles() {
+ return framework.getBundles(this);
+ }
+
+ /**
+ * Add a service listener with a filter.
+ * {@link ServiceListener}s are notified when a service has a lifecycle
+ * state change.
+ * See {@link #getServiceReferences(String, String) getServiceReferences}
+ * for a description of the filter syntax.
+ * The listener is added to the context bundle's list of listeners.
+ * See {@link #getBundle() getBundle()}
+ * for a definition of context bundle.
+ *
+ * <p>The listener is called if the filter criteria is met.
+ * To filter based upon the class of the service, the filter
+ * should reference the "objectClass" property.
+ * If the filter paramater is <code>null</code>, all services
+ * are considered to match the filter.
+ * <p>If the Java runtime environment supports permissions, then additional
+ * filtering is done.
+ * {@link AbstractBundle#hasPermission(Object) Bundle.hasPermission} is called for the
+ * bundle which defines the listener to validate that the listener has the
+ * {@link ServicePermission} permission to <code>"get"</code> the service
+ * using at least one of the named classes the service was registered under.
+ *
+ * @param listener The service listener to add.
+ * @param filter The filter criteria.
+ * @exception InvalidSyntaxException If the filter parameter contains
+ * an invalid filter string which cannot be parsed.
+ * @see ServiceEvent
+ * @see ServiceListener
+ * @exception java.lang.IllegalStateException
+ * If the bundle context has stopped.
+ */
+ public void addServiceListener(ServiceListener listener, String filter) throws InvalidSyntaxException {
+ checkValid();
+
+ if (listener == null) {
+ throw new IllegalArgumentException();
+ }
+ framework.getServiceRegistry().addServiceListener(this, listener, filter);
+ }
+
+ /**
+ * Add a service listener.
+ *
+ * <p>This method is the same as calling
+ * {@link #addServiceListener(ServiceListener, String)}
+ * with filter set to <code>null</code>.
+ *
+ * @see #addServiceListener(ServiceListener, String)
+ */
+ public void addServiceListener(ServiceListener listener) {
+ try {
+ addServiceListener(listener, null);
+ } catch (InvalidSyntaxException e) {
+ if (Debug.DEBUG_GENERAL) {
+ Debug.println("InvalidSyntaxException w/ null filter" + e.getMessage()); //$NON-NLS-1$
+ Debug.printStackTrace(e);
+ }
+ }
+ }
+
+ /**
+ * Remove a service listener.
+ * The listener is removed from the context bundle's list of listeners.
+ * See {@link #getBundle() getBundle()}
+ * for a definition of context bundle.
+ *
+ * <p>If this method is called with a listener which is not registered,
+ * then this method does nothing.
+ *
+ * @param listener The service listener to remove.
+ * @exception java.lang.IllegalStateException
+ * If the bundle context has stopped.
+ */
+ public void removeServiceListener(ServiceListener listener) {
+ checkValid();
+
+ if (listener == null) {
+ throw new IllegalArgumentException();
+ }
+ framework.getServiceRegistry().removeServiceListener(this, listener);
+ }
+
+ /**
+ * Add a bundle listener.
+ * {@link BundleListener}s are notified when a bundle has a lifecycle
+ * state change.
+ * The listener is added to the context bundle's list of listeners.
+ * See {@link #getBundle() getBundle()}
+ * for a definition of context bundle.
+ *
+ * @param listener The bundle listener to add.
+ * @exception java.lang.IllegalStateException
+ * If the bundle context has stopped.
+ * @see BundleEvent
+ * @see BundleListener
+ */
+ public void addBundleListener(BundleListener listener) {
+ checkValid();
+ if (listener == null) {
+ throw new IllegalArgumentException();
+ }
+
+ if (Debug.DEBUG_EVENTS) {
+ String listenerName = listener.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(listener)); //$NON-NLS-1$
+ Debug.println("addBundleListener[" + bundle + "](" + listenerName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ framework.addBundleListener(listener, this);
+ }
+
+ /**
+ * Remove a bundle listener.
+ * The listener is removed from the context bundle's list of listeners.
+ * See {@link #getBundle() getBundle()}
+ * for a definition of context bundle.
+ *
+ * <p>If this method is called with a listener which is not registered,
+ * then this method does nothing.
+ *
+ * @param listener The bundle listener to remove.
+ * @exception java.lang.IllegalStateException
+ * If the bundle context has stopped.
+ */
+ public void removeBundleListener(BundleListener listener) {
+ checkValid();
+ if (listener == null) {
+ throw new IllegalArgumentException();
+ }
+
+ if (Debug.DEBUG_EVENTS) {
+ String listenerName = listener.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(listener)); //$NON-NLS-1$
+ Debug.println("removeBundleListener[" + bundle + "](" + listenerName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ framework.removeBundleListener(listener, this);
+ }
+
+ /**
+ * Add a general framework listener.
+ * {@link FrameworkListener}s are notified of general framework events.
+ * The listener is added to the context bundle's list of listeners.
+ * See {@link #getBundle() getBundle()}
+ * for a definition of context bundle.
+ *
+ * @param listener The framework listener to add.
+ * @exception java.lang.IllegalStateException
+ * If the bundle context has stopped.
+ * @see FrameworkEvent
+ * @see FrameworkListener
+ */
+ public void addFrameworkListener(FrameworkListener listener) {
+ checkValid();
+ if (listener == null) {
+ throw new IllegalArgumentException();
+ }
+
+ if (Debug.DEBUG_EVENTS) {
+ String listenerName = listener.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(listener)); //$NON-NLS-1$
+ Debug.println("addFrameworkListener[" + bundle + "](" + listenerName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ framework.addFrameworkListener(listener, this);
+ }
+
+ /**
+ * Remove a framework listener.
+ * The listener is removed from the context bundle's list of listeners.
+ * See {@link #getBundle() getBundle()}
+ * for a definition of context bundle.
+ *
+ * <p>If this method is called with a listener which is not registered,
+ * then this method does nothing.
+ *
+ * @param listener The framework listener to remove.
+ * @exception java.lang.IllegalStateException
+ * If the bundle context has stopped.
+ */
+ public void removeFrameworkListener(FrameworkListener listener) {
+ checkValid();
+ if (listener == null) {
+ throw new IllegalArgumentException();
+ }
+
+ if (Debug.DEBUG_EVENTS) {
+ String listenerName = listener.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(listener)); //$NON-NLS-1$
+ Debug.println("removeFrameworkListener[" + bundle + "](" + listenerName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ framework.removeFrameworkListener(listener, this);
+ }
+
+ /**
+ * Register a service with multiple names.
+ * This method registers the given service object with the given properties
+ * under the given class names.
+ * A {@link ServiceRegistration} object is returned.
+ * The {@link ServiceRegistration} object is for the private use of the bundle
+ * registering the service and should not be shared with other bundles.
+ * The registering bundle is defined to be the context bundle.
+ * See {@link #getBundle()} for a definition of context bundle.
+ * Other bundles can locate the service by using either the
+ * {@link #getServiceReferences getServiceReferences} or
+ * {@link #getServiceReference getServiceReference} method.
+ *
+ * <p>A bundle can register a service object that implements the
+ * {@link ServiceFactory} interface to
+ * have more flexiblity in providing service objects to different
+ * bundles.
+ *
+ * <p>The following steps are followed to register a service:
+ * <ol>
+ * <li>If the service parameter is not a {@link ServiceFactory},
+ * an <code>IllegalArgumentException</code> is thrown if the
+ * service parameter is not an <code>instanceof</code>
+ * all the classes named.
+ * <li>The service is added to the framework's service registry
+ * and may now be used by other bundles.
+ * <li>A {@link ServiceEvent} of type {@link ServiceEvent#REGISTERED}
+ * is synchronously sent.
+ * <li>A {@link ServiceRegistration} object for this registration
+ * is returned.
+ * </ol>
+ *
+ * @param clazzes The class names under which the service can be located.
+ * The class names in this array will be stored in the service's
+ * properties under the key "objectClass".
+ * @param service The service object or a {@link ServiceFactory} object.
+ * @param properties The properties for this service.
+ * The keys in the properties object must all be Strings.
+ * Changes should not be made to this object after calling this method.
+ * To update the service's properties call the
+ * {@link ServiceRegistration#setProperties ServiceRegistration.setProperties}
+ * method.
+ * This parameter may be <code>null</code> if the service has no properties.
+ * @return A {@link ServiceRegistration} object for use by the bundle
+ * registering the service to update the
+ * service's properties or to unregister the service.
+ * @exception java.lang.IllegalArgumentException If one of the following is true:
+ * <ul>
+ * <li>The service parameter is null.
+ * <li>The service parameter is not a {@link ServiceFactory} and is not an
+ * <code>instanceof</code> all the named classes in the clazzes parameter.
+ * </ul>
+ * @exception java.lang.SecurityException If the caller does not have
+ * {@link ServicePermission} permission to "register" the service for
+ * all the named classes
+ * and the Java runtime environment supports permissions.
+ * @exception java.lang.IllegalStateException
+ * If the bundle context has stopped.
+ * @see ServiceRegistration
+ * @see ServiceFactory
+ */
+ public ServiceRegistration<?> registerService(String[] clazzes, Object service, Dictionary<String, ?> properties) {
+ checkValid();
+ return framework.getServiceRegistry().registerService(this, clazzes, service, properties);
+ }
+
+ /**
+ * Register a service with a single name.
+ * This method registers the given service object with the given properties
+ * under the given class name.
+ *
+ * <p>This method is otherwise identical to
+ * {@link #registerService(java.lang.String[], java.lang.Object, java.util.Dictionary)}
+ * and is provided as a convenience when the service parameter will only be registered
+ * under a single class name.
+ *
+ * @see #registerService(java.lang.String[], java.lang.Object, java.util.Dictionary)
+ */
+ public ServiceRegistration<?> registerService(String clazz, Object service, Dictionary<String, ?> properties) {
+ String[] clazzes = new String[] {clazz};
+
+ return registerService(clazzes, service, properties);
+ }
+
+ /**
+ * Returns a list of <tt>ServiceReference</tt> objects. This method returns a list of
+ * <tt>ServiceReference</tt> objects for services which implement and were registered under
+ * the specified class and match the specified filter criteria.
+ *
+ * <p>The list is valid at the time of the call to this method, however as the Framework is
+ * a very dynamic environment, services can be modified or unregistered at anytime.
+ *
+ * <p><tt>filter</tt> is used to select the registered service whose
+ * properties objects contain keys and values which satisfy the filter.
+ * See {@link Filter}for a description of the filter string syntax.
+ *
+ * <p>If <tt>filter</tt> is <tt>null</tt>, all registered services
+ * are considered to match the filter.
+ * <p>If <tt>filter</tt> cannot be parsed, an {@link InvalidSyntaxException} will
+ * be thrown with a human readable message where the filter became unparsable.
+ *
+ * <p>The following steps are required to select a service:
+ * <ol>
+ * <li>If the Java Runtime Environment supports permissions, the caller is checked for the
+ * <tt>ServicePermission</tt> to get the service with the specified class.
+ * If the caller does not have the correct permission, <tt>null</tt> is returned.
+ * <li>If the filter string is not <tt>null</tt>, the filter string is
+ * parsed and the set of registered services which satisfy the filter is
+ * produced.
+ * If the filter string is <tt>null</tt>, then all registered services
+ * are considered to satisfy the filter.
+ * <li>If <code>clazz</code> is not <tt>null</tt>, the set is further reduced to
+ * those services which are an <tt>instanceof</tt> and were registered under the specified class.
+ * The complete list of classes of which a service is an instance and which
+ * were specified when the service was registered is available from the
+ * service's {@link Constants#OBJECTCLASS}property.
+ * <li>An array of <tt>ServiceReference</tt> to the selected services is returned.
+ * </ol>
+ *
+ * @param clazz The class name with which the service was registered, or
+ * <tt>null</tt> for all services.
+ * @param filter The filter criteria.
+ * @return An array of <tt>ServiceReference</tt> objects, or
+ * <tt>null</tt> if no services are registered which satisfy the search.
+ * @exception InvalidSyntaxException If <tt>filter</tt> contains
+ * an invalid filter string which cannot be parsed.
+ */
+ public ServiceReference<?>[] getServiceReferences(String clazz, String filter) throws InvalidSyntaxException {
+ checkValid();
+ return framework.getServiceRegistry().getServiceReferences(this, clazz, filter, false);
+ }
+
+ public ServiceReference<?>[] getAllServiceReferences(String clazz, String filter) throws InvalidSyntaxException {
+ checkValid();
+ return framework.getServiceRegistry().getServiceReferences(this, clazz, filter, true);
+ }
+
+ /**
+ * Get a service reference.
+ * Retrieves a {@link ServiceReference} for a service
+ * which implements the named class.
+ *
+ * <p>This reference is valid at the time
+ * of the call to this method, but since the framework is a very dynamic
+ * environment, services can be modified or unregistered at anytime.
+ *
+ * <p>This method is provided as a convenience for when the caller is
+ * interested in any service which implements a named class. This method is
+ * the same as calling {@link #getServiceReferences getServiceReferences}
+ * with a <code>null</code> filter string but only a single {@link ServiceReference}
+ * is returned.
+ *
+ * @param clazz The class name which the service must implement.
+ * @return A {@link ServiceReference} object, or <code>null</code>
+ * if no services are registered which implement the named class.
+ * @see #getServiceReferences
+ */
+ public ServiceReference<?> getServiceReference(String clazz) {
+ checkValid();
+
+ return framework.getServiceRegistry().getServiceReference(this, clazz);
+ }
+
+ /**
+ * Get a service's service object.
+ * Retrieves the service object for a service.
+ * A bundle's use of a service is tracked by a
+ * use count. Each time a service's service object is returned by
+ * {@link #getService}, the context bundle's use count for the service
+ * is incremented by one. Each time the service is release by
+ * {@link #ungetService}, the context bundle's use count
+ * for the service is decremented by one.
+ * When a bundle's use count for a service
+ * drops to zero, the bundle should no longer use the service.
+ * See {@link #getBundle()} for a definition of context bundle.
+ *
+ * <p>This method will always return <code>null</code> when the
+ * service associated with this reference has been unregistered.
+ *
+ * <p>The following steps are followed to get the service object:
+ * <ol>
+ * <li>If the service has been unregistered,
+ * <code>null</code> is returned.
+ * <li>The context bundle's use count for this service is incremented by one.
+ * <li>If the context bundle's use count for the service is now one and
+ * the service was registered with a {@link ServiceFactory},
+ * the {@link ServiceFactory#getService ServiceFactory.getService} method
+ * is called to create a service object for the context bundle.
+ * This service object is cached by the framework.
+ * While the context bundle's use count for the service is greater than zero,
+ * subsequent calls to get the services's service object for the context bundle
+ * will return the cached service object.
+ * <br>If the service object returned by the {@link ServiceFactory}
+ * is not an <code>instanceof</code>
+ * all the classes named when the service was registered or
+ * the {@link ServiceFactory} throws an exception,
+ * <code>null</code> is returned and a
+ * {@link FrameworkEvent} of type {@link FrameworkEvent#ERROR} is broadcast.
+ * <li>The service object for the service is returned.
+ * </ol>
+ *
+ * @param reference A reference to the service whose service object is desired.
+ * @return A service object for the service associated with this
+ * reference, or <code>null</code> if the service is not registered.
+ * @exception java.lang.SecurityException If the caller does not have
+ * {@link ServicePermission} permission to "get" the service
+ * using at least one of the named classes the service was registered under
+ * and the Java runtime environment supports permissions.
+ * @exception java.lang.IllegalStateException
+ * If the bundle context has stopped.
+ * @see #ungetService
+ * @see ServiceFactory
+ */
+ public <S> S getService(ServiceReference<S> reference) {
+ checkValid();
+ if (reference == null)
+ throw new NullPointerException("A null service reference is not allowed."); //$NON-NLS-1$
+ synchronized (contextLock) {
+ if (servicesInUse == null)
+ // Cannot predict how many services a bundle will use, start with a small table.
+ servicesInUse = new HashMap<ServiceRegistrationImpl<?>, ServiceUse<?>>(10);
+ }
+
+ @SuppressWarnings("unchecked")
+ S service = (S) framework.getServiceRegistry().getService(this, (ServiceReferenceImpl<S>) reference);
+ return service;
+ }
+
+ /**
+ * Unget a service's service object.
+ * Releases the service object for a service.
+ * If the context bundle's use count for the service is zero, this method
+ * returns <code>false</code>. Otherwise, the context bundle's use count for the
+ * service is decremented by one.
+ * See {@link #getBundle()} for a definition of context bundle.
+ *
+ * <p>The service's service object
+ * should no longer be used and all references to it should be destroyed
+ * when a bundle's use count for the service
+ * drops to zero.
+ *
+ * <p>The following steps are followed to unget the service object:
+ * <ol>
+ * <li>If the context bundle's use count for the service is zero or
+ * the service has been unregistered,
+ * <code>false</code> is returned.
+ * <li>The context bundle's use count for this service is decremented by one.
+ * <li>If the context bundle's use count for the service is now zero and
+ * the service was registered with a {@link ServiceFactory},
+ * the {@link ServiceFactory#ungetService ServiceFactory.ungetService} method
+ * is called to release the service object for the context bundle.
+ * <li><code>true</code> is returned.
+ * </ol>
+ *
+ * @param reference A reference to the service to be released.
+ * @return <code>false</code> if the context bundle's use count for the service
+ * is zero or if the service has been unregistered,
+ * otherwise <code>true</code>.
+ * @exception java.lang.IllegalStateException
+ * If the bundle context has stopped.
+ * @see #getService
+ * @see ServiceFactory
+ */
+ public boolean ungetService(ServiceReference<?> reference) {
+ checkValid();
+
+ return framework.getServiceRegistry().ungetService(this, (ServiceReferenceImpl<?>) reference);
+ }
+
+ /**
+ * 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.
+ * See {@link #getBundle()} for a definition of context bundle.
+ *
+ * <p>If the Java runtime environment supports permissions,
+ * the framework the will ensure that the bundle has
+ * <code>java.io.FilePermission</code> with actions
+ * "read","write","execute","delete" for all files (recursively) in the
+ * persistent storage area provided for the context bundle by the framework.
+ *
+ * @param filename A relative name to the file to be accessed.
+ * @return A <code>File</code> object that represents the requested file or
+ * <code>null</code> if the adaptor does not have file system support.
+ * @exception java.lang.IllegalStateException
+ * If the bundle context has stopped.
+ */
+ public File getDataFile(String filename) {
+ checkValid();
+
+ return (framework.getDataFile(bundle, filename));
+ }
+
+ /**
+ * Call bundle's BundleActivator.start()
+ * This method is called by Bundle.startWorker to start the bundle.
+ *
+ * @exception BundleException if
+ * the bundle has a class that implements the BundleActivator interface,
+ * but Framework couldn't instantiate it, or the BundleActivator.start()
+ * method failed
+ */
+ protected void start() throws BundleException {
+ activator = bundle.loadBundleActivator();
+
+ if (activator != null) {
+ try {
+ startActivator(activator);
+ } catch (BundleException be) {
+ activator = null;
+ throw be;
+ }
+ }
+
+ /* activator completed successfully. We must use this
+ same activator object when we stop this bundle. */
+ }
+
+ /**
+ * Calls the start method of a BundleActivator.
+ * @param bundleActivator that activator to start
+ */
+ protected void startActivator(final BundleActivator bundleActivator) throws BundleException {
+ if (Profile.PROFILE && Profile.STARTUP)
+ Profile.logEnter("BundleContextImpl.startActivator()", null); //$NON-NLS-1$
+ try {
+ AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
+ public Object run() throws Exception {
+ if (bundleActivator != null) {
+ if (Profile.PROFILE && Profile.STARTUP)
+ Profile.logTime("BundleContextImpl.startActivator()", "calling " + bundle.getLocation() + " bundle activator"); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
+ // make sure the context class loader is set correctly
+ Object previousTCCL = setContextFinder();
+ /* Start the bundle synchronously */
+ try {
+ bundleActivator.start(BundleContextImpl.this);
+ } finally {
+ if (previousTCCL != Boolean.FALSE)
+ Thread.currentThread().setContextClassLoader((ClassLoader) previousTCCL);
+ }
+ if (Profile.PROFILE && Profile.STARTUP)
+ Profile.logTime("BundleContextImpl.startActivator()", "returned from " + bundle.getLocation() + " bundle activator"); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
+ }
+ return null;
+ }
+ });
+ } catch (Throwable t) {
+ if (t instanceof PrivilegedActionException) {
+ t = ((PrivilegedActionException) t).getException();
+ }
+
+ if (Debug.DEBUG_GENERAL) {
+ Debug.printStackTrace(t);
+ }
+
+ String clazz = null;
+ clazz = bundleActivator.getClass().getName();
+
+ throw new BundleException(NLS.bind(Msg.BUNDLE_ACTIVATOR_EXCEPTION, new Object[] {clazz, "start", bundle.getSymbolicName() == null ? "" + bundle.getBundleId() : bundle.getSymbolicName()}), BundleException.ACTIVATOR_ERROR, t); //$NON-NLS-1$ //$NON-NLS-2$
+ } finally {
+ if (Profile.PROFILE && Profile.STARTUP)
+ Profile.logExit("BundleContextImpl.startActivator()"); //$NON-NLS-1$
+ }
+ }
+
+ Object setContextFinder() {
+ if (!SET_TCCL)
+ return Boolean.FALSE;
+ Thread currentThread = Thread.currentThread();
+ ClassLoader previousTCCL = currentThread.getContextClassLoader();
+ ClassLoader contextFinder = framework.getContextFinder();
+ if (previousTCCL != contextFinder) {
+ currentThread.setContextClassLoader(framework.getContextFinder());
+ return previousTCCL;
+ }
+ return Boolean.FALSE;
+ }
+
+ /**
+ * Call bundle's BundleActivator.stop()
+ * This method is called by Bundle.stopWorker to stop the bundle.
+ *
+ * @exception BundleException if
+ * the bundle has a class that implements the BundleActivator interface,
+ * and the BundleActivator.stop() method failed
+ */
+ protected void stop() throws BundleException {
+ try {
+ AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
+ public Object run() throws Exception {
+ if (activator != null) {
+ // make sure the context class loader is set correctly
+ Object previousTCCL = setContextFinder();
+ try {
+ /* Stop the bundle synchronously */
+ activator.stop(BundleContextImpl.this);
+ } finally {
+ if (previousTCCL != Boolean.FALSE)
+ Thread.currentThread().setContextClassLoader((ClassLoader) previousTCCL);
+ }
+ }
+ return null;
+ }
+ });
+ } catch (Throwable t) {
+ if (t instanceof PrivilegedActionException) {
+ t = ((PrivilegedActionException) t).getException();
+ }
+
+ if (Debug.DEBUG_GENERAL) {
+ Debug.printStackTrace(t);
+ }
+
+ String clazz = (activator == null) ? "" : activator.getClass().getName(); //$NON-NLS-1$
+
+ throw new BundleException(NLS.bind(Msg.BUNDLE_ACTIVATOR_EXCEPTION, new Object[] {clazz, "stop", bundle.getSymbolicName() == null ? "" + bundle.getBundleId() : bundle.getSymbolicName()}), BundleException.ACTIVATOR_ERROR, t); //$NON-NLS-1$ //$NON-NLS-2$
+ } finally {
+ activator = null;
+ }
+ }
+
+ /**
+ * Return the map of ServiceRegistrationImpl to ServiceUse for services being
+ * used by this context.
+ * @return A map of ServiceRegistrationImpl to ServiceUse for services in use by
+ * this context.
+ */
+ public Map<ServiceRegistrationImpl<?>, ServiceUse<?>> getServicesInUseMap() {
+ synchronized (contextLock) {
+ return servicesInUse;
+ }
+ }
+
+ /**
+ * Bottom level event dispatcher for the BundleContext.
+ *
+ * @param originalListener listener object registered under.
+ * @param l listener to call (may be filtered).
+ * @param action Event class type
+ * @param object Event object
+ */
+ public void dispatchEvent(Object originalListener, Object l, int action, Object object) {
+ // save the bundle ref to a local variable
+ // to avoid interference from another thread closing this context
+ AbstractBundle tmpBundle = bundle;
+ Object previousTCCL = setContextFinder();
+ try {
+ if (isValid()) /* if context still valid */{
+ switch (action) {
+ case Framework.BUNDLEEVENT :
+ case Framework.BUNDLEEVENTSYNC : {
+ BundleListener listener = (BundleListener) l;
+
+ if (Debug.DEBUG_EVENTS) {
+ String listenerName = listener.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(listener)); //$NON-NLS-1$
+ Debug.println("dispatchBundleEvent[" + tmpBundle + "](" + listenerName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ BundleEvent event = (BundleEvent) object;
+ switch (event.getType()) {
+ case Framework.BATCHEVENT_BEGIN : {
+ if (listener instanceof BatchBundleListener)
+ ((BatchBundleListener) listener).batchBegin();
+ break;
+ }
+ case Framework.BATCHEVENT_END : {
+ if (listener instanceof BatchBundleListener)
+ ((BatchBundleListener) listener).batchEnd();
+ break;
+ }
+ default : {
+ listener.bundleChanged((BundleEvent) object);
+ }
+ }
+ break;
+ }
+
+ case ServiceRegistry.SERVICEEVENT : {
+ ServiceEvent event = (ServiceEvent) object;
+
+ ServiceListener listener = (ServiceListener) l;
+ if (Debug.DEBUG_EVENTS) {
+ String listenerName = listener.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(listener)); //$NON-NLS-1$
+ Debug.println("dispatchServiceEvent[" + tmpBundle + "](" + listenerName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ listener.serviceChanged(event);
+
+ break;
+ }
+
+ case Framework.FRAMEWORKEVENT : {
+ FrameworkListener listener = (FrameworkListener) l;
+
+ if (Debug.DEBUG_EVENTS) {
+ String listenerName = listener.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(listener)); //$NON-NLS-1$
+ Debug.println("dispatchFrameworkEvent[" + tmpBundle + "](" + listenerName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ listener.frameworkEvent((FrameworkEvent) object);
+ break;
+ }
+ default : {
+ throw new InternalError();
+ }
+ }
+ }
+ } catch (Throwable t) {
+ if (Debug.DEBUG_GENERAL) {
+ Debug.println("Exception in bottom level event dispatcher: " + t.getMessage()); //$NON-NLS-1$
+ Debug.printStackTrace(t);
+ }
+ // allow the adaptor to handle this unexpected error
+ framework.adaptor.handleRuntimeError(t);
+ publisherror: {
+ if (action == Framework.FRAMEWORKEVENT) {
+ FrameworkEvent event = (FrameworkEvent) object;
+ if (event.getType() == FrameworkEvent.ERROR) {
+ break publisherror; // avoid infinite loop
+ }
+ }
+
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, tmpBundle, t);
+ }
+ } finally {
+ if (previousTCCL != Boolean.FALSE)
+ Thread.currentThread().setContextClassLoader((ClassLoader) previousTCCL);
+ }
+ }
+
+ /**
+ * Construct a Filter object. This filter object may be used
+ * to match a ServiceReference or a Dictionary.
+ * See Filter
+ * for a description of the filter string syntax.
+ *
+ * @param filter The filter string.
+ * @return A Filter object encapsulating the filter string.
+ * @exception InvalidSyntaxException If the filter parameter contains
+ * an invalid filter string which cannot be parsed.
+ */
+ public Filter createFilter(String filter) throws InvalidSyntaxException {
+ checkValid();
+
+ return FilterImpl.newInstance(filter);
+ }
+
+ /**
+ * This method checks that the context is still valid. If the context is
+ * no longer valid, an IllegalStateException is thrown.
+ *
+ * @exception java.lang.IllegalStateException
+ * If the context bundle has stopped.
+ */
+ public void checkValid() {
+ if (!isValid()) {
+ throw new IllegalStateException(Msg.BUNDLE_CONTEXT_INVALID_EXCEPTION);
+ }
+ }
+
+ /**
+ * This method checks that the context is still valid.
+ *
+ * @return true if the context is still valid; false otherwise
+ */
+ protected boolean isValid() {
+ return valid;
+ }
+
+ public Framework getFramework() {
+ return framework;
+ }
+
+ public <S> ServiceRegistration<S> registerService(Class<S> clazz, S service, Dictionary<String, ?> properties) {
+ @SuppressWarnings("unchecked")
+ ServiceRegistration<S> registration = (ServiceRegistration<S>) registerService(clazz.getName(), service, properties);
+ return registration;
+ }
+
+ public <S> ServiceReference<S> getServiceReference(Class<S> clazz) {
+ @SuppressWarnings("unchecked")
+ ServiceReference<S> reference = (ServiceReference<S>) getServiceReference(clazz.getName());
+ return reference;
+ }
+
+ public <S> Collection<ServiceReference<S>> getServiceReferences(Class<S> clazz, String filter) throws InvalidSyntaxException {
+ @SuppressWarnings("unchecked")
+ ServiceReference<S>[] refs = (ServiceReference<S>[]) getServiceReferences(clazz.getName(), filter);
+ if (refs == null) {
+ @SuppressWarnings("unchecked")
+ Collection<ServiceReference<S>> empty = Collections.EMPTY_LIST;
+ return empty;
+ }
+ List<ServiceReference<S>> result = new ArrayList<ServiceReference<S>>(refs.length);
+ for (ServiceReference<S> b : refs) {
+ result.add(b);
+ }
+ return result;
+ }
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/BundleFragment.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/BundleFragment.java
new file mode 100644
index 000000000..98ca8ee24
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/BundleFragment.java
@@ -0,0 +1,334 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2011 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.IOException;
+import java.net.URL;
+import java.util.Enumeration;
+import org.eclipse.osgi.framework.adaptor.BundleData;
+import org.eclipse.osgi.framework.debug.Debug;
+import org.eclipse.osgi.internal.loader.BundleLoader;
+import org.eclipse.osgi.util.NLS;
+import org.osgi.framework.*;
+
+public class BundleFragment extends AbstractBundle {
+
+ /** The resolved host that this fragment is attached to */
+ protected BundleHost[] hosts;
+
+ /**
+ * @param bundledata
+ * @param framework
+ * @throws BundleException
+ */
+ public BundleFragment(BundleData bundledata, Framework framework) throws BundleException {
+ super(bundledata, framework);
+ hosts = null;
+ }
+
+ /**
+ * Load the bundle.
+ */
+ protected void load() {
+ if (Debug.DEBUG_GENERAL) {
+ if ((state & (INSTALLED)) == 0) {
+ Debug.println("Bundle.load called when state != INSTALLED: " + this); //$NON-NLS-1$
+ Debug.printStackTrace(new Exception("Stack trace")); //$NON-NLS-1$
+ }
+ }
+
+ if (framework.isActive()) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null && framework.securityAdmin != null) {
+ domain = framework.securityAdmin.createProtectionDomain(this);
+ }
+ }
+ }
+
+ /**
+ * Reload from a new bundle.
+ * This method must be called while holding the bundles lock.
+ *
+ * @param newBundle Dummy Bundle which contains new data.
+ * @return true if an exported package is "in use". i.e. it has been imported by a bundle
+ */
+ protected boolean reload(AbstractBundle newBundle) {
+ if (Debug.DEBUG_GENERAL) {
+ if ((state & (INSTALLED | RESOLVED)) == 0) {
+ Debug.println("Bundle.reload called when state != INSTALLED | RESOLVED: " + this); //$NON-NLS-1$
+ Debug.printStackTrace(new Exception("Stack trace")); //$NON-NLS-1$
+ }
+ }
+
+ boolean exporting = false;
+ if (framework.isActive()) {
+ if (hosts != null) {
+ if (state == RESOLVED) {
+ exporting = true; // if we have a host we cannot be removed until the host is refreshed
+ hosts = null;
+ state = INSTALLED;
+ }
+ }
+ } else {
+ /* close the outgoing jarfile */
+ try {
+ this.bundledata.close();
+ } catch (IOException e) {
+ // Do Nothing
+ }
+ }
+ if (!exporting) {
+ /* close the outgoing jarfile */
+ try {
+ this.bundledata.close();
+ } catch (IOException e) {
+ // Do Nothing
+ }
+ }
+
+ this.bundledata = newBundle.bundledata;
+ this.bundledata.setBundle(this);
+ // create a new domain for the bundle because its signers/symbolic-name may have changed
+ if (framework.isActive() && System.getSecurityManager() != null && framework.securityAdmin != null)
+ domain = framework.securityAdmin.createProtectionDomain(this);
+ return (exporting);
+ }
+
+ /**
+ * Refresh the bundle. This is called by Framework.refreshPackages.
+ * This method must be called while holding the bundles lock.
+ * this.loader.unimportPackages must have already been called before calling
+ * this method!
+ */
+ protected void refresh() {
+ if (Debug.DEBUG_GENERAL) {
+ if ((state & (UNINSTALLED | INSTALLED | RESOLVED)) == 0) {
+ Debug.println("Bundle.refresh called when state != UNINSTALLED | INSTALLED | RESOLVED: " + this); //$NON-NLS-1$
+ Debug.printStackTrace(new Exception("Stack trace")); //$NON-NLS-1$
+ }
+ }
+
+ if (state == RESOLVED) {
+ hosts = null;
+ state = INSTALLED;
+ // Do not publish UNRESOLVED event here. This is done by caller
+ // to resolve if appropriate.
+ }
+ manifestLocalization = null;
+ }
+
+ /**
+ * Unload the bundle.
+ * This method must be called while holding the bundles lock.
+ *
+ * @return true if an exported package is "in use". i.e. it has been imported by a bundle
+ */
+ protected boolean unload() {
+ if (Debug.DEBUG_GENERAL) {
+ if ((state & (UNINSTALLED | INSTALLED | RESOLVED)) == 0) {
+ Debug.println("Bundle.unload called when state != UNINSTALLED | INSTALLED | RESOLVED: " + this); //$NON-NLS-1$
+ Debug.printStackTrace(new Exception("Stack trace")); //$NON-NLS-1$
+ }
+ }
+
+ boolean exporting = false;
+ if (framework.isActive()) {
+ if (hosts != null) {
+ if (state == RESOLVED) {
+ exporting = true; // if we have a host we cannot be removed until the host is refreshed
+ hosts = null;
+ state = INSTALLED;
+ }
+ domain = null;
+ }
+ }
+ if (!exporting) {
+ try {
+ this.bundledata.close();
+ } catch (IOException e) { // Do Nothing.
+ }
+ }
+
+ return (exporting);
+ }
+
+ /**
+ * This method loads a class from the bundle.
+ *
+ * @param name the name of the desired Class.
+ * @param checkPermission indicates whether a permission check should be done.
+ * @return the resulting Class
+ * @exception java.lang.ClassNotFoundException if the class definition was not found.
+ */
+ protected Class<?> loadClass(String name, boolean checkPermission) throws ClassNotFoundException {
+ if (checkPermission) {
+ try {
+ framework.checkAdminPermission(this, AdminPermission.CLASS);
+ } catch (SecurityException e) {
+ throw new ClassNotFoundException(name, e);
+ }
+ checkValid();
+ }
+ // cannot load a class from a fragment because there is no classloader
+ // associated with fragments.
+ throw new ClassNotFoundException(NLS.bind(Msg.BUNDLE_FRAGMENT_CNFE, name));
+ }
+
+ /**
+ * Find the specified resource in this bundle.
+ *
+ * This bundle's class loader is called to search for the named resource.
+ * If this bundle's state is <tt>INSTALLED</tt>, then only this bundle will
+ * be searched for the specified resource. Imported packages cannot be searched
+ * when a bundle has not been resolved.
+ *
+ * @param name The name of the resource.
+ * See <tt>java.lang.ClassLoader.getResource</tt> for a description of
+ * the format of a resource name.
+ * @return a URL to the named resource, or <tt>null</tt> if the resource could
+ * not be found or if the caller does not have
+ * the <tt>AdminPermission</tt>, and the Java Runtime Environment supports permissions.
+ *
+ * @exception java.lang.IllegalStateException If this bundle has been uninstalled.
+ */
+ public URL getResource(String name) {
+ checkValid();
+ // cannot get a resource for a fragment because there is no classloader
+ // associated with fragments.
+ return (null);
+
+ }
+
+ public Enumeration<URL> getResources(String name) {
+ checkValid();
+ // cannot get a resource for a fragment because there is no classloader
+ // associated with fragments.
+ return null;
+ }
+
+ /**
+ * Internal worker to start a bundle.
+ *
+ * @param options
+ */
+ protected void startWorker(int options) throws BundleException {
+ throw new BundleException(NLS.bind(Msg.BUNDLE_FRAGMENT_START, this), BundleException.INVALID_OPERATION);
+ }
+
+ /**
+ * Internal worker to stop a bundle.
+ *
+ * @param options
+ */
+ protected void stopWorker(int options) throws BundleException {
+ throw new BundleException(NLS.bind(Msg.BUNDLE_FRAGMENT_STOP, this), BundleException.INVALID_OPERATION);
+ }
+
+ /**
+ * Provides a list of {@link ServiceReference}s for the services
+ * registered by this bundle
+ * or <code>null</code> if the bundle has no registered
+ * services.
+ *
+ * <p>The list is valid at the time
+ * of the call to this method, but the framework is a very dynamic
+ * environment and services can be modified or unregistered at anytime.
+ *
+ * @return An array of {@link ServiceReference} or <code>null</code>.
+ * @exception java.lang.IllegalStateException If the
+ * bundle has been uninstalled.
+ * @see ServiceRegistration
+ * @see ServiceReference
+ */
+ public ServiceReference<?>[] getRegisteredServices() {
+ checkValid();
+ // Fragments cannot have a BundleContext and therefore
+ // cannot have any services registered.
+ return null;
+ }
+
+ /**
+ * Provides a list of {@link ServiceReference}s for the
+ * services this bundle is using,
+ * or <code>null</code> if the bundle is not using any services.
+ * A bundle is considered to be using a service if the bundle's
+ * use count for the service is greater than zero.
+ *
+ * <p>The list is valid at the time
+ * of the call to this method, but the framework is a very dynamic
+ * environment and services can be modified or unregistered at anytime.
+ *
+ * @return An array of {@link ServiceReference} or <code>null</code>.
+ * @exception java.lang.IllegalStateException If the
+ * bundle has been uninstalled.
+ * @see ServiceReference
+ */
+ public ServiceReference<?>[] getServicesInUse() {
+ checkValid();
+ // Fragments cannot have a BundleContext and therefore
+ // cannot have any services in use.
+ return null;
+ }
+
+ synchronized BundleHost[] getHosts() {
+ return hosts;
+ }
+
+ protected boolean isFragment() {
+ return true;
+ }
+
+ /**
+ * Adds a host bundle for this fragment.
+ * @param host the BundleHost to add to the set of host bundles
+ */
+ boolean addHost(BundleHost host) {
+ if (host == null)
+ return false;
+ try {
+ host.attachFragment(this);
+ } catch (BundleException be) {
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, host, be);
+ return false;
+ }
+ synchronized (this) {
+ if (hosts == null) {
+ hosts = new BundleHost[] {host};
+ return true;
+ }
+ for (int i = 0; i < hosts.length; i++) {
+ if (host == hosts[i])
+ return true; // already a host
+ }
+ BundleHost[] newHosts = new BundleHost[hosts.length + 1];
+ System.arraycopy(hosts, 0, newHosts, 0, hosts.length);
+ newHosts[newHosts.length - 1] = host;
+ hosts = newHosts;
+ }
+ return true;
+ }
+
+ protected BundleLoader getBundleLoader() {
+ // Fragments cannot have a BundleLoader.
+ return null;
+ }
+
+ /**
+ * Return the current context for this bundle.
+ *
+ * @return BundleContext for this bundle.
+ */
+ protected BundleContextImpl getContext() {
+ // Fragments cannot have a BundleContext.
+ return null;
+ }
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/BundleHost.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/BundleHost.java
new file mode 100644
index 000000000..cfc55b0a3
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/BundleHost.java
@@ -0,0 +1,686 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2011 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.IOException;
+import java.net.URL;
+import java.util.Enumeration;
+import org.eclipse.osgi.framework.adaptor.*;
+import org.eclipse.osgi.framework.debug.Debug;
+import org.eclipse.osgi.framework.log.FrameworkLogEntry;
+import org.eclipse.osgi.internal.loader.BundleLoader;
+import org.eclipse.osgi.internal.loader.BundleLoaderProxy;
+import org.eclipse.osgi.service.resolver.BundleDescription;
+import org.eclipse.osgi.service.resolver.ResolverHookException;
+import org.eclipse.osgi.util.NLS;
+import org.osgi.framework.*;
+
+public class BundleHost extends AbstractBundle {
+ public static final int LAZY_TRIGGER = 0x40000000;
+ /**
+ * The BundleLoader proxy; a lightweight object that acts as a proxy
+ * to the BundleLoader and allows lazy creation of the BundleLoader object
+ */
+ private BundleLoaderProxy proxy;
+
+ /** The BundleContext that represents this Bundle and all of its fragments */
+ protected BundleContextImpl context;
+
+ /** The List of BundleFragments */
+ protected BundleFragment[] fragments;
+
+ public BundleHost(BundleData bundledata, Framework framework) {
+ super(bundledata, framework);
+ context = null;
+ fragments = null;
+ }
+
+ /**
+ * Load the bundle.
+ */
+ protected void load() {
+ if (Debug.DEBUG_GENERAL) {
+ if ((state & (INSTALLED)) == 0) {
+ Debug.println("Bundle.load called when state != INSTALLED: " + this); //$NON-NLS-1$
+ Debug.printStackTrace(new Exception("Stack trace")); //$NON-NLS-1$
+ }
+ if (proxy != null) {
+ Debug.println("Bundle.load called when proxy != null: " + this); //$NON-NLS-1$
+ Debug.printStackTrace(new Exception("Stack trace")); //$NON-NLS-1$
+ }
+ }
+
+ if (framework.isActive()) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null && framework.securityAdmin != null) {
+ domain = framework.securityAdmin.createProtectionDomain(this);
+ }
+ }
+ proxy = null;
+ }
+
+ /**
+ * Reload from a new bundle.
+ * This method must be called while holding the bundles lock.
+ *
+ * @param newBundle Dummy Bundle which contains new data.
+ * @return true if an exported package is "in use". i.e. it has been imported by a bundle
+ */
+ protected boolean reload(AbstractBundle newBundle) {
+ if (Debug.DEBUG_GENERAL) {
+ if ((state & (INSTALLED | RESOLVED)) == 0) {
+ Debug.println("Bundle.reload called when state != INSTALLED | RESOLVED: " + this); //$NON-NLS-1$
+ Debug.printStackTrace(new Exception("Stack trace")); //$NON-NLS-1$
+ }
+ }
+
+ boolean exporting = false;
+
+ if (framework.isActive()) {
+ if (state == RESOLVED) {
+ BundleLoaderProxy curProxy = getLoaderProxy();
+ exporting = curProxy.inUse();
+ if (exporting) {
+ // make sure the BundleLoader is created.
+ curProxy.getBundleLoader().createClassLoader();
+ } else
+ BundleLoader.closeBundleLoader(proxy);
+ state = INSTALLED;
+ proxy = null;
+ fragments = null;
+ }
+
+ } else {
+ /* close the outgoing jarfile */
+ try {
+ this.bundledata.close();
+ } catch (IOException e) {
+ // Do Nothing
+ }
+ }
+ this.bundledata = newBundle.bundledata;
+ this.bundledata.setBundle(this);
+ // create a new domain for the bundle because its signers/symbolic-name may have changed
+ if (framework.isActive() && System.getSecurityManager() != null && framework.securityAdmin != null)
+ domain = framework.securityAdmin.createProtectionDomain(this);
+ return (exporting);
+ }
+
+ /**
+ * Refresh the bundle. This is called by Framework.refreshPackages.
+ * This method must be called while holding the bundles lock.
+ */
+ protected void refresh() {
+ if (Debug.DEBUG_GENERAL) {
+ if ((state & (UNINSTALLED | INSTALLED | RESOLVED)) == 0) {
+ Debug.println("Bundle.reload called when state != UNINSTALLED | INSTALLED | RESOLVED: " + this); //$NON-NLS-1$
+ Debug.printStackTrace(new Exception("Stack trace")); //$NON-NLS-1$
+ }
+ }
+ if (state == RESOLVED) {
+ BundleLoader.closeBundleLoader(proxy);
+ proxy = null;
+ fragments = null;
+ state = INSTALLED;
+ // Do not publish UNRESOLVED event here. This is done by caller
+ // to resolve if appropriate.
+ }
+ manifestLocalization = null;
+ }
+
+ /**
+ * Unload the bundle.
+ * This method must be called while holding the bundles lock.
+ *
+ * @return true if an exported package is "in use". i.e. it has been imported by a bundle
+ */
+ protected boolean unload() {
+ if (Debug.DEBUG_GENERAL) {
+ if ((state & (UNINSTALLED | INSTALLED | RESOLVED)) == 0) {
+ Debug.println("Bundle.unload called when state != UNINSTALLED | INSTALLED | RESOLVED: " + this); //$NON-NLS-1$
+ Debug.printStackTrace(new Exception("Stack trace")); //$NON-NLS-1$
+ }
+ }
+
+ boolean exporting = false;
+
+ if (framework.isActive()) {
+ if (state == RESOLVED) {
+ BundleLoaderProxy curProxy = getLoaderProxy();
+ exporting = curProxy.inUse();
+ if (exporting) {
+ // make sure the BundleLoader is created.
+ curProxy.getBundleLoader().createClassLoader();
+ } else
+ BundleLoader.closeBundleLoader(proxy);
+
+ state = INSTALLED;
+ proxy = null;
+ fragments = null;
+ domain = null;
+ }
+ }
+ if (!exporting) {
+ try {
+ this.bundledata.close();
+ } catch (IOException e) { // Do Nothing.
+ }
+ }
+
+ return (exporting);
+ }
+
+ private BundleLoader checkLoader() {
+ checkValid();
+
+ // check to see if the bundle is resolved
+ if (!isResolved()) {
+ if (!framework.packageAdmin.resolveBundles(new Bundle[] {this})) {
+ return null;
+ }
+ }
+ if (Debug.DEBUG_GENERAL) {
+ if ((state & (STARTING | ACTIVE | STOPPING | RESOLVED)) == 0) {
+ Debug.println("Bundle.checkLoader() called when state != STARTING | ACTIVE | STOPPING | RESOLVED: " + this); //$NON-NLS-1$
+ Debug.printStackTrace(new Exception("Stack trace")); //$NON-NLS-1$
+ }
+ }
+
+ BundleLoader loader = getBundleLoader();
+ if (loader == null) {
+ if (Debug.DEBUG_GENERAL) {
+ Debug.println("Bundle.checkLoader() called when loader == null: " + this); //$NON-NLS-1$
+ Debug.printStackTrace(new Exception("Stack trace")); //$NON-NLS-1$
+ }
+ return null;
+ }
+ return loader;
+ }
+
+ /**
+ * This method loads a class from the bundle.
+ *
+ * @param name the name of the desired Class.
+ * @param checkPermission indicates whether a permission check should be done.
+ * @return the resulting Class
+ * @exception java.lang.ClassNotFoundException if the class definition was not found.
+ */
+ protected Class<?> loadClass(String name, boolean checkPermission) throws ClassNotFoundException {
+ if (checkPermission) {
+ try {
+ framework.checkAdminPermission(this, AdminPermission.CLASS);
+ } catch (SecurityException e) {
+ throw new ClassNotFoundException(name, e);
+ }
+ }
+ BundleLoader loader = checkLoader();
+ if (loader == null)
+ throw new ClassNotFoundException(NLS.bind(Msg.BUNDLE_CNFE_NOT_RESOLVED, name, getBundleData().getLocation()));
+ try {
+ return (loader.loadClass(name));
+ } catch (ClassNotFoundException e) {
+ // this is to support backward compatibility in eclipse
+ // we always attempted to start a bundle even if the class was not found
+ if (!(e instanceof StatusException) && (bundledata.getStatus() & Constants.BUNDLE_LAZY_START) != 0 && !testStateChanging(Thread.currentThread()))
+ try {
+ // only start the bundle if this is a simple CNFE
+ loader.setLazyTrigger();
+ } catch (BundleException be) {
+ framework.adaptor.getFrameworkLog().log(new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.WARNING, 0, be.getMessage(), 0, be, null));
+ }
+ throw e;
+ }
+ }
+
+ /**
+ * Find the specified resource in this bundle.
+ *
+ * This bundle's class loader is called to search for the named resource.
+ * If this bundle's state is <tt>INSTALLED</tt>, then only this bundle will
+ * be searched for the specified resource. Imported packages cannot be searched
+ * when a bundle has not been resolved.
+ *
+ * @param name The name of the resource.
+ * See <tt>java.lang.ClassLoader.getResource</tt> for a description of
+ * the format of a resource name.
+ * @return a URL to the named resource, or <tt>null</tt> if the resource could
+ * not be found or if the caller does not have
+ * the <tt>AdminPermission</tt>, and the Java Runtime Environment supports permissions.
+ *
+ * @exception java.lang.IllegalStateException If this bundle has been uninstalled.
+ */
+ public URL getResource(String name) {
+ BundleLoader loader = null;
+ try {
+ framework.checkAdminPermission(this, AdminPermission.RESOURCE);
+ } catch (SecurityException ee) {
+ return null;
+ }
+ loader = checkLoader();
+ if (loader == null) {
+ Enumeration<URL> result = bundledata.findLocalResources(name);
+ if (result != null && result.hasMoreElements())
+ return result.nextElement();
+ return null;
+ }
+ return loader.findResource(name);
+ }
+
+ public Enumeration<URL> getResources(String name) throws IOException {
+ BundleLoader loader = null;
+ try {
+ framework.checkAdminPermission(this, AdminPermission.RESOURCE);
+ } catch (SecurityException ee) {
+ return null;
+ }
+ Enumeration<URL> result;
+ loader = checkLoader();
+ if (loader == null)
+ result = bundledata.findLocalResources(name);
+ else
+ result = loader.getResources(name);
+ if (result != null && result.hasMoreElements())
+ return result;
+ return null;
+ }
+
+ /**
+ * Internal worker to start a bundle.
+ *
+ * @param options the start options
+ */
+ protected void startWorker(int options) throws BundleException {
+ if ((options & START_TRANSIENT) == 0) {
+ setStatus(Constants.BUNDLE_STARTED, true);
+ setStatus(Constants.BUNDLE_ACTIVATION_POLICY, (options & START_ACTIVATION_POLICY) != 0);
+ if (Debug.MONITOR_ACTIVATION)
+ new Exception("A persistent start has been called on bundle: " + getBundleData()).printStackTrace(); //$NON-NLS-1$
+ }
+ if (!framework.active || (state & ACTIVE) != 0)
+ return;
+ if (getInternalStartLevel() > framework.startLevelManager.getStartLevel()) {
+ if ((options & LAZY_TRIGGER) == 0 && (options & START_TRANSIENT) != 0) {
+ // throw exception if this is a transient start
+ String msg = NLS.bind(Msg.BUNDLE_TRANSIENT_START_ERROR, this);
+ // Use a StatusException to indicate to the lazy starter that this should result in a warning
+ throw new BundleException(msg, BundleException.INVALID_OPERATION, new BundleStatusException(msg, StatusException.CODE_WARNING, this));
+ }
+ return;
+ }
+
+ if (state == INSTALLED) {
+ try {
+ if (!framework.packageAdmin.resolveBundles(new Bundle[] {this}, true))
+ throw getResolutionFailureException();
+ } catch (IllegalStateException e) {
+ // Can happen if the resolver detects a nested resolve process
+ throw new BundleException("Unexpected resolution exception.", BundleException.RESOLVE_ERROR, e); //$NON-NLS-1$
+ } catch (ResolverHookException e) {
+ throw new BundleException("Unexpected resolution exception.", BundleException.REJECTED_BY_HOOK, e.getCause()); //$NON-NLS-1$
+ }
+
+ }
+
+ if ((options & START_ACTIVATION_POLICY) != 0 && (bundledata.getStatus() & Constants.BUNDLE_LAZY_START) != 0) {
+ // the bundle must use the activation policy here.
+ if ((state & RESOLVED) != 0) {
+ // now we must publish the LAZY_ACTIVATION event and return
+ state = STARTING;
+ // release the state change lock before sending lazy activation event (bug 258659)
+ completeStateChange();
+ framework.publishBundleEvent(BundleEvent.LAZY_ACTIVATION, this);
+ }
+ return;
+ }
+
+ if (Debug.DEBUG_GENERAL) {
+ Debug.println("Bundle: Active sl = " + framework.startLevelManager.getStartLevel() + "; Bundle " + getBundleId() + " sl = " + getInternalStartLevel()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ if ((options & LAZY_TRIGGER) != 0) {
+ if ((state & RESOLVED) != 0) {
+ // Should publish the lazy activation event here before the starting event
+ // This can happen if another bundle in the same start-level causes a class load from the lazy start bundle.
+ state = STARTING;
+ // release the state change lock before sending lazy activation event (bug 258659)
+ completeStateChange();
+ framework.publishBundleEvent(BundleEvent.LAZY_ACTIVATION, this);
+ beginStateChange();
+ if (state != STARTING) {
+ // while firing the LAZY_ACTIVATION event some one else caused the bundle to transition
+ // out of STARTING. This could have happened because some listener called start on the bundle
+ // or another class load could have caused the start trigger to get fired again.
+ return;
+ }
+ }
+ }
+ state = STARTING;
+ framework.publishBundleEvent(BundleEvent.STARTING, this);
+ context = getContext();
+ //STARTUP TIMING Start here
+ long start = 0;
+
+ BundleWatcher bundleStats = framework.adaptor.getBundleWatcher();
+ if (bundleStats != null)
+ bundleStats.watchBundle(this, BundleWatcher.START_ACTIVATION);
+ if (Debug.DEBUG_BUNDLE_TIME) {
+ start = System.currentTimeMillis();
+ System.out.println("Starting " + getSymbolicName()); //$NON-NLS-1$
+ }
+
+ try {
+ context.start();
+ startHook();
+ if (framework.active) {
+ state = ACTIVE;
+
+ if (Debug.DEBUG_GENERAL) {
+ Debug.println("->started " + this); //$NON-NLS-1$
+ }
+ // release the state change lock before sending lazy activation event (bug 258659)
+ completeStateChange();
+ framework.publishBundleEvent(BundleEvent.STARTED, this);
+ }
+
+ } catch (BundleException e) {
+ // we must fire the stopping event
+ state = STOPPING;
+ framework.publishBundleEvent(BundleEvent.STOPPING, this);
+
+ stopHook();
+ context.close();
+ context = null;
+
+ state = RESOLVED;
+ // if this is a lazy start bundle that fails to start then
+ // we must fire the stopped event
+ framework.publishBundleEvent(BundleEvent.STOPPED, this);
+ throw e;
+ } finally {
+ if (bundleStats != null)
+ bundleStats.watchBundle(this, BundleWatcher.END_ACTIVATION);
+ if (Debug.DEBUG_BUNDLE_TIME)
+ System.out.println("End starting " + getSymbolicName() + " " + (System.currentTimeMillis() - start)); //$NON-NLS-1$ //$NON-NLS-2$
+
+ }
+
+ if (state == UNINSTALLED) {
+ context.close();
+ context = null;
+ throw new BundleException(NLS.bind(Msg.BUNDLE_UNINSTALLED_EXCEPTION, getBundleData().getLocation()), BundleException.STATECHANGE_ERROR);
+ }
+ }
+
+ /**
+ * @throws BundleException
+ */
+ protected void startHook() throws BundleException {
+ // do nothing by default
+ }
+
+ protected boolean readyToResume() {
+ // Return false if the bundle is not at the correct start-level
+ if (getInternalStartLevel() > framework.startLevelManager.getStartLevel())
+ return false;
+ int status = bundledata.getStatus();
+ // Return false if the bundle is not persistently marked for start
+ if ((status & Constants.BUNDLE_STARTED) == 0)
+ return false;
+ if ((status & Constants.BUNDLE_ACTIVATION_POLICY) == 0 || (status & Constants.BUNDLE_LAZY_START) == 0 || isLazyTriggerSet())
+ return true;
+ if (!isResolved()) {
+ if (framework.getAdaptor().getState().isResolved() || !framework.packageAdmin.resolveBundles(new Bundle[] {this}))
+ // should never transition from UNRESOLVED -> STARTING
+ return false;
+ }
+ // now we can publish the LAZY_ACTIVATION event
+ state = STARTING;
+ // release the state change lock before sending lazy activation event (bug 258659)
+ completeStateChange();
+ framework.publishBundleEvent(BundleEvent.LAZY_ACTIVATION, this);
+ return false;
+ }
+
+ private synchronized boolean isLazyTriggerSet() {
+ if (proxy == null)
+ return false;
+ BundleLoader loader = proxy.getBasicBundleLoader();
+ return loader != null ? loader.isLazyTriggerSet() : false;
+ }
+
+ /**
+ * Create a BundleContext for this bundle.
+ *
+ * @return BundleContext for this bundle.
+ */
+ protected BundleContextImpl createContext() {
+ return (new BundleContextImpl(this));
+ }
+
+ /**
+ * Return the current context for this bundle.
+ *
+ * @return BundleContext for this bundle.
+ */
+ protected synchronized BundleContextImpl getContext() {
+ if (context == null) {
+ // only create the context if we are starting, active or stopping
+ // this is so that SCR can get the context for lazy-start bundles
+ if ((state & (STARTING | ACTIVE | STOPPING)) != 0)
+ context = createContext();
+ }
+ return (context);
+ }
+
+ /**
+ * Internal worker to stop a bundle.
+ *
+ * @param options the stop options
+ */
+ protected void stopWorker(int options) throws BundleException {
+ if ((options & STOP_TRANSIENT) == 0) {
+ setStatus(Constants.BUNDLE_STARTED, false);
+ setStatus(Constants.BUNDLE_ACTIVATION_POLICY, false);
+ if (Debug.MONITOR_ACTIVATION)
+ new Exception("A persistent start has been called on bundle: " + getBundleData()).printStackTrace(); //$NON-NLS-1$
+ }
+ if (framework.active) {
+ if ((state & (STOPPING | RESOLVED | INSTALLED)) != 0) {
+ return;
+ }
+
+ BundleWatcher bundleStats = framework.adaptor.getBundleWatcher();
+ if (bundleStats != null)
+ bundleStats.watchBundle(this, BundleWatcher.START_DEACTIVATION);
+
+ state = STOPPING;
+ framework.publishBundleEvent(BundleEvent.STOPPING, this);
+ try {
+ // context may be null if a lazy-start bundle is STARTING
+ if (context != null)
+ context.stop();
+ } finally {
+ stopHook();
+ if (context != null) {
+ context.close();
+ context = null;
+ }
+
+ checkValid();
+
+ state = RESOLVED;
+
+ if (Debug.DEBUG_GENERAL) {
+ Debug.println("->stopped " + this); //$NON-NLS-1$
+ }
+
+ framework.publishBundleEvent(BundleEvent.STOPPED, this);
+ if (bundleStats != null)
+ bundleStats.watchBundle(this, BundleWatcher.END_DEACTIVATION);
+
+ }
+ }
+ }
+
+ /**
+ * @throws BundleException
+ */
+ protected void stopHook() throws BundleException {
+ // do nothing
+ }
+
+ /**
+ * Provides a list of {@link ServiceReference}s for the services
+ * registered by this bundle
+ * or <code>null</code> if the bundle has no registered
+ * services.
+ *
+ * <p>The list is valid at the time
+ * of the call to this method, but the framework is a very dynamic
+ * environment and services can be modified or unregistered at anytime.
+ *
+ * @return An array of {@link ServiceReference} or <code>null</code>.
+ * @exception java.lang.IllegalStateException If the
+ * bundle has been uninstalled.
+ * @see ServiceRegistration
+ * @see ServiceReference
+ */
+ public ServiceReference<?>[] getRegisteredServices() {
+ checkValid();
+
+ if (context == null) {
+ return null;
+ }
+
+ return context.getFramework().getServiceRegistry().getRegisteredServices(context);
+ }
+
+ /**
+ * Provides a list of {@link ServiceReference}s for the
+ * services this bundle is using,
+ * or <code>null</code> if the bundle is not using any services.
+ * A bundle is considered to be using a service if the bundle's
+ * use count for the service is greater than zero.
+ *
+ * <p>The list is valid at the time
+ * of the call to this method, but the framework is a very dynamic
+ * environment and services can be modified or unregistered at anytime.
+ *
+ * @return An array of {@link ServiceReference} or <code>null</code>.
+ * @exception java.lang.IllegalStateException If the
+ * bundle has been uninstalled.
+ * @see ServiceReference
+ */
+ public ServiceReference<?>[] getServicesInUse() {
+ checkValid();
+
+ if (context == null) {
+ return null;
+ }
+
+ return context.getFramework().getServiceRegistry().getServicesInUse(context);
+ }
+
+ public BundleFragment[] getFragments() {
+ synchronized (framework.bundles) {
+ if (fragments == null)
+ return null;
+ BundleFragment[] result = new BundleFragment[fragments.length];
+ System.arraycopy(fragments, 0, result, 0, result.length);
+ return result;
+ }
+ }
+
+ /**
+ * Attaches a fragment to this BundleHost. Fragments must be attached to
+ * the host by ID order. If the ClassLoader of the host is already created
+ * then the fragment must be attached to the host ClassLoader
+ * @param fragment The fragment bundle to attach
+ * return true if the fragment successfully attached; false if the fragment
+ * could not be logically inserted at the end of the fragment chain.
+ */
+ protected void attachFragment(BundleFragment fragment) throws BundleException {
+ // do not force the creation of the bundle loader here
+ BundleLoader loader = getLoaderProxy().getBasicBundleLoader();
+ // If the Host ClassLoader exists then we must attach
+ // the fragment to the ClassLoader.
+ if (loader != null)
+ loader.attachFragment(fragment);
+
+ if (fragments == null) {
+ fragments = new BundleFragment[] {fragment};
+ } else {
+ boolean inserted = false;
+ // We must keep our fragments ordered by bundle ID; or
+ // install order.
+ BundleFragment[] newFragments = new BundleFragment[fragments.length + 1];
+ for (int i = 0; i < fragments.length; i++) {
+ if (fragment == fragments[i])
+ return; // this fragment is already attached
+ // need to flush the other attached fragment manifest caches in case the attaching fragment provides translations (bug 339211)
+ fragments[i].manifestLocalization = null;
+ if (!inserted && fragment.getBundleId() < fragments[i].getBundleId()) {
+ // if the loader has already been created
+ // then we cannot attach a fragment into the middle
+ // of the fragment chain.
+ if (loader != null) {
+ throw new BundleException(NLS.bind(Msg.BUNDLE_LOADER_ATTACHMENT_ERROR, fragments[i].getSymbolicName(), getSymbolicName()), BundleException.INVALID_OPERATION);
+ }
+ newFragments[i] = fragment;
+ inserted = true;
+ }
+ newFragments[inserted ? i + 1 : i] = fragments[i];
+ }
+ if (!inserted)
+ newFragments[newFragments.length - 1] = fragment;
+ fragments = newFragments;
+ }
+ // need to flush the manifest cache in case the attaching fragment provides translations
+ manifestLocalization = null;
+ }
+
+ protected BundleLoader getBundleLoader() {
+ BundleLoaderProxy curProxy = getLoaderProxy();
+ return curProxy == null ? null : curProxy.getBundleLoader();
+ }
+
+ public synchronized BundleLoaderProxy getLoaderProxy() {
+ if (proxy != null)
+ return proxy;
+ BundleDescription bundleDescription = getBundleDescription();
+ if (bundleDescription == null)
+ return null;
+ proxy = new BundleLoaderProxy(this, bundleDescription);
+ // Note that BundleLoaderProxy is a BundleReference
+ // this is necessary to ensure the resolver can continue
+ // to provide BundleRevision objects to resolver hooks.
+ bundleDescription.setUserObject(proxy);
+ return proxy;
+ }
+
+ /**
+ * Gets the class loader for the host bundle. This may end up
+ * creating the bundle class loader if it was not already created.
+ * A null value may be returned if the bundle is not resolved.
+ * @return the bundle class loader or null if the bundle is not resolved.
+ */
+ public ClassLoader getClassLoader() {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ sm.checkPermission(new RuntimePermission("getClassLoader")); //$NON-NLS-1$
+ BundleLoaderProxy curProxy = getLoaderProxy();
+ BundleLoader loader = curProxy == null ? null : curProxy.getBundleLoader();
+ BundleClassLoader bcl = loader == null ? null : loader.createClassLoader();
+ return (bcl instanceof ClassLoader) ? (ClassLoader) bcl : null;
+ }
+
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/BundleRepository.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/BundleRepository.java
new file mode 100644
index 000000000..f10edf828
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/BundleRepository.java
@@ -0,0 +1,196 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2011 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
+ * Rob Harrop - SpringSource Inc. (bug 247521)
+ *******************************************************************************/
+
+package org.eclipse.osgi.framework.internal.core;
+
+import java.util.*;
+import org.eclipse.osgi.framework.util.KeyedHashSet;
+import org.osgi.framework.Version;
+
+/**
+ * The BundleRepository holds all installed Bundle object for the
+ * Framework. The BundleRepository is also used to mark and unmark
+ * bundle dependancies.
+ *
+ * <p>
+ * This class is internally synchronized and supports client locking. Clients
+ * wishing to perform threadsafe composite operations on instances of this
+ * class can synchronize on the instance itself when doing these operations.
+ */
+public final class BundleRepository {
+ /** bundles by install order */
+ private List<AbstractBundle> bundlesByInstallOrder;
+
+ /** bundles keyed by bundle Id */
+ private KeyedHashSet bundlesById;
+
+ /** bundles keyed by SymbolicName */
+ private Map<String, AbstractBundle[]> bundlesBySymbolicName;
+
+ public BundleRepository(int initialCapacity) {
+ synchronized (this) {
+ bundlesByInstallOrder = new ArrayList<AbstractBundle>(initialCapacity);
+ bundlesById = new KeyedHashSet(initialCapacity, true);
+ bundlesBySymbolicName = new HashMap<String, AbstractBundle[]>(initialCapacity);
+ }
+ }
+
+ /**
+ * Gets a list of bundles ordered by install order.
+ * @return List of bundles by install order.
+ */
+ public synchronized List<AbstractBundle> getBundles() {
+ return bundlesByInstallOrder;
+ }
+
+ /**
+ * Gets a bundle by its bundle Id.
+ * @param bundleId
+ * @return a bundle with the specified id or null if one does not exist
+ */
+ public synchronized AbstractBundle getBundle(long bundleId) {
+ Long key = new Long(bundleId);
+ return (AbstractBundle) bundlesById.getByKey(key);
+ }
+
+ public synchronized AbstractBundle[] getBundles(String symbolicName) {
+ if (Constants.SYSTEM_BUNDLE_SYMBOLICNAME.equals(symbolicName))
+ symbolicName = Constants.getInternalSymbolicName();
+ return bundlesBySymbolicName.get(symbolicName);
+ }
+
+ @SuppressWarnings("unchecked")
+ public synchronized List<AbstractBundle> getBundles(String symbolicName, Version version) {
+ AbstractBundle[] bundles = getBundles(symbolicName);
+ List<AbstractBundle> result = null;
+ if (bundles != null) {
+ if (bundles.length > 0) {
+ for (int i = 0; i < bundles.length; i++) {
+ if (bundles[i].getVersion().equals(version)) {
+ if (result == null)
+ result = new ArrayList<AbstractBundle>();
+ result.add(bundles[i]);
+ }
+ }
+ }
+ }
+ return result == null ? Collections.EMPTY_LIST : result;
+ }
+
+ public synchronized void add(AbstractBundle bundle) {
+ bundlesByInstallOrder.add(bundle);
+ bundlesById.add(bundle);
+ addSymbolicName(bundle);
+ }
+
+ private void addSymbolicName(AbstractBundle bundle) {
+ String symbolicName = bundle.getSymbolicName();
+ if (symbolicName == null)
+ return;
+ AbstractBundle[] bundles = bundlesBySymbolicName.get(symbolicName);
+ if (bundles == null) {
+ // making the initial capacity on this 1 since it
+ // should be rare that multiple version exist
+ bundles = new AbstractBundle[1];
+ bundles[0] = bundle;
+ bundlesBySymbolicName.put(symbolicName, bundles);
+ return;
+ }
+
+ List<AbstractBundle> list = new ArrayList<AbstractBundle>(bundles.length + 1);
+ // find place to insert the bundle
+ Version newVersion = bundle.getVersion();
+ boolean added = false;
+ for (int i = 0; i < bundles.length; i++) {
+ AbstractBundle oldBundle = bundles[i];
+ Version oldVersion = oldBundle.getVersion();
+ if (!added && newVersion.compareTo(oldVersion) >= 0) {
+ added = true;
+ list.add(bundle);
+ }
+ list.add(oldBundle);
+ }
+ if (!added) {
+ list.add(bundle);
+ }
+
+ bundles = new AbstractBundle[list.size()];
+ list.toArray(bundles);
+ bundlesBySymbolicName.put(symbolicName, bundles);
+ }
+
+ public synchronized boolean remove(AbstractBundle bundle) {
+ // remove by bundle ID
+ boolean found = bundlesById.remove(bundle);
+ if (!found)
+ return false;
+
+ // remove by install order
+ bundlesByInstallOrder.remove(bundle);
+ // remove by symbolic name
+ String symbolicName = bundle.getSymbolicName();
+ if (symbolicName == null)
+ return true;
+ removeSymbolicName(symbolicName, bundle);
+ return true;
+ }
+
+ private void removeSymbolicName(String symbolicName, AbstractBundle bundle) {
+ AbstractBundle[] bundles = bundlesBySymbolicName.get(symbolicName);
+ if (bundles == null)
+ return;
+
+ // found some bundles with the global name.
+ // remove all references to the specified bundle.
+ int numRemoved = 0;
+ for (int i = 0; i < bundles.length; i++) {
+ if (bundle == bundles[i]) {
+ numRemoved++;
+ bundles[i] = null;
+ }
+ }
+ if (numRemoved > 0) {
+ if (bundles.length - numRemoved <= 0) {
+ // no bundles left in the array remove the array from the hash
+ bundlesBySymbolicName.remove(symbolicName);
+ } else {
+ // create a new array with the null entries removed.
+ AbstractBundle[] newBundles = new AbstractBundle[bundles.length - numRemoved];
+ int indexCnt = 0;
+ for (int i = 0; i < bundles.length; i++) {
+ if (bundles[i] != null) {
+ newBundles[indexCnt] = bundles[i];
+ indexCnt++;
+ }
+ }
+ bundlesBySymbolicName.put(symbolicName, newBundles);
+ }
+ }
+ }
+
+ public synchronized void update(String oldSymbolicName, AbstractBundle bundle) {
+ if (oldSymbolicName != null) {
+ if (!oldSymbolicName.equals(bundle.getSymbolicName())) {
+ removeSymbolicName(oldSymbolicName, bundle);
+ addSymbolicName(bundle);
+ }
+ } else {
+ addSymbolicName(bundle);
+ }
+ }
+
+ public synchronized void removeAllBundles() {
+ bundlesByInstallOrder.clear();
+ bundlesById = new KeyedHashSet();
+ bundlesBySymbolicName.clear();
+ }
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/BundleResourceHandler.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/BundleResourceHandler.java
new file mode 100644
index 000000000..68d28e9ca
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/BundleResourceHandler.java
@@ -0,0 +1,304 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 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.IOException;
+import java.net.*;
+import org.eclipse.osgi.baseadaptor.BaseAdaptor;
+import org.eclipse.osgi.baseadaptor.bundlefile.BundleEntry;
+import org.eclipse.osgi.baseadaptor.loader.BaseClassLoader;
+import org.eclipse.osgi.framework.adaptor.FrameworkAdaptor;
+import org.eclipse.osgi.framework.internal.protocol.ProtocolActivator;
+import org.eclipse.osgi.internal.baseadaptor.AdaptorMsg;
+import org.eclipse.osgi.internal.loader.BundleLoader;
+import org.eclipse.osgi.util.NLS;
+import org.osgi.framework.*;
+
+/**
+ * URLStreamHandler the bundleentry and bundleresource protocols.
+ */
+
+public abstract class BundleResourceHandler extends URLStreamHandler implements ProtocolActivator {
+ public static final String SECURITY_CHECKED = "SECURITY_CHECKED"; //$NON-NLS-1$
+ public static final String SECURITY_UNCHECKED = "SECURITY_UNCHECKED"; //$NON-NLS-1$
+ public static final String BID_FWKID_SEPARATOR = ".fwk"; //$NON-NLS-1$
+ private BaseAdaptor adaptor;
+ protected BundleEntry bundleEntry;
+
+ /**
+ * Constructor for a bundle protocol resource URLStreamHandler.
+ */
+ public BundleResourceHandler() {
+ this(null, null);
+ }
+
+ public BundleResourceHandler(BundleEntry bundleEntry, BaseAdaptor adaptor) {
+ this.bundleEntry = bundleEntry;
+ this.adaptor = adaptor;
+ }
+
+ public void start(BundleContext context, FrameworkAdaptor baseAdaptor) {
+ this.adaptor = (BaseAdaptor) baseAdaptor;
+ }
+
+ /**
+ * Parse reference URL.
+ */
+ protected void parseURL(URL url, String str, int start, int end) {
+ if (end < start)
+ return;
+ if (url.getPath() != null)
+ // A call to a URL constructor has been made that uses an authorized URL as its context.
+ // Null out bundleEntry because it will not be valid for the new path
+ bundleEntry = null;
+ String spec = ""; //$NON-NLS-1$
+ if (start < end)
+ spec = str.substring(start, end);
+ end -= start;
+ //Default is to use path and bundleId from context
+ String path = url.getPath();
+ String host = url.getHost();
+ int resIndex = url.getPort();
+ if (resIndex < 0) // -1 indicates port was not set; must default to 0
+ resIndex = 0;
+ int pathIdx = 0;
+ if (spec.startsWith("//")) { //$NON-NLS-1$
+ int bundleIdIdx = 2;
+ pathIdx = spec.indexOf('/', bundleIdIdx);
+ if (pathIdx == -1) {
+ pathIdx = end;
+ // Use default
+ path = ""; //$NON-NLS-1$
+ }
+ int bundleIdEnd = spec.indexOf(':', bundleIdIdx);
+ if (bundleIdEnd > pathIdx || bundleIdEnd == -1)
+ bundleIdEnd = pathIdx;
+ if (bundleIdEnd < pathIdx - 1)
+ try {
+ resIndex = Integer.parseInt(spec.substring(bundleIdEnd + 1, pathIdx));
+ } catch (NumberFormatException e) {
+ // do nothing; results in resIndex == 0
+ }
+ host = spec.substring(bundleIdIdx, bundleIdEnd);
+ }
+ if (pathIdx < end && spec.charAt(pathIdx) == '/')
+ path = spec.substring(pathIdx, end);
+ else if (end > pathIdx) {
+ if (path == null || path.equals("")) //$NON-NLS-1$
+ path = "/"; //$NON-NLS-1$
+ int last = path.lastIndexOf('/') + 1;
+ if (last == 0)
+ path = spec.substring(pathIdx, end);
+ else
+ path = path.substring(0, last) + spec.substring(pathIdx, end);
+ }
+ if (path == null)
+ path = ""; //$NON-NLS-1$
+ //modify path if there's any relative references
+ // see RFC2396 Section 5.2
+ // Note: For ".." references above the root the approach taken is removing them from the resolved path
+ if (path.endsWith("/.") || path.endsWith("/..")) //$NON-NLS-1$ //$NON-NLS-2$
+ path = path + '/';
+ int dotIndex;
+ while ((dotIndex = path.indexOf("/./")) >= 0) //$NON-NLS-1$
+ path = path.substring(0, dotIndex + 1) + path.substring(dotIndex + 3);
+ while ((dotIndex = path.indexOf("/../")) >= 0) { //$NON-NLS-1$
+ if (dotIndex != 0)
+ path = path.substring(0, path.lastIndexOf('/', dotIndex - 1)) + path.substring(dotIndex + 3);
+ else
+ path = path.substring(dotIndex + 3);
+ }
+ while ((dotIndex = path.indexOf("//")) >= 0) //$NON-NLS-1$
+ path = path.substring(0, dotIndex + 1) + path.substring(dotIndex + 2);
+
+ // Check the permission of the caller to see if they
+ // are allowed access to the resource.
+ String authorized = SECURITY_UNCHECKED;
+ long bundleId = getBundleID(host);
+ Bundle bundle = adaptor == null ? null : adaptor.getBundle(bundleId);
+ if (checkAuthorization(bundle))
+ authorized = SECURITY_CHECKED;
+ // Always force the use of the hash from the adaptor
+ if (adaptor != null)
+ host = Long.toString(bundleId) + BID_FWKID_SEPARATOR + Integer.toString(adaptor.hashCode());
+ // Setting the authority portion of the URL to SECURITY_ATHORIZED
+ // ensures that this URL was created by using this parseURL
+ // method. The openConnection method will only open URLs
+ // that have the authority set to this.
+ setURL(url, url.getProtocol(), host, resIndex, authorized, null, path, null, url.getRef());
+ }
+
+ /**
+ * Establishes a connection to the resource specified by <code>URL</code>.
+ * Since different protocols may have unique ways of connecting, it must be
+ * overridden by the subclass.
+ *
+ * @return java.net.URLConnection
+ * @param url java.net.URL
+ *
+ * @exception IOException thrown if an IO error occurs during connection establishment
+ */
+ protected URLConnection openConnection(URL url) throws IOException {
+ if (bundleEntry != null) // if the bundleEntry is not null then return quick
+ return (new BundleURLConnection(url, bundleEntry));
+
+ String host = url.getHost();
+ if (host == null) {
+ throw new IOException(NLS.bind(AdaptorMsg.URL_NO_BUNDLE_ID, url.toExternalForm()));
+ }
+ AbstractBundle bundle = null;
+ long bundleID;
+ try {
+ bundleID = getBundleID(host);
+ } catch (NumberFormatException nfe) {
+ throw (MalformedURLException) new MalformedURLException(NLS.bind(AdaptorMsg.URL_INVALID_BUNDLE_ID, host)).initCause(nfe);
+ }
+ bundle = adaptor == null ? null : (AbstractBundle) adaptor.getBundle(bundleID);
+ if (bundle == null)
+ throw new IOException(NLS.bind(AdaptorMsg.URL_NO_BUNDLE_FOUND, url.toExternalForm()));
+ // check to make sure that this URL was created using the
+ // parseURL method. This ensures the security check was done
+ // at URL construction.
+ if (!url.getAuthority().equals(SECURITY_CHECKED)) {
+ // No admin security check was made better check now.
+ checkAuthorization(bundle);
+ }
+ return (new BundleURLConnection(url, findBundleEntry(url, bundle)));
+ }
+
+ /**
+ * Finds the bundle entry for this protocal. This is handled
+ * differently for Bundle.gerResource() and Bundle.getEntry()
+ * because getResource uses the bundle classloader and getEntry
+ * only used the base bundle file.
+ * @param url The URL to find the BundleEntry for.
+ * @return the bundle entry
+ */
+ abstract protected BundleEntry findBundleEntry(URL url, AbstractBundle bundle) throws IOException;
+
+ /**
+ * Converts a bundle URL to a String.
+ *
+ * @param url the URL.
+ * @return a string representation of the URL.
+ */
+ protected String toExternalForm(URL url) {
+ StringBuffer result = new StringBuffer(url.getProtocol());
+ result.append("://"); //$NON-NLS-1$
+
+ String host = url.getHost();
+ if ((host != null) && (host.length() > 0))
+ result.append(host);
+ int index = url.getPort();
+ if (index > 0)
+ result.append(':').append(index);
+
+ String path = url.getPath();
+ if (path != null) {
+ if ((path.length() > 0) && (path.charAt(0) != '/')) /* if name doesn't have a leading slash */
+ {
+ result.append("/"); //$NON-NLS-1$
+ }
+
+ result.append(path);
+ }
+ String ref = url.getRef();
+ if (ref != null && ref.length() > 0)
+ result.append('#').append(ref);
+
+ return (result.toString());
+ }
+
+ protected int hashCode(URL url) {
+ int hash = 0;
+ String protocol = url.getProtocol();
+ if (protocol != null)
+ hash += protocol.hashCode();
+
+ String host = url.getHost();
+ if (host != null)
+ hash += host.hashCode();
+
+ hash += url.getPort();
+
+ String path = url.getPath();
+ if (path != null)
+ hash += path.hashCode();
+
+ if (adaptor != null)
+ hash += adaptor.hashCode();
+ return hash;
+ }
+
+ protected boolean equals(URL url1, URL url2) {
+ return sameFile(url1, url2);
+ }
+
+ protected synchronized InetAddress getHostAddress(URL url) {
+ return null;
+ }
+
+ protected boolean hostsEqual(URL url1, URL url2) {
+ String host1 = url1.getHost();
+ String host2 = url2.getHost();
+ if (host1 != null && host2 != null)
+ return host1.equalsIgnoreCase(host2);
+ return (host1 == null && host2 == null);
+ }
+
+ protected boolean sameFile(URL url1, URL url2) {
+ // do a hashcode test to allow each handler to check the adaptor first
+ if (url1.hashCode() != url2.hashCode())
+ return false;
+ String p1 = url1.getProtocol();
+ String p2 = url2.getProtocol();
+ if (!((p1 == p2) || (p1 != null && p1.equalsIgnoreCase(p2))))
+ return false;
+
+ if (!hostsEqual(url1, url2))
+ return false;
+
+ if (url1.getPort() != url2.getPort())
+ return false;
+
+ String path1 = url1.getPath();
+ String path2 = url2.getPath();
+ if (!((path1 == path2) || (path1 != null && path1.equals(path2))))
+ return false;
+
+ return true;
+ // note that the authority is not checked here because it can be different for two
+ // URLs depending on how they were constructed.
+ }
+
+ protected boolean checkAuthorization(Bundle bundle) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm == null)
+ return true;
+ if (bundle == null)
+ return false;
+ sm.checkPermission(new AdminPermission(bundle, AdminPermission.RESOURCE));
+ return true;
+ }
+
+ protected static BaseClassLoader getBundleClassLoader(AbstractBundle bundle) {
+ BundleLoader loader = bundle.getBundleLoader();
+ if (loader == null)
+ return null;
+ return (BaseClassLoader) loader.createClassLoader();
+ }
+
+ private long getBundleID(String host) {
+ int dotIndex = host.indexOf('.');
+ return (dotIndex >= 0 && dotIndex < host.length() - 1) ? Long.parseLong(host.substring(0, dotIndex)) : Long.parseLong(host);
+ }
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/BundleSource.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/BundleSource.java
new file mode 100644
index 000000000..a27e1ee9a
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/BundleSource.java
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * 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.IOException;
+import java.io.InputStream;
+import java.net.URLConnection;
+
+/**
+ * BundleSource class to wrap in InputStream.
+ *
+ * <p>This class implements a URLConnection which
+ * wraps an InputStream.
+ */
+public class BundleSource extends URLConnection {
+ private InputStream in;
+
+ protected BundleSource(InputStream in) {
+ super(null);
+ this.in = in;
+ }
+
+ /**
+ * @throws IOException
+ */
+ public void connect() throws IOException {
+ connected = true;
+ }
+
+ /**
+ * @throws IOException
+ */
+ public InputStream getInputStream() throws IOException {
+ return (in);
+ }
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/BundleURLConnection.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/BundleURLConnection.java
new file mode 100644
index 000000000..c0163c4cc
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/BundleURLConnection.java
@@ -0,0 +1,131 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 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.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import org.eclipse.osgi.baseadaptor.bundlefile.BundleEntry;
+import org.eclipse.osgi.internal.baseadaptor.AdaptorMsg;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * URLConnection for BundleClassLoader resources.
+ */
+
+public class BundleURLConnection extends URLConnection {
+ /** BundleEntry that the URL is associated. */
+ protected final BundleEntry bundleEntry;
+
+ /** InputStream for this URLConnection. */
+ protected InputStream in;
+
+ /** content type for this URLConnection */
+ protected String contentType;
+
+ /**
+ * Constructor for a BundleClassLoader resource URLConnection.
+ *
+ * @param url URL for this URLConnection.
+ * @param bundleEntry BundleEntry that the URLConnection is associated.
+ */
+ public BundleURLConnection(URL url, BundleEntry bundleEntry) {
+ super(url);
+
+ this.bundleEntry = bundleEntry;
+ this.in = null;
+ this.contentType = null;
+ }
+
+ public synchronized void connect() throws IOException {
+ if (!connected) {
+ if (bundleEntry != null) {
+ in = bundleEntry.getInputStream();
+ connected = true;
+ } else {
+ throw new IOException(NLS.bind(AdaptorMsg.RESOURCE_NOT_FOUND_EXCEPTION, url));
+ }
+ }
+ }
+
+ public int getContentLength() {
+ return ((int) bundleEntry.getSize());
+ }
+
+ public String getContentType() {
+ if (contentType == null) {
+ contentType = guessContentTypeFromName(bundleEntry.getName());
+
+ if (contentType == null) {
+ if (!connected) {
+ try {
+ connect();
+ } catch (IOException e) {
+ return (null);
+ }
+ }
+ try {
+ if (in.markSupported())
+ contentType = guessContentTypeFromStream(in);
+ } catch (IOException e) {
+ // do nothing
+ }
+ }
+ }
+
+ return (contentType);
+ }
+
+ public boolean getDoInput() {
+ return (true);
+ }
+
+ public boolean getDoOutput() {
+ return (false);
+ }
+
+ public InputStream getInputStream() throws IOException {
+ if (!connected) {
+ connect();
+ }
+
+ return (in);
+ }
+
+ public long getLastModified() {
+ long lastModified = bundleEntry.getTime();
+
+ if (lastModified == -1) {
+ return (0);
+ }
+
+ return (lastModified);
+ }
+
+ /**
+ * Converts the URL to a common local URL protocol (i.e file: or jar: protocol)
+ * @return the local URL using a common local protocol
+ */
+ public URL getLocalURL() {
+ return bundleEntry.getLocalURL();
+ }
+
+ /**
+ * Converts the URL to a URL that uses the file: protocol. The content of this
+ * URL may be downloaded or extracted onto the local filesystem to create a file URL.
+ * @return the local URL that uses the file: protocol
+ */
+ public URL getFileURL() {
+ return bundleEntry.getFileURL();
+ }
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/ConsoleManager.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/ConsoleManager.java
new file mode 100644
index 000000000..14b3f8f3a
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/ConsoleManager.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2011 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 org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+
+public class ConsoleManager {
+
+ public static final String PROP_CONSOLE = "osgi.console"; //$NON-NLS-1$
+ private static final String PROP_SYSTEM_IN_OUT = "console.systemInOut"; //$NON-NLS-1$
+ private static final String CONSOLE_NAME = "OSGi Console"; //$NON-NLS-1$
+ public static final String CONSOLE_BUNDLE = "org.eclipse.equinox.console"; //$NON-NLS-1$
+ public static final String PROP_CONSOLE_ENABLED = "osgi.console.enable.builtin"; //$NON-NLS-1$
+
+ private final Framework framework;
+ private final String consoleBundle;
+ private final String consolePort;
+
+ public ConsoleManager(Framework framework, String consolePropValue) {
+ String port = null;
+ if (consolePropValue != null) {
+ int index = consolePropValue.lastIndexOf(":"); //$NON-NLS-1$
+ port = consolePropValue.substring(index + 1);
+ }
+ this.consolePort = port != null ? port.trim() : port;
+ String enabled = FrameworkProperties.getProperty(PROP_CONSOLE_ENABLED, CONSOLE_BUNDLE);
+ this.framework = framework;
+ if (!"true".equals(enabled) || "none".equals(consolePort)) { //$NON-NLS-1$ //$NON-NLS-2$
+ this.consoleBundle = "false".equals(enabled) ? CONSOLE_BUNDLE : enabled; //$NON-NLS-1$
+ if (consolePort == null || consolePort.length() > 0) {
+ // no -console was specified or it has specified none or a port for telnet;
+ // need to make sure the gogo shell does not create an interactive console on standard in/out
+ FrameworkProperties.setProperty("gosh.args", "--nointeractive"); //$NON-NLS-1$//$NON-NLS-2$
+ } else {
+ // Need to make sure we don't shutdown the framework if no console is around (bug 362412)
+ FrameworkProperties.setProperty("gosh.args", "--noshutdown"); //$NON-NLS-1$//$NON-NLS-2$
+ }
+ return;
+ }
+ this.consoleBundle = "unknown"; //$NON-NLS-1$
+ }
+
+ public static ConsoleManager startConsole(Framework framework) {
+ ConsoleManager consoleManager = new ConsoleManager(framework, FrameworkProperties.getProperty(PROP_CONSOLE));
+ return consoleManager;
+ }
+
+ public void checkForConsoleBundle() throws BundleException {
+ if ("none".equals(consolePort)) //$NON-NLS-1$
+ return;
+ // otherwise we need to check for the equinox console bundle and start it
+ Bundle[] consoles = framework.getBundleBySymbolicName(consoleBundle);
+ if (consoles == null || consoles.length == 0) {
+ if (consolePort != null)
+ throw new BundleException("Could not find bundle: " + consoleBundle, BundleException.UNSUPPORTED_OPERATION); //$NON-NLS-1$
+ return;
+ }
+ try {
+ consoles[0].start(Bundle.START_TRANSIENT);
+ } catch (BundleException e) {
+ throw new BundleException("Could not start bundle: " + consoleBundle, BundleException.UNSUPPORTED_OPERATION, e); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Stops the OSGi Command console
+ *
+ */
+ public void stopConsole() {
+ // nothing
+ }
+
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/Constants.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/Constants.java
new file mode 100644
index 000000000..1bd279453
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/Constants.java
@@ -0,0 +1,254 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2011 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;
+
+/**
+ * This interface contains the constants used by the eclipse
+ * OSGi implementation.
+ */
+
+public class Constants implements org.osgi.framework.Constants {
+ /** Default framework version */
+ public static final String OSGI_FRAMEWORK_VERSION = "1.3"; //$NON-NLS-1$
+
+ /** Framework vendor */
+ public static final String OSGI_FRAMEWORK_VENDOR = "Eclipse"; //$NON-NLS-1$
+
+ /** Bundle manifest name */
+ public static final String OSGI_BUNDLE_MANIFEST = "META-INF/MANIFEST.MF"; //$NON-NLS-1$
+
+ /** OSGi framework package name. */
+ public static final String OSGI_FRAMEWORK_PACKAGE = "org.osgi.framework"; //$NON-NLS-1$
+
+ /** Bundle resource URL protocol */
+ public static final String OSGI_RESOURCE_URL_PROTOCOL = "bundleresource"; //$NON-NLS-1$
+
+ /** Bundle entry URL protocol */
+ public static final String OSGI_ENTRY_URL_PROTOCOL = "bundleentry"; //$NON-NLS-1$
+
+ /** Processor aliases resource */
+ public static final String OSGI_PROCESSOR_ALIASES = "processor.aliases"; //$NON-NLS-1$
+
+ /** OS name aliases resource */
+ public static final String OSGI_OSNAME_ALIASES = "osname.aliases"; //$NON-NLS-1$
+
+ /** Default permissions for bundles with no permission set
+ * and there are no default permissions set.
+ */
+ public static final String OSGI_DEFAULT_DEFAULT_PERMISSIONS = "default.permissions"; //$NON-NLS-1$
+
+ /** Base implied permissions for all bundles */
+ public static final String OSGI_BASE_IMPLIED_PERMISSIONS = "implied.permissions"; //$NON-NLS-1$
+
+ /** Name of OSGi LogService */
+ public static final String OSGI_LOGSERVICE_NAME = "org.osgi.service.log.LogService"; //$NON-NLS-1$
+
+ /** Name of OSGi PackageAdmin */
+ public static final String OSGI_PACKAGEADMIN_NAME = "org.osgi.service.packageadmin.PackageAdmin"; //$NON-NLS-1$
+
+ /** Name of OSGi PermissionAdmin */
+ public static final String OSGI_PERMISSIONADMIN_NAME = "org.osgi.service.permissionadmin.PermissionAdmin"; //$NON-NLS-1$
+
+ /** Name of OSGi StartLevel */
+ public static final String OSGI_STARTLEVEL_NAME = "org.osgi.service.startlevel.StartLevel"; //$NON-NLS-1$
+
+ /** JVM java.vm.name property name */
+ public static final String JVM_VM_NAME = "java.vm.name"; //$NON-NLS-1$
+
+ /** JVM os.arch property name */
+ public static final String JVM_OS_ARCH = "os.arch"; //$NON-NLS-1$
+
+ /** JVM os.name property name */
+ public static final String JVM_OS_NAME = "os.name"; //$NON-NLS-1$
+
+ /** JVM os.version property name */
+ public static final String JVM_OS_VERSION = "os.version"; //$NON-NLS-1$
+
+ /** JVM user.language property name */
+ public static final String JVM_USER_LANGUAGE = "user.language"; //$NON-NLS-1$
+
+ /** JVM user.region property name */
+ public static final String JVM_USER_REGION = "user.region"; //$NON-NLS-1$
+
+ /** J2ME configuration property name */
+ public static final String J2ME_MICROEDITION_CONFIGURATION = "microedition.configuration"; //$NON-NLS-1$
+
+ /** J2ME profile property name */
+ public static final String J2ME_MICROEDITION_PROFILES = "microedition.profiles"; //$NON-NLS-1$
+
+ /** Persistent start bundle status */
+ public static final int BUNDLE_STARTED = 0x00000001;
+ /** Lazy start flag bundle status */
+ public static final int BUNDLE_LAZY_START = 0x00000002;
+ public static final int BUNDLE_ACTIVATION_POLICY = 0x00000004;
+
+ /** Property file locations and default names. */
+ public static final String OSGI_PROPERTIES = "osgi.framework.properties"; //$NON-NLS-1$
+ public static final String DEFAULT_OSGI_PROPERTIES = "osgi.properties"; //$NON-NLS-1$
+
+ private static String INTERNAL_SYSTEM_BUNDLE = "org.eclipse.osgi"; //$NON-NLS-1$
+
+ public static String getInternalSymbolicName() {
+ return INTERNAL_SYSTEM_BUNDLE;
+ }
+
+ static void setInternalSymbolicName(String name) {
+ INTERNAL_SYSTEM_BUNDLE = name;
+ }
+
+ /** OSGI implementation version properties key */
+ public static final String OSGI_IMPL_VERSION_KEY = "osgi.framework.version"; //$NON-NLS-1$
+ /** OSGi java profile; used to give a URL to a java profile */
+ public static final String OSGI_JAVA_PROFILE = "osgi.java.profile"; //$NON-NLS-1$
+ public static final String OSGI_JAVA_PROFILE_NAME = "osgi.java.profile.name"; //$NON-NLS-1$
+ /**
+ * OSGi java profile bootdelegation; used to indicate how the org.osgi.framework.bootdelegation
+ * property defined in the java profile should be processed, (ingnore, override, none). default is ignore
+ */
+ public static final String OSGI_JAVA_PROFILE_BOOTDELEGATION = "osgi.java.profile.bootdelegation"; //$NON-NLS-1$
+ /** indicates that the org.osgi.framework.bootdelegation in the java profile should be ingored */
+ public static final String OSGI_BOOTDELEGATION_IGNORE = "ignore"; //$NON-NLS-1$
+ /** indicates that the org.osgi.framework.bootdelegation in the java profile should override the system property */
+ public static final String OSGI_BOOTDELEGATION_OVERRIDE = "override"; //$NON-NLS-1$
+ /** indicates that the org.osgi.framework.bootdelegation in the java profile AND the system properties should be ignored */
+ public static final String OSGI_BOOTDELEGATION_NONE = "none"; //$NON-NLS-1$
+ /** OSGi strict delegation **/
+ public static final String OSGI_RESOLVER_MODE = "osgi.resolverMode"; //$NON-NLS-1$
+ public static final String STRICT_MODE = "strict"; //$NON-NLS-1$
+ public static final String DEVELOPMENT_MODE = "development"; //$NON-NLS-1$
+
+ public static final String STATE_SYSTEM_BUNDLE = "osgi.system.bundle"; //$NON-NLS-1$
+
+ public static final String PROP_OSGI_RELAUNCH = "osgi.framework.relaunch"; //$NON-NLS-1$
+
+ public static String OSGI_COMPATIBILITY_BOOTDELEGATION = "osgi.compatibility.bootdelegation"; //$NON-NLS-1$
+
+ /** Eclipse-SystemBundle header */
+ public static final String ECLIPSE_SYSTEMBUNDLE = "Eclipse-SystemBundle"; //$NON-NLS-1$
+ public static final String ECLIPSE_PLATFORMFILTER = "Eclipse-PlatformFilter"; //$NON-NLS-1$
+ public static final String Eclipse_JREBUNDLE = "Eclipse-JREBundle"; //$NON-NLS-1$
+ /**
+ * Manifest Export-Package directive indicating that the exported package should only
+ * be made available when the resolver is not in strict mode.
+ */
+ public static final String INTERNAL_DIRECTIVE = "x-internal"; //$NON-NLS-1$
+
+ /**
+ * Manifest Export-Package directive indicating that the exported package should only
+ * be made available to friends of the exporting bundle.
+ */
+ public static final String FRIENDS_DIRECTIVE = "x-friends"; //$NON-NLS-1$
+
+ /**
+ * Manifest header (named &quot;Provide-Package&quot;)
+ * identifying the packages name
+ * provided to other bundles which require the bundle.
+ *
+ * <p>
+ * NOTE: this is only used for backwards compatibility, bundles manifest using
+ * syntax version 2 will not recognize this header.
+ *
+ * <p>The attribute value may be retrieved from the
+ * <tt>Dictionary</tt> object returned by the <tt>Bundle.getHeaders</tt> method.
+ * @deprecated
+ */
+ public final static String PROVIDE_PACKAGE = "Provide-Package"; //$NON-NLS-1$
+
+ /**
+ * Manifest header attribute (named &quot;reprovide&quot;)
+ * for Require-Bundle
+ * identifying that any packages that are provided
+ * by the required bundle must be reprovided by the requiring bundle.
+ * The default value is <tt>false</tt>.
+ * <p>
+ * The attribute value is encoded in the Require-Bundle manifest
+ * header like:
+ * <pre>
+ * Require-Bundle: com.acme.module.test; reprovide="true"
+ * </pre>
+ * <p>
+ * NOTE: this is only used for backwards compatibility, bundles manifest using
+ * syntax version 2 will not recognize this attribute.
+ * @deprecated
+ */
+ public final static String REPROVIDE_ATTRIBUTE = "reprovide"; //$NON-NLS-1$
+
+ /**
+ * Manifest header attribute (named &quot;optional&quot;)
+ * for Require-Bundle
+ * identifying that a required bundle is optional and that
+ * the requiring bundle can be resolved if there is no
+ * suitable required bundle.
+ * The default value is <tt>false</tt>.
+ *
+ * <p>The attribute value is encoded in the Require-Bundle manifest
+ * header like:
+ * <pre>
+ * Require-Bundle: com.acme.module.test; optional="true"
+ * </pre>
+ * <p>
+ * NOTE: this is only used for backwards compatibility, bundles manifest using
+ * syntax version 2 will not recognize this attribute.
+ * @since 1.3 <b>EXPERIMENTAL</b>
+ * @deprecated
+ */
+ public final static String OPTIONAL_ATTRIBUTE = "optional"; //$NON-NLS-1$
+
+ /**
+ * The key used to designate the buddy loader associated with a given bundle.
+ */
+ public final static String BUDDY_LOADER = "Eclipse-BuddyPolicy"; //$NON-NLS-1$
+
+ public final static String REGISTERED_POLICY = "Eclipse-RegisterBuddy"; //$NON-NLS-1$
+
+ static public final String INTERNAL_HANDLER_PKGS = "equinox.interal.handler.pkgs"; //$NON-NLS-1$
+
+ // TODO rename it to Eclipse-PluginClass
+ public static final String PLUGIN_CLASS = "Plugin-Class"; //$NON-NLS-1$
+
+ /** Manifest header used to specify the lazy start properties of a bundle */
+ public static final String ECLIPSE_LAZYSTART = "Eclipse-LazyStart"; //$NON-NLS-1$
+
+ /** An Eclipse-LazyStart attribute used to specify exception classes for auto start */
+ public static final String ECLIPSE_LAZYSTART_EXCEPTIONS = "exceptions"; //$NON-NLS-1$
+
+ /**
+ * Manifest header used to specify the auto start properties of a bundle
+ * @deprecated use {@link #ECLIPSE_LAZYSTART}
+ */
+ public static final String ECLIPSE_AUTOSTART = "Eclipse-AutoStart"; //$NON-NLS-1$
+
+ /**
+ * @deprecated use {@link #ECLIPSE_LAZYSTART_EXCEPTIONS}
+ */
+ public static final String ECLIPSE_AUTOSTART_EXCEPTIONS = ECLIPSE_LAZYSTART_EXCEPTIONS;
+
+ /**
+ * Framework launching property specifying whether Equinox's FrameworkWiring
+ * implementation should refresh bundles with equal symbolic names.
+ *
+ * <p>
+ * Default value is <b>TRUE</b> in this release of the Equinox.
+ * This default may change to <b>FALSE</b> in a future Equinox release.
+ * Therefore, code must not assume the default behavior is
+ * <b>TRUE</b> and should interrogate the value of this property to
+ * determine the behavior.
+ *
+ * <p>
+ * The value of this property may be retrieved by calling the
+ * {@code BundleContext.getProperty} method.
+ * @see <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=351519">bug 351519</a>
+ * @since 3.7.1
+ */
+ public static final String REFRESH_DUPLICATE_BSN = "equinox.refresh.duplicate.bsn"; //$NON-NLS-1$
+
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/CoreResolverHookFactory.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/CoreResolverHookFactory.java
new file mode 100644
index 000000000..986de041d
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/CoreResolverHookFactory.java
@@ -0,0 +1,216 @@
+/*******************************************************************************
+ * Copyright (c) 2010, 2012 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.util.*;
+import org.eclipse.osgi.framework.debug.Debug;
+import org.eclipse.osgi.internal.serviceregistry.*;
+import org.eclipse.osgi.service.resolver.ResolverHookException;
+import org.eclipse.osgi.util.NLS;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.hooks.resolver.ResolverHook;
+import org.osgi.framework.hooks.resolver.ResolverHookFactory;
+import org.osgi.framework.wiring.*;
+
+/**
+ * This class encapsulates the delegation to ResolverHooks that are registered with the service
+ * registry. This way the resolver implementation only has to call out to a single hook
+ * which does all the necessary service registry lookups.
+ *
+ * This class is not thread safe and expects external synchronization.
+ *
+ */
+public class CoreResolverHookFactory implements ResolverHookFactory {
+ // need a tuple to hold the service reference and hook object
+ // do not use a map for performance reasons; no need to hash based on a key.
+ static class HookReference {
+ public HookReference(ServiceReferenceImpl<ResolverHookFactory> reference, ResolverHook hook) {
+ this.reference = reference;
+ this.hook = hook;
+ }
+
+ final ServiceReferenceImpl<ResolverHookFactory> reference;
+ final ResolverHook hook;
+ }
+
+ private final BundleContextImpl context;
+ private final ServiceRegistry registry;
+
+ public CoreResolverHookFactory(BundleContextImpl context, ServiceRegistry registry) {
+ this.context = context;
+ this.registry = registry;
+ }
+
+ void handleHookException(Throwable t, Object hook, String method) {
+ if (Debug.DEBUG_HOOKS) {
+ Debug.println(hook.getClass().getName() + "." + method + "() exception:"); //$NON-NLS-1$ //$NON-NLS-2$
+ if (t != null)
+ Debug.printStackTrace(t);
+ }
+ String message = NLS.bind(Msg.SERVICE_FACTORY_EXCEPTION, hook.getClass().getName(), method);
+ throw new ResolverHookException(message, t);
+ }
+
+ private ServiceReferenceImpl<ResolverHookFactory>[] getHookReferences() {
+ try {
+ @SuppressWarnings("unchecked")
+ ServiceReferenceImpl<ResolverHookFactory>[] result = (ServiceReferenceImpl<ResolverHookFactory>[]) registry.getServiceReferences(context, ResolverHookFactory.class.getName(), null, false, false);
+ return result;
+ } catch (InvalidSyntaxException e) {
+ // cannot happen; no filter
+ return null;
+ }
+ }
+
+ public ResolverHook begin(Collection<BundleRevision> triggers) {
+ if (Debug.DEBUG_HOOKS) {
+ Debug.println("ResolverHook.begin"); //$NON-NLS-1$
+ }
+ ServiceReferenceImpl<ResolverHookFactory>[] refs = getHookReferences();
+ @SuppressWarnings("unchecked")
+ List<HookReference> hookRefs = refs == null ? Collections.EMPTY_LIST : new ArrayList<CoreResolverHookFactory.HookReference>(refs.length);
+ if (refs != null)
+ for (ServiceReferenceImpl<ResolverHookFactory> hookRef : refs) {
+ ResolverHookFactory factory = context.getService(hookRef);
+ if (factory != null) {
+ try {
+ ResolverHook hook = factory.begin(triggers);
+ if (hook != null)
+ hookRefs.add(new HookReference(hookRef, hook));
+ } catch (Throwable t) {
+ // need to force an end call on the ResolverHooks we got and release them
+ try {
+ new CoreResolverHook(hookRefs).end();
+ } catch (Throwable endError) {
+ // we are already in failure mode; just continue
+ }
+ handleHookException(t, factory, "begin"); //$NON-NLS-1$
+ }
+ }
+ }
+ return new CoreResolverHook(hookRefs);
+ }
+
+ void releaseHooks(List<HookReference> hookRefs) {
+ for (HookReference hookRef : hookRefs)
+ context.ungetService(hookRef.reference);
+ hookRefs.clear();
+ }
+
+ class CoreResolverHook implements ResolverHook {
+ private final List<HookReference> hooks;
+
+ CoreResolverHook(List<HookReference> hooks) {
+ this.hooks = hooks;
+ }
+
+ public void filterResolvable(Collection<BundleRevision> candidates) {
+ if (Debug.DEBUG_HOOKS) {
+ Debug.println("ResolverHook.filterResolvable(" + candidates + ")"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ if (hooks.isEmpty())
+ return;
+ candidates = new ShrinkableCollection<BundleRevision>(candidates);
+ for (Iterator<HookReference> iHooks = hooks.iterator(); iHooks.hasNext();) {
+ HookReference hookRef = iHooks.next();
+ if (hookRef.reference.getBundle() == null) {
+ handleHookException(null, hookRef.hook, "filterResolvable"); //$NON-NLS-1$
+ } else {
+ try {
+ hookRef.hook.filterResolvable(candidates);
+ } catch (Throwable t) {
+ handleHookException(t, hookRef.hook, "filterResolvable"); //$NON-NLS-1$
+ }
+ }
+ }
+ }
+
+ public void filterSingletonCollisions(BundleCapability singleton, Collection<BundleCapability> collisionCandidates) {
+ if (Debug.DEBUG_HOOKS) {
+ Debug.println("ResolverHook.filterSingletonCollisions(" + singleton + ", " + collisionCandidates + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ if (hooks.isEmpty())
+ return;
+ collisionCandidates = new ShrinkableCollection<BundleCapability>(collisionCandidates);
+ for (Iterator<HookReference> iHooks = hooks.iterator(); iHooks.hasNext();) {
+ HookReference hookRef = iHooks.next();
+ if (hookRef.reference.getBundle() == null) {
+ handleHookException(null, hookRef.hook, "filterSingletonCollisions"); //$NON-NLS-1$
+ } else {
+ try {
+ hookRef.hook.filterSingletonCollisions(singleton, collisionCandidates);
+ } catch (Throwable t) {
+ handleHookException(t, hookRef.hook, "filterSingletonCollisions"); //$NON-NLS-1$
+ }
+ }
+ }
+ }
+
+ public void filterMatches(BundleRequirement requirement, Collection<BundleCapability> candidates) {
+ if (Debug.DEBUG_HOOKS) {
+ Debug.println("ResolverHook.filterMatches(" + requirement + ", " + candidates + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ if (hooks.isEmpty())
+ return;
+ candidates = new ShrinkableCollection<BundleCapability>(candidates);
+ for (Iterator<HookReference> iHooks = hooks.iterator(); iHooks.hasNext();) {
+ HookReference hookRef = iHooks.next();
+ if (hookRef.reference.getBundle() == null) {
+ handleHookException(null, hookRef.hook, "filterMatches"); //$NON-NLS-1$
+ } else {
+ try {
+ hookRef.hook.filterMatches(requirement, candidates);
+ } catch (Throwable t) {
+ handleHookException(t, hookRef.hook, "filterMatches"); //$NON-NLS-1$
+ }
+ }
+ }
+ }
+
+ public void end() {
+ if (Debug.DEBUG_HOOKS) {
+ Debug.println("ResolverHook.end"); //$NON-NLS-1$
+ }
+ if (hooks.isEmpty())
+ return;
+ try {
+ HookReference missingHook = null;
+ Throwable endError = null;
+ HookReference endBadHook = null;
+ for (Iterator<HookReference> iHooks = hooks.iterator(); iHooks.hasNext();) {
+ HookReference hookRef = iHooks.next();
+ // We do not remove unregistered services here because we are going to remove all of them at the end
+ if (hookRef.reference.getBundle() == null) {
+ if (missingHook == null)
+ missingHook = hookRef;
+ } else {
+ try {
+ hookRef.hook.end();
+ } catch (Throwable t) {
+ // Must continue on to the next hook.end method
+ // save the error for throwing at the end
+ if (endError == null) {
+ endError = t;
+ endBadHook = hookRef;
+ }
+ }
+ }
+ }
+ if (missingHook != null)
+ handleHookException(null, missingHook.hook, "end"); //$NON-NLS-1$
+ if (endError != null)
+ handleHookException(endError, endBadHook.hook, "end"); //$NON-NLS-1$
+ } finally {
+ releaseHooks(hooks);
+ }
+ }
+ }
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/EquinoxLauncher.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/EquinoxLauncher.java
new file mode 100644
index 000000000..2b71f2467
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/EquinoxLauncher.java
@@ -0,0 +1,347 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2011 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.net.URL;
+import java.security.*;
+import java.security.cert.X509Certificate;
+import java.util.*;
+import org.eclipse.core.runtime.adaptor.EclipseStarter;
+import org.eclipse.osgi.baseadaptor.BaseAdaptor;
+import org.eclipse.osgi.framework.adaptor.FrameworkAdaptor;
+import org.osgi.framework.*;
+
+public class EquinoxLauncher implements org.osgi.framework.launch.Framework {
+
+ private volatile Framework framework;
+ private volatile Bundle systemBundle;
+ private final Map<String, String> configuration;
+ private volatile ConsoleManager consoleMgr = null;
+
+ public EquinoxLauncher(Map<String, String> configuration) {
+ this.configuration = configuration;
+ }
+
+ public void init() {
+ checkAdminPermission(AdminPermission.EXECUTE);
+ if (System.getSecurityManager() == null)
+ internalInit();
+ else {
+ AccessController.doPrivileged(new PrivilegedAction<Object>() {
+ public Object run() {
+ internalInit();
+ return null;
+ }
+ });
+ }
+ }
+
+ synchronized Framework internalInit() {
+ if ((getState() & (Bundle.ACTIVE | Bundle.STARTING | Bundle.STOPPING)) != 0)
+ return framework; // no op
+
+ if (System.getSecurityManager() != null && configuration.get(Constants.FRAMEWORK_SECURITY) != null)
+ throw new SecurityException("Cannot specify the \"" + Constants.FRAMEWORK_SECURITY + "\" configuration property when a security manager is already installed."); //$NON-NLS-1$ //$NON-NLS-2$
+
+ Framework current = framework;
+ if (current != null) {
+ current.close();
+ framework = null;
+ systemBundle = null;
+ }
+ ClassLoader tccl = Thread.currentThread().getContextClassLoader();
+ try {
+ FrameworkProperties.setProperties(configuration);
+ FrameworkProperties.initializeProperties();
+ // make sure the active framework thread is used
+ setEquinoxProperties(configuration);
+ current = new Framework(new BaseAdaptor(new String[0]));
+ consoleMgr = ConsoleManager.startConsole(current);
+ current.launch();
+ framework = current;
+ systemBundle = current.systemBundle;
+ } finally {
+ ClassLoader currentCCL = Thread.currentThread().getContextClassLoader();
+ if (currentCCL != tccl)
+ Thread.currentThread().setContextClassLoader(tccl);
+ }
+ return current;
+ }
+
+ private void setEquinoxProperties(Map<String, String> configuration) {
+ Object threadBehavior = configuration == null ? null : configuration.get(Framework.PROP_FRAMEWORK_THREAD);
+ if (threadBehavior == null) {
+ if (FrameworkProperties.getProperty(Framework.PROP_FRAMEWORK_THREAD) == null)
+ FrameworkProperties.setProperty(Framework.PROP_FRAMEWORK_THREAD, Framework.THREAD_NORMAL);
+ } else {
+ FrameworkProperties.setProperty(Framework.PROP_FRAMEWORK_THREAD, (String) threadBehavior);
+ }
+
+ // set the compatibility boot delegation flag to false to get "standard" OSGi behavior WRT boot delegation (bug 344850)
+ if (FrameworkProperties.getProperty(Constants.OSGI_COMPATIBILITY_BOOTDELEGATION) == null)
+ FrameworkProperties.setProperty(Constants.OSGI_COMPATIBILITY_BOOTDELEGATION, "false"); //$NON-NLS-1$
+ // set the support for multiple host to true to get "standard" OSGi behavior (bug 344850)
+ if (FrameworkProperties.getProperty("osgi.support.multipleHosts") == null) //$NON-NLS-1$
+ FrameworkProperties.setProperty("osgi.support.multipleHosts", "true"); //$NON-NLS-1$ //$NON-NLS-2$
+ // first check props we are required to provide reasonable defaults for
+ Object windowSystem = configuration == null ? null : configuration.get(Constants.FRAMEWORK_WINDOWSYSTEM);
+ if (windowSystem == null) {
+ windowSystem = FrameworkProperties.getProperty(EclipseStarter.PROP_WS);
+ if (windowSystem != null)
+ FrameworkProperties.setProperty(Constants.FRAMEWORK_WINDOWSYSTEM, (String) windowSystem);
+ }
+ // rest of props can be ignored if the configuration is null
+ if (configuration == null)
+ return;
+ // check each osgi clean property and set the appropriate equinox one
+ Object clean = configuration.get(Constants.FRAMEWORK_STORAGE_CLEAN);
+ if (Constants.FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT.equals(clean)) {
+ // remove this so we only clean on first init
+ configuration.remove(Constants.FRAMEWORK_STORAGE_CLEAN);
+ FrameworkProperties.setProperty(EclipseStarter.PROP_CLEAN, Boolean.TRUE.toString());
+ }
+ }
+
+ public FrameworkEvent waitForStop(long timeout) throws InterruptedException {
+ Framework current = framework;
+ if (current == null)
+ return new FrameworkEvent(FrameworkEvent.STOPPED, this, null);
+ return current.waitForStop(timeout);
+ }
+
+ public Enumeration<URL> findEntries(String path, String filePattern, boolean recurse) {
+ Bundle current = systemBundle;
+ if (current == null)
+ return null;
+ return current.findEntries(path, filePattern, recurse);
+ }
+
+ public BundleContext getBundleContext() {
+ Bundle current = systemBundle;
+ if (current == null)
+ return null;
+ return current.getBundleContext();
+ }
+
+ public long getBundleId() {
+ return 0;
+ }
+
+ public URL getEntry(String path) {
+ Bundle current = systemBundle;
+ if (current == null)
+ return null;
+ return current.getEntry(path);
+ }
+
+ public Enumeration<String> getEntryPaths(String path) {
+ Bundle current = systemBundle;
+ if (current == null)
+ return null;
+ return current.getEntryPaths(path);
+ }
+
+ public Dictionary<String, String> getHeaders() {
+ Bundle current = systemBundle;
+ if (current == null)
+ return null;
+ return current.getHeaders();
+ }
+
+ public Dictionary<String, String> getHeaders(String locale) {
+ Bundle current = systemBundle;
+ if (current == null)
+ return null;
+ return current.getHeaders(locale);
+ }
+
+ public long getLastModified() {
+ Bundle current = systemBundle;
+ if (current == null)
+ return System.currentTimeMillis();
+ return current.getLastModified();
+ }
+
+ public String getLocation() {
+ return Constants.SYSTEM_BUNDLE_LOCATION;
+ }
+
+ public ServiceReference<?>[] getRegisteredServices() {
+ Bundle current = systemBundle;
+ if (current == null)
+ return null;
+ return current.getRegisteredServices();
+ }
+
+ public URL getResource(String name) {
+ Bundle current = systemBundle;
+ if (current == null)
+ return null;
+ return current.getResource(name);
+ }
+
+ public Enumeration<URL> getResources(String name) throws IOException {
+ Bundle current = systemBundle;
+ if (current == null)
+ return null;
+ return current.getResources(name);
+ }
+
+ public ServiceReference<?>[] getServicesInUse() {
+ Bundle current = systemBundle;
+ if (current == null)
+ return null;
+ return current.getServicesInUse();
+ }
+
+ public int getState() {
+ Bundle current = systemBundle;
+ if (current == null)
+ return Bundle.INSTALLED;
+ return current.getState();
+ }
+
+ public String getSymbolicName() {
+ return FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME;
+ }
+
+ public boolean hasPermission(Object permission) {
+ Bundle current = systemBundle;
+ if (current == null)
+ return false;
+ return current.hasPermission(permission);
+ }
+
+ public Class<?> loadClass(String name) throws ClassNotFoundException {
+ Bundle current = systemBundle;
+ if (current == null)
+ return null;
+ return current.loadClass(name);
+ }
+
+ public void start(int options) throws BundleException {
+ start();
+ }
+
+ /**
+ * @throws BundleException
+ */
+ public void start() throws BundleException {
+ checkAdminPermission(AdminPermission.EXECUTE);
+ if (System.getSecurityManager() == null)
+ internalStart();
+ else
+ try {
+ AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
+ public Object run() {
+ internalStart();
+ return null;
+ }
+ });
+ } catch (PrivilegedActionException e) {
+ throw (BundleException) e.getException();
+ }
+ }
+
+ private void checkAdminPermission(String actions) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ sm.checkPermission(new AdminPermission(this, actions));
+ }
+
+ void internalStart() {
+ if (getState() == Bundle.ACTIVE)
+ return;
+ Framework current = internalInit();
+ int level = 1;
+ try {
+ level = Integer.parseInt(configuration.get(Constants.FRAMEWORK_BEGINNING_STARTLEVEL));
+ } catch (Throwable t) {
+ // do nothing
+ }
+ current.startLevelManager.doSetStartLevel(level);
+ }
+
+ public void stop(int options) throws BundleException {
+ stop();
+ }
+
+ public void stop() throws BundleException {
+ Bundle current = systemBundle;
+ if (current == null)
+ return;
+ ConsoleManager currentConsole = consoleMgr;
+ if (currentConsole != null) {
+ currentConsole.stopConsole();
+ consoleMgr = null;
+ }
+ current.stop();
+ }
+
+ public void uninstall() throws BundleException {
+ throw new BundleException(Msg.BUNDLE_SYSTEMBUNDLE_UNINSTALL_EXCEPTION, BundleException.INVALID_OPERATION);
+ }
+
+ public void update() throws BundleException {
+ Bundle current = systemBundle;
+ if (current == null)
+ return;
+ current.update();
+ }
+
+ public void update(InputStream in) throws BundleException {
+ try {
+ in.close();
+ } catch (IOException e) {
+ // nothing; just being nice
+ }
+ update();
+ }
+
+ public Map<X509Certificate, List<X509Certificate>> getSignerCertificates(int signersType) {
+ Bundle current = systemBundle;
+ if (current != null)
+ return current.getSignerCertificates(signersType);
+ @SuppressWarnings("unchecked")
+ final Map<X509Certificate, List<X509Certificate>> empty = Collections.EMPTY_MAP;
+ return empty;
+ }
+
+ public Version getVersion() {
+ Bundle current = systemBundle;
+ if (current != null)
+ return current.getVersion();
+ return Version.emptyVersion;
+ }
+
+ public <A> A adapt(Class<A> adapterType) {
+ Bundle current = systemBundle;
+ if (current != null) {
+ return current.adapt(adapterType);
+ }
+ return null;
+ }
+
+ public int compareTo(Bundle o) {
+ Bundle current = systemBundle;
+ if (current != null)
+ return current.compareTo(o);
+ throw new IllegalStateException();
+ }
+
+ public File getDataFile(String filename) {
+ Bundle current = systemBundle;
+ if (current != null)
+ return current.getDataFile(filename);
+ return null;
+ }
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/ExportedPackageImpl.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/ExportedPackageImpl.java
new file mode 100644
index 000000000..a26998541
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/ExportedPackageImpl.java
@@ -0,0 +1,99 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2011 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.util.ArrayList;
+import java.util.List;
+import org.eclipse.osgi.internal.loader.*;
+import org.eclipse.osgi.service.resolver.BundleDescription;
+import org.eclipse.osgi.service.resolver.ExportPackageDescription;
+import org.osgi.framework.*;
+import org.osgi.framework.Constants;
+import org.osgi.service.packageadmin.ExportedPackage;
+
+/**
+ * @deprecated
+ */
+public class ExportedPackageImpl implements ExportedPackage {
+
+ private final ExportPackageDescription exportedPackage;
+ private final BundleLoaderProxy supplier;
+
+ public ExportedPackageImpl(ExportPackageDescription exportedPackage, BundleLoaderProxy supplier) {
+ this.exportedPackage = exportedPackage;
+ this.supplier = supplier;
+ }
+
+ public String getName() {
+ return exportedPackage.getName();
+ }
+
+ public org.osgi.framework.Bundle getExportingBundle() {
+ if (supplier.isStale())
+ return null;
+ return supplier.getBundleHost();
+ }
+
+ /*
+ * get the bundle without checking if it is stale
+ */
+ AbstractBundle getBundle() {
+ return supplier.getBundleHost();
+ }
+
+ public Bundle[] getImportingBundles() {
+ if (supplier.isStale())
+ return null;
+ AbstractBundle bundle = (AbstractBundle) getExportingBundle();
+ if (bundle == null)
+ return null;
+ AbstractBundle[] bundles = bundle.framework.getAllBundles();
+ List<Bundle> importers = new ArrayList<Bundle>(10);
+ PackageSource supplierSource = supplier.createPackageSource(exportedPackage, false);
+ for (int i = 0; i < bundles.length; i++) {
+ if (!(bundles[i] instanceof BundleHost))
+ continue;
+ BundleLoader loader = ((BundleHost) bundles[i]).getBundleLoader();
+ if (loader == null || loader.getBundle() == supplier.getBundle())
+ continue; // do not include include the exporter of the package
+ PackageSource importerSource = loader.getPackageSource(getName());
+ if (supplierSource != null && supplierSource.hasCommonSource(importerSource))
+ importers.add(bundles[i]);
+ }
+ return importers.toArray(new Bundle[importers.size()]);
+ }
+
+ /**
+ * @deprecated
+ */
+ public String getSpecificationVersion() {
+ return exportedPackage.getVersion().toString();
+ }
+
+ public Version getVersion() {
+ return exportedPackage.getVersion();
+ }
+
+ public boolean isRemovalPending() {
+ BundleDescription exporter = exportedPackage.getExporter();
+ if (exporter != null)
+ return exporter.isRemovalPending();
+ return true;
+ }
+
+ public String toString() {
+ StringBuffer result = new StringBuffer(getName());
+ result.append("; ").append(Constants.VERSION_ATTRIBUTE); //$NON-NLS-1$
+ result.append("=\"").append(exportedPackage.getVersion().toString()).append("\""); //$NON-NLS-1$//$NON-NLS-2$
+
+ return result.toString();
+ }
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/FilterImpl.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/FilterImpl.java
new file mode 100644
index 000000000..4611c63cb
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/FilterImpl.java
@@ -0,0 +1,1751 @@
+/*******************************************************************************
+ * 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.lang.reflect.*;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.*;
+import org.eclipse.osgi.framework.debug.Debug;
+import org.eclipse.osgi.framework.util.Headers;
+import org.eclipse.osgi.internal.serviceregistry.ServiceReferenceImpl;
+import org.eclipse.osgi.util.NLS;
+import org.osgi.framework.*;
+
+/**
+ * RFC 1960-based Filter. Filter objects can be created by calling
+ * the constructor with the desired filter string.
+ * A Filter object can be called numerous times to determine if the
+ * match argument matches the filter string that was used to create the Filter
+ * object.
+ *
+ * <p>The syntax of a filter string is the string representation
+ * of LDAP search filters as defined in RFC 1960:
+ * <i>A String Representation of LDAP Search Filters</i> (available at
+ * http://www.ietf.org/rfc/rfc1960.txt).
+ * It should be noted that RFC 2254:
+ * <i>A String Representation of LDAP Search Filters</i>
+ * (available at http://www.ietf.org/rfc/rfc2254.txt) supersedes
+ * RFC 1960 but only adds extensible matching and is not applicable for this
+ * API.
+ *
+ * <p>The string representation of an LDAP search filter is defined by the
+ * following grammar. It uses a prefix format.
+ * <pre>
+ * &lt;filter&gt; ::= '(' &lt;filtercomp&gt; ')'
+ * &lt;filtercomp&gt; ::= &lt;and&gt; | &lt;or&gt; | &lt;not&gt; | &lt;item&gt;
+ * &lt;and&gt; ::= '&' &lt;filterlist&gt;
+ * &lt;or&gt; ::= '|' &lt;filterlist&gt;
+ * &lt;not&gt; ::= '!' &lt;filter&gt;
+ * &lt;filterlist&gt; ::= &lt;filter&gt; | &lt;filter&gt; &lt;filterlist&gt;
+ * &lt;item&gt; ::= &lt;simple&gt; | &lt;present&gt; | &lt;substring&gt;
+ * &lt;simple&gt; ::= &lt;attr&gt; &lt;filtertype&gt; &lt;value&gt;
+ * &lt;filtertype&gt; ::= &lt;equal&gt; | &lt;approx&gt; | &lt;greater&gt; | &lt;less&gt;
+ * &lt;equal&gt; ::= '='
+ * &lt;approx&gt; ::= '~='
+ * &lt;greater&gt; ::= '&gt;='
+ * &lt;less&gt; ::= '&lt;='
+ * &lt;present&gt; ::= &lt;attr&gt; '=*'
+ * &lt;substring&gt; ::= &lt;attr&gt; '=' &lt;initial&gt; &lt;any&gt; &lt;final&gt;
+ * &lt;initial&gt; ::= NULL | &lt;value&gt;
+ * &lt;any&gt; ::= '*' &lt;starval&gt;
+ * &lt;starval&gt; ::= NULL | &lt;value&gt; '*' &lt;starval&gt;
+ * &lt;final&gt; ::= NULL | &lt;value&gt;
+ * </pre>
+ *
+ * <code>&lt;attr&gt;</code> is a string representing an attribute, or
+ * key, in the properties objects of the registered services.
+ * Attribute names are not case sensitive;
+ * that is cn and CN both refer to the same attribute.
+ * <code>&lt;value&gt;</code> is a string representing the value, or part of
+ * one, of a key in the properties objects of the registered services.
+ * If a <code>&lt;value&gt;</code> must
+ * contain one of the characters '<code>*</code>' or '<code>(</code>'
+ * or '<code>)</code>', these characters
+ * should be escaped by preceding them with the backslash '<code>\</code>'
+ * character.
+ * Note that although both the <code>&lt;substring&gt;</code> and
+ * <code>&lt;present&gt;</code> productions can
+ * produce the <code>'attr=*'</code> construct, this construct is used only to
+ * denote a presence filter.
+ *
+ * <p>Examples of LDAP filters are:
+ *
+ * <pre>
+ * &quot;(cn=Babs Jensen)&quot;
+ * &quot;(!(cn=Tim Howes))&quot;
+ * &quot;(&(&quot; + Constants.OBJECTCLASS + &quot;=Person)(|(sn=Jensen)(cn=Babs J*)))&quot;
+ * &quot;(o=univ*of*mich*)&quot;
+ * </pre>
+ *
+ * <p>The approximate match (<code>~=</code>) is implementation specific but
+ * should at least ignore case and white space differences. Optional are
+ * codes like soundex or other smart "closeness" comparisons.
+ *
+ * <p>Comparison of values is not straightforward. Strings
+ * are compared differently than numbers and it is
+ * possible for a key to have multiple values. Note that
+ * that keys in the match argument must always be strings.
+ * The comparison is defined by the object type of the key's
+ * value. The following rules apply for comparison:
+ *
+ * <blockquote>
+ * <TABLE BORDER=0>
+ * <TR><TD><b>Property Value Type </b></TD><TD><b>Comparison Type</b></TD></TR>
+ * <TR><TD>String </TD><TD>String comparison</TD></TR>
+ * <TR valign=top><TD>Integer, Long, Float, Double, Byte, Short, BigInteger, BigDecimal </TD><TD>numerical comparison</TD></TR>
+ * <TR><TD>Character </TD><TD>character comparison</TD></TR>
+ * <TR><TD>Boolean </TD><TD>equality comparisons only</TD></TR>
+ * <TR><TD>[] (array)</TD><TD>recursively applied to values </TD></TR>
+ * <TR><TD>Vector</TD><TD>recursively applied to elements </TD></TR>
+ * </TABLE>
+ * Note: arrays of primitives are also supported.
+ * </blockquote>
+ *
+ * A filter matches a key that has multiple values if it
+ * matches at least one of those values. For example,
+ * <pre>
+ * Dictionary d = new Hashtable();
+ * d.put( "cn", new String[] { "a", "b", "c" } );
+ * </pre>
+ * d will match <code>(cn=a)</code> and also <code>(cn=b)</code>
+ *
+ * <p>A filter component that references a key having an unrecognizable
+ * data type will evaluate to <code>false</code> .
+ */
+
+public class FilterImpl implements Filter /* since Framework 1.1 */{
+ /* public methods in org.osgi.framework.Filter */
+
+ /**
+ * Constructs a {@link FilterImpl} object. This filter object may be used
+ * to match a {@link ServiceReferenceImpl} or a Dictionary.
+ *
+ * <p> If the filter cannot be parsed, an {@link InvalidSyntaxException}
+ * will be thrown with a human readable message where the
+ * filter became unparsable.
+ *
+ * @param filterString the filter string.
+ * @exception InvalidSyntaxException If the filter parameter contains
+ * an invalid filter string that cannot be parsed.
+ */
+ public static FilterImpl newInstance(String filterString) throws InvalidSyntaxException {
+ return new Parser(filterString).parse();
+ }
+
+ /**
+ * Filter using a service's properties.
+ * <p>
+ * This {@code Filter} is executed using the keys and values of the
+ * referenced service's properties. The keys are looked up in a case
+ * insensitive manner.
+ *
+ * @param reference The reference to the service whose properties are used
+ * in the match.
+ * @return {@code true} if the service's properties match this
+ * {@code Filter}; {@code false} otherwise.
+ */
+ public boolean match(ServiceReference<?> reference) {
+ if (reference instanceof ServiceReferenceImpl) {
+ return matchCase(((ServiceReferenceImpl<?>) reference).getRegistration().getProperties());
+ }
+ return matchCase(new ServiceReferenceDictionary(reference));
+ }
+
+ /**
+ * Filter using a {@code Dictionary} with case insensitive key lookup. This
+ * {@code Filter} is executed using the specified {@code Dictionary}'s keys
+ * and values. The keys are looked up in a case insensitive manner.
+ *
+ * @param dictionary The {@code Dictionary} whose key/value pairs are used
+ * in the match.
+ * @return {@code true} if the {@code Dictionary}'s values match this
+ * filter; {@code false} otherwise.
+ * @throws IllegalArgumentException If {@code dictionary} contains case
+ * variants of the same key name.
+ */
+ public boolean match(Dictionary<String, ?> dictionary) {
+ if (dictionary != null) {
+ dictionary = new Headers<String, Object>(dictionary);
+ }
+
+ return matchCase(dictionary);
+ }
+
+ /**
+ * Filter using a {@code Dictionary}. This {@code Filter} is executed using
+ * the specified {@code Dictionary}'s keys and values. The keys are looked
+ * up in a normal manner respecting case.
+ *
+ * @param dictionary The {@code Dictionary} whose key/value pairs are used
+ * in the match.
+ * @return {@code true} if the {@code Dictionary}'s values match this
+ * filter; {@code false} otherwise.
+ * @since 1.3
+ */
+ public boolean matchCase(Dictionary<String, ?> dictionary) {
+ switch (op) {
+ case AND : {
+ FilterImpl[] filters = (FilterImpl[]) value;
+ for (FilterImpl f : filters) {
+ if (!f.matchCase(dictionary)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ case OR : {
+ FilterImpl[] filters = (FilterImpl[]) value;
+ for (FilterImpl f : filters) {
+ if (f.matchCase(dictionary)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ case NOT : {
+ FilterImpl filter = (FilterImpl) value;
+
+ return !filter.matchCase(dictionary);
+ }
+
+ case SUBSTRING :
+ case EQUAL :
+ case GREATER :
+ case LESS :
+ case APPROX : {
+ Object prop = (dictionary == null) ? null : dictionary.get(attr);
+
+ return compare(op, prop, value);
+ }
+
+ case PRESENT : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("PRESENT(" + attr + ")"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ Object prop = (dictionary == null) ? null : dictionary.get(attr);
+
+ return prop != null;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Filter using a {@code Map}. This {@code Filter} is executed using the
+ * specified {@code Map}'s keys and values. The keys are looked up in a
+ * normal manner respecting case.
+ *
+ * @param map The {@code Map} whose key/value pairs are used in the match.
+ * Maps with {@code null} key or values are not supported. A
+ * {@code null} value is considered not present to the filter.
+ * @return {@code true} if the {@code Map}'s values match this filter;
+ * {@code false} otherwise.
+ * @since 1.6
+ */
+ public boolean matches(Map<String, ?> map) {
+ switch (op) {
+ case AND : {
+ FilterImpl[] filters = (FilterImpl[]) value;
+ for (FilterImpl f : filters) {
+ if (!f.matches(map)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ case OR : {
+ FilterImpl[] filters = (FilterImpl[]) value;
+ for (FilterImpl f : filters) {
+ if (f.matches(map)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ case NOT : {
+ FilterImpl filter = (FilterImpl) value;
+
+ return !filter.matches(map);
+ }
+
+ case SUBSTRING :
+ case EQUAL :
+ case GREATER :
+ case LESS :
+ case APPROX : {
+ Object prop = (map == null) ? null : map.get(attr);
+
+ return compare(op, prop, value);
+ }
+
+ case PRESENT : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("PRESENT(" + attr + ")"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ Object prop = (map == null) ? null : map.get(attr);
+
+ return prop != null;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns this <code>Filter</code> object's filter string.
+ * <p>
+ * The filter string is normalized by removing whitespace which does not
+ * affect the meaning of the filter.
+ *
+ * @return Filter string.
+ */
+
+ public String toString() {
+ String result = filterString;
+ if (result == null) {
+ filterString = result = normalize().toString();
+ }
+ return result;
+ }
+
+ /**
+ * Returns this <code>Filter</code>'s normalized filter string.
+ * <p>
+ * The filter string is normalized by removing whitespace which does not
+ * affect the meaning of the filter.
+ *
+ * @return This <code>Filter</code>'s filter string.
+ */
+ private StringBuffer normalize() {
+ StringBuffer sb = new StringBuffer();
+ sb.append('(');
+
+ switch (op) {
+ case AND : {
+ sb.append('&');
+
+ FilterImpl[] filters = (FilterImpl[]) value;
+ for (FilterImpl f : filters) {
+ sb.append(f.normalize());
+ }
+
+ break;
+ }
+
+ case OR : {
+ sb.append('|');
+
+ FilterImpl[] filters = (FilterImpl[]) value;
+ for (FilterImpl f : filters) {
+ sb.append(f.normalize());
+ }
+
+ break;
+ }
+
+ case NOT : {
+ sb.append('!');
+ FilterImpl filter = (FilterImpl) value;
+ sb.append(filter.normalize());
+
+ break;
+ }
+
+ case SUBSTRING : {
+ sb.append(attr);
+ sb.append('=');
+
+ String[] substrings = (String[]) value;
+
+ for (String substr : substrings) {
+ if (substr == null) /* * */{
+ sb.append('*');
+ } else /* xxx */{
+ sb.append(encodeValue(substr));
+ }
+ }
+
+ break;
+ }
+ case EQUAL : {
+ sb.append(attr);
+ sb.append('=');
+ sb.append(encodeValue((String) value));
+
+ break;
+ }
+ case GREATER : {
+ sb.append(attr);
+ sb.append(">="); //$NON-NLS-1$
+ sb.append(encodeValue((String) value));
+
+ break;
+ }
+ case LESS : {
+ sb.append(attr);
+ sb.append("<="); //$NON-NLS-1$
+ sb.append(encodeValue((String) value));
+
+ break;
+ }
+ case APPROX : {
+ sb.append(attr);
+ sb.append("~="); //$NON-NLS-1$
+ sb.append(encodeValue(approxString((String) value)));
+
+ break;
+ }
+
+ case PRESENT : {
+ sb.append(attr);
+ sb.append("=*"); //$NON-NLS-1$
+
+ break;
+ }
+ }
+
+ sb.append(')');
+
+ return sb;
+ }
+
+ /**
+ * Compares this <code>Filter</code> object to another object.
+ *
+ * @param obj The object to compare against this <code>Filter</code>
+ * object.
+ * @return If the other object is a <code>Filter</code> object, then
+ * returns <code>this.toString().equals(obj.toString()</code>;
+ * <code>false</code> otherwise.
+ */
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+
+ if (!(obj instanceof Filter)) {
+ return false;
+ }
+
+ return this.toString().equals(obj.toString());
+ }
+
+ /**
+ * Returns the hashCode for this <code>Filter</code> object.
+ *
+ * @return The hashCode of the filter string; that is,
+ * <code>this.toString().hashCode()</code>.
+ */
+ public int hashCode() {
+ return this.toString().hashCode();
+ }
+
+ /* non public fields and methods for the Filter implementation */
+
+ /** filter operation */
+ private final int op;
+ private static final int EQUAL = 1;
+ private static final int APPROX = 2;
+ private static final int GREATER = 3;
+ private static final int LESS = 4;
+ private static final int PRESENT = 5;
+ private static final int SUBSTRING = 6;
+ private static final int AND = 7;
+ private static final int OR = 8;
+ private static final int NOT = 9;
+
+ /** filter attribute or null if operation AND, OR or NOT */
+ private final String attr;
+ /** filter operands */
+ private final Object value;
+
+ /* normalized filter string for topLevel Filter object */
+ private transient volatile String filterString;
+
+ FilterImpl(int operation, String attr, Object value) {
+ this.op = operation;
+ this.attr = attr;
+ this.value = value;
+ }
+
+ /**
+ * Encode the value string such that '(', '*', ')'
+ * and '\' are escaped.
+ *
+ * @param value unencoded value string.
+ * @return encoded value string.
+ */
+ private static String encodeValue(String value) {
+ boolean encoded = false;
+ int inlen = value.length();
+ int outlen = inlen << 1; /* inlen * 2 */
+
+ char[] output = new char[outlen];
+ value.getChars(0, inlen, output, inlen);
+
+ int cursor = 0;
+ for (int i = inlen; i < outlen; i++) {
+ char c = output[i];
+
+ switch (c) {
+ case '(' :
+ case '*' :
+ case ')' :
+ case '\\' : {
+ output[cursor] = '\\';
+ cursor++;
+ encoded = true;
+
+ break;
+ }
+ }
+
+ output[cursor] = c;
+ cursor++;
+ }
+
+ return encoded ? new String(output, 0, cursor) : value;
+ }
+
+ private boolean compare(int operation, Object value1, Object value2) {
+ if (value1 == null) {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("compare(" + value1 + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ return false;
+ }
+
+ if (value1 instanceof String) {
+ return compare_String(operation, (String) value1, value2);
+ }
+
+ Class<?> clazz = value1.getClass();
+ if (clazz.isArray()) {
+ Class<?> type = clazz.getComponentType();
+ if (type.isPrimitive()) {
+ return compare_PrimitiveArray(operation, type, value1, value2);
+ }
+ return compare_ObjectArray(operation, (Object[]) value1, value2);
+ }
+ if (value1 instanceof Collection<?>) {
+ return compare_Collection(operation, (Collection<?>) value1, value2);
+ }
+
+ if (value1 instanceof Integer) {
+ return compare_Integer(operation, ((Integer) value1).intValue(), value2);
+ }
+
+ if (value1 instanceof Long) {
+ return compare_Long(operation, ((Long) value1).longValue(), value2);
+ }
+
+ if (value1 instanceof Byte) {
+ return compare_Byte(operation, ((Byte) value1).byteValue(), value2);
+ }
+
+ if (value1 instanceof Short) {
+ return compare_Short(operation, ((Short) value1).shortValue(), value2);
+ }
+
+ if (value1 instanceof Character) {
+ return compare_Character(operation, ((Character) value1).charValue(), value2);
+ }
+
+ if (value1 instanceof Float) {
+ return compare_Float(operation, ((Float) value1).floatValue(), value2);
+ }
+
+ if (value1 instanceof Double) {
+ return compare_Double(operation, ((Double) value1).doubleValue(), value2);
+ }
+
+ if (value1 instanceof Boolean) {
+ return compare_Boolean(operation, ((Boolean) value1).booleanValue(), value2);
+ }
+ if (value1 instanceof Comparable<?>) {
+ @SuppressWarnings("unchecked")
+ Comparable<Object> comparable = (Comparable<Object>) value1;
+ return compare_Comparable(operation, comparable, value2);
+ }
+
+ return compare_Unknown(operation, value1, value2); // RFC 59
+ }
+
+ private boolean compare_Collection(int operation, Collection<?> collection, Object value2) {
+ for (Object value1 : collection) {
+ if (compare(operation, value1, value2)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private boolean compare_ObjectArray(int operation, Object[] array, Object value2) {
+ for (Object value1 : array) {
+ if (compare(operation, value1, value2)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private boolean compare_PrimitiveArray(int operation, Class<?> type, Object primarray, Object value2) {
+ if (Integer.TYPE.isAssignableFrom(type)) {
+ int[] array = (int[]) primarray;
+ for (int value1 : array) {
+ if (compare_Integer(operation, value1, value2)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ if (Long.TYPE.isAssignableFrom(type)) {
+ long[] array = (long[]) primarray;
+ for (long value1 : array) {
+ if (compare_Long(operation, value1, value2)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ if (Byte.TYPE.isAssignableFrom(type)) {
+ byte[] array = (byte[]) primarray;
+ for (byte value1 : array) {
+ if (compare_Byte(operation, value1, value2)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ if (Short.TYPE.isAssignableFrom(type)) {
+ short[] array = (short[]) primarray;
+ for (short value1 : array) {
+ if (compare_Short(operation, value1, value2)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ if (Character.TYPE.isAssignableFrom(type)) {
+ char[] array = (char[]) primarray;
+ for (char value1 : array) {
+ if (compare_Character(operation, value1, value2)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ if (Float.TYPE.isAssignableFrom(type)) {
+ float[] array = (float[]) primarray;
+ for (float value1 : array) {
+ if (compare_Float(operation, value1, value2)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ if (Double.TYPE.isAssignableFrom(type)) {
+ double[] array = (double[]) primarray;
+ for (double value1 : array) {
+ if (compare_Double(operation, value1, value2)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ if (Boolean.TYPE.isAssignableFrom(type)) {
+ boolean[] array = (boolean[]) primarray;
+ for (boolean value1 : array) {
+ if (compare_Boolean(operation, value1, value2)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ return false;
+ }
+
+ private boolean compare_String(int operation, String string, Object value2) {
+ switch (operation) {
+ case SUBSTRING : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("SUBSTRING(" + string + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ String[] substrings = (String[]) value2;
+ int pos = 0;
+ for (int i = 0, size = substrings.length; i < size; i++) {
+ String substr = substrings[i];
+
+ if (i + 1 < size) /* if this is not that last substr */{
+ if (substr == null) /* * */{
+ String substr2 = substrings[i + 1];
+
+ if (substr2 == null) /* ** */
+ continue; /* ignore first star */
+ /* *xxx */
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("indexOf(\"" + substr2 + "\"," + pos + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ int index = string.indexOf(substr2, pos);
+ if (index == -1) {
+ return false;
+ }
+
+ pos = index + substr2.length();
+ if (i + 2 < size) // if there are more substrings, increment over the string we just matched; otherwise need to do the last substr check
+ i++;
+ } else /* xxx */{
+ int len = substr.length();
+
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("regionMatches(" + pos + ",\"" + substr + "\")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ if (string.regionMatches(pos, substr, 0, len)) {
+ pos += len;
+ } else {
+ return false;
+ }
+ }
+ } else /* last substr */{
+ if (substr == null) /* * */{
+ return true;
+ }
+ /* xxx */
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("regionMatches(" + pos + "," + substr + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return string.endsWith(substr);
+ }
+ }
+
+ return true;
+ }
+ case EQUAL : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("EQUAL(" + string + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return string.equals(value2);
+ }
+ case APPROX : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("APPROX(" + string + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ string = approxString(string);
+ String string2 = approxString((String) value2);
+
+ return string.equalsIgnoreCase(string2);
+ }
+ case GREATER : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("GREATER(" + string + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return string.compareTo((String) value2) >= 0;
+ }
+ case LESS : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("LESS(" + string + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return string.compareTo((String) value2) <= 0;
+ }
+ }
+
+ return false;
+ }
+
+ private boolean compare_Integer(int operation, int intval, Object value2) {
+ if (operation == SUBSTRING) {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("SUBSTRING(" + intval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return false;
+ }
+
+ int intval2;
+ try {
+ intval2 = Integer.parseInt(((String) value2).trim());
+ } catch (IllegalArgumentException e) {
+ return false;
+ }
+ switch (operation) {
+ case EQUAL : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("EQUAL(" + intval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return intval == intval2;
+ }
+ case APPROX : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("APPROX(" + intval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return intval == intval2;
+ }
+ case GREATER : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("GREATER(" + intval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return intval >= intval2;
+ }
+ case LESS : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("LESS(" + intval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return intval <= intval2;
+ }
+ }
+
+ return false;
+ }
+
+ private boolean compare_Long(int operation, long longval, Object value2) {
+ if (operation == SUBSTRING) {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("SUBSTRING(" + longval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return false;
+ }
+
+ long longval2;
+ try {
+ longval2 = Long.parseLong(((String) value2).trim());
+ } catch (IllegalArgumentException e) {
+ return false;
+ }
+ switch (operation) {
+ case EQUAL : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("EQUAL(" + longval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return longval == longval2;
+ }
+ case APPROX : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("APPROX(" + longval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return longval == longval2;
+ }
+ case GREATER : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("GREATER(" + longval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return longval >= longval2;
+ }
+ case LESS : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("LESS(" + longval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return longval <= longval2;
+ }
+ }
+
+ return false;
+ }
+
+ private boolean compare_Byte(int operation, byte byteval, Object value2) {
+ if (operation == SUBSTRING) {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("SUBSTRING(" + byteval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return false;
+ }
+
+ byte byteval2;
+ try {
+ byteval2 = Byte.parseByte(((String) value2).trim());
+ } catch (IllegalArgumentException e) {
+ return false;
+ }
+ switch (operation) {
+ case EQUAL : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("EQUAL(" + byteval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return byteval == byteval2;
+ }
+ case APPROX : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("APPROX(" + byteval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return byteval == byteval2;
+ }
+ case GREATER : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("GREATER(" + byteval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return byteval >= byteval2;
+ }
+ case LESS : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("LESS(" + byteval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return byteval <= byteval2;
+ }
+ }
+
+ return false;
+ }
+
+ private boolean compare_Short(int operation, short shortval, Object value2) {
+ if (operation == SUBSTRING) {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("SUBSTRING(" + shortval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return false;
+ }
+
+ short shortval2;
+ try {
+ shortval2 = Short.parseShort(((String) value2).trim());
+ } catch (IllegalArgumentException e) {
+ return false;
+ }
+ switch (operation) {
+ case EQUAL : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("EQUAL(" + shortval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return shortval == shortval2;
+ }
+ case APPROX : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("APPROX(" + shortval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return shortval == shortval2;
+ }
+ case GREATER : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("GREATER(" + shortval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return shortval >= shortval2;
+ }
+ case LESS : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("LESS(" + shortval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return shortval <= shortval2;
+ }
+ }
+
+ return false;
+ }
+
+ private boolean compare_Character(int operation, char charval, Object value2) {
+ if (operation == SUBSTRING) {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("SUBSTRING(" + charval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return false;
+ }
+
+ char charval2;
+ try {
+ charval2 = ((String) value2).charAt(0);
+ } catch (IndexOutOfBoundsException e) {
+ return false;
+ }
+ switch (operation) {
+ case EQUAL : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("EQUAL(" + charval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return charval == charval2;
+ }
+ case APPROX : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("APPROX(" + charval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return (charval == charval2) || (Character.toUpperCase(charval) == Character.toUpperCase(charval2)) || (Character.toLowerCase(charval) == Character.toLowerCase(charval2));
+ }
+ case GREATER : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("GREATER(" + charval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return charval >= charval2;
+ }
+ case LESS : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("LESS(" + charval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return charval <= charval2;
+ }
+ }
+
+ return false;
+ }
+
+ private boolean compare_Boolean(int operation, boolean boolval, Object value2) {
+ if (operation == SUBSTRING) {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("SUBSTRING(" + boolval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return false;
+ }
+
+ boolean boolval2 = Boolean.valueOf(((String) value2).trim()).booleanValue();
+ switch (operation) {
+ case EQUAL : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("EQUAL(" + boolval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return boolval == boolval2;
+ }
+ case APPROX : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("APPROX(" + boolval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return boolval == boolval2;
+ }
+ case GREATER : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("GREATER(" + boolval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return boolval == boolval2;
+ }
+ case LESS : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("LESS(" + boolval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return boolval == boolval2;
+ }
+ }
+
+ return false;
+ }
+
+ private boolean compare_Float(int operation, float floatval, Object value2) {
+ if (operation == SUBSTRING) {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("SUBSTRING(" + floatval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return false;
+ }
+
+ float floatval2;
+ try {
+ floatval2 = Float.parseFloat(((String) value2).trim());
+ } catch (IllegalArgumentException e) {
+ return false;
+ }
+ switch (operation) {
+ case EQUAL : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("EQUAL(" + floatval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return Float.compare(floatval, floatval2) == 0;
+ }
+ case APPROX : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("APPROX(" + floatval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return Float.compare(floatval, floatval2) == 0;
+ }
+ case GREATER : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("GREATER(" + floatval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return Float.compare(floatval, floatval2) >= 0;
+ }
+ case LESS : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("LESS(" + floatval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return Float.compare(floatval, floatval2) <= 0;
+ }
+ }
+
+ return false;
+ }
+
+ private boolean compare_Double(int operation, double doubleval, Object value2) {
+ if (operation == SUBSTRING) {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("SUBSTRING(" + doubleval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return false;
+ }
+
+ double doubleval2;
+ try {
+ doubleval2 = Double.parseDouble(((String) value2).trim());
+ } catch (IllegalArgumentException e) {
+ return false;
+ }
+ switch (operation) {
+ case EQUAL : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("EQUAL(" + doubleval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return Double.compare(doubleval, doubleval2) == 0;
+ }
+ case APPROX : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("APPROX(" + doubleval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return Double.compare(doubleval, doubleval2) == 0;
+ }
+ case GREATER : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("GREATER(" + doubleval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return Double.compare(doubleval, doubleval2) >= 0;
+ }
+ case LESS : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("LESS(" + doubleval + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return Double.compare(doubleval, doubleval2) <= 0;
+ }
+ }
+
+ return false;
+ }
+
+ private static Object valueOf(Class<?> target, String value2) {
+ do {
+ Method method;
+ try {
+ method = target.getMethod("valueOf", String.class); //$NON-NLS-1$
+ } catch (NoSuchMethodException e) {
+ break;
+ }
+ if (Modifier.isStatic(method.getModifiers()) && target.isAssignableFrom(method.getReturnType())) {
+ setAccessible(method);
+ try {
+ return method.invoke(null, value2.trim());
+ } catch (IllegalAccessException e) {
+ return null;
+ } catch (InvocationTargetException e) {
+ return null;
+ }
+ }
+ } while (false);
+
+ do {
+ Constructor<?> constructor;
+ try {
+ constructor = target.getConstructor(String.class);
+ } catch (NoSuchMethodException e) {
+ break;
+ }
+ setAccessible(constructor);
+ try {
+ return constructor.newInstance(value2.trim());
+ } catch (IllegalAccessException e) {
+ return null;
+ } catch (InvocationTargetException e) {
+ return null;
+ } catch (InstantiationException e) {
+ return null;
+ }
+ } while (false);
+
+ return null;
+ }
+
+ private static void setAccessible(AccessibleObject accessible) {
+ if (!accessible.isAccessible()) {
+ AccessController.doPrivileged(new SetAccessibleAction(accessible));
+ }
+ }
+
+ private boolean compare_Comparable(int operation, Comparable<Object> value1, Object value2) {
+ if (operation == SUBSTRING) {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("SUBSTRING(" + value1 + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return false;
+ }
+ value2 = valueOf(value1.getClass(), (String) value2);
+ if (value2 == null) {
+ return false;
+ }
+
+ try {
+ switch (operation) {
+ case EQUAL : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("EQUAL(" + value1 + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return value1.compareTo(value2) == 0;
+ }
+ case APPROX : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("APPROX(" + value1 + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return value1.compareTo(value2) == 0;
+ }
+ case GREATER : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("GREATER(" + value1 + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return value1.compareTo(value2) >= 0;
+ }
+ case LESS : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("LESS(" + value1 + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return value1.compareTo(value2) <= 0;
+ }
+ }
+ } catch (Exception e) {
+ // if the compareTo method throws an exception; return false
+ return false;
+ }
+ return false;
+ }
+
+ private boolean compare_Unknown(int operation, Object value1, Object value2) { //RFC 59
+ if (operation == SUBSTRING) {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("SUBSTRING(" + value1 + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return false;
+ }
+ value2 = valueOf(value1.getClass(), (String) value2);
+ if (value2 == null) {
+ return false;
+ }
+
+ try {
+ switch (operation) {
+ case EQUAL : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("EQUAL(" + value1 + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return value1.equals(value2);
+ }
+ case APPROX : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("APPROX(" + value1 + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return value1.equals(value2);
+ }
+ case GREATER : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("GREATER(" + value1 + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return value1.equals(value2);
+ }
+ case LESS : {
+ if (Debug.DEBUG_FILTER) {
+ Debug.println("LESS(" + value1 + "," + value2 + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ return value1.equals(value2);
+ }
+ }
+ } catch (Exception e) {
+ // if the equals method throws an exception; return false
+ return false;
+ }
+
+ return false;
+ }
+
+ /**
+ * Map a string for an APPROX (~=) comparison.
+ *
+ * This implementation removes white spaces.
+ * This is the minimum implementation allowed by
+ * the OSGi spec.
+ *
+ * @param input Input string.
+ * @return String ready for APPROX comparison.
+ */
+ private static String approxString(String input) {
+ boolean changed = false;
+ char[] output = input.toCharArray();
+ int cursor = 0;
+ for (char c : output) {
+ if (Character.isWhitespace(c)) {
+ changed = true;
+ continue;
+ }
+
+ output[cursor] = c;
+ cursor++;
+ }
+
+ return changed ? new String(output, 0, cursor) : input;
+ }
+
+ /**
+ * Returns the leftmost required objectClass value for the filter to evaluate to true.
+ *
+ * @return The leftmost required objectClass value or null if none could be determined.
+ */
+ public String getRequiredObjectClass() {
+ return getPrimaryKeyValue(Constants.OBJECTCLASS);
+ }
+
+ /**
+ * Returns the leftmost required primary key value for the filter to evaluate to true.
+ * This is useful for indexing candidates to match against this filter.
+ * @param primaryKey the primary key
+ * @return The leftmost required primary key value or null if none could be determined.
+ */
+ public String getPrimaryKeyValue(String primaryKey) {
+ // just checking for simple filters here where primaryKey is the only attr or it is one attr of a base '&' clause
+ // (primaryKey=org.acme.BrickService) OK
+ // (&(primaryKey=org.acme.BrickService)(|(vendor=IBM)(vendor=SUN))) OK
+ // (primaryKey=org.acme.*) NOT OK
+ // (|(primaryKey=org.acme.BrickService)(primaryKey=org.acme.CementService)) NOT OK
+ // (&(primaryKey=org.acme.BrickService)(primaryKey=org.acme.CementService)) OK but only the first objectClass is returned
+ switch (op) {
+ case EQUAL :
+ if (attr.equalsIgnoreCase(primaryKey) && (value instanceof String))
+ return (String) value;
+ break;
+ case AND :
+ FilterImpl[] clauses = (FilterImpl[]) value;
+ for (FilterImpl clause : clauses)
+ if (clause.op == EQUAL) {
+ String result = clause.getPrimaryKeyValue(primaryKey);
+ if (result != null)
+ return result;
+ }
+ break;
+ }
+ return null;
+ }
+
+ /**
+ * Returns all the attributes contained within this filter
+ * @return all the attributes contained within this filter
+ */
+ public String[] getAttributes() {
+ List<String> results = new ArrayList<String>();
+ getAttributesInternal(results);
+ return results.toArray(new String[results.size()]);
+ }
+
+ private void getAttributesInternal(List<String> results) {
+ if (value instanceof FilterImpl[]) {
+ FilterImpl[] children = (FilterImpl[]) value;
+ for (FilterImpl child : children)
+ child.getAttributesInternal(results);
+ return;
+ } else if (value instanceof FilterImpl) {
+ // The NOT operation only has one child filter (bug 188075)
+ FilterImpl child = ((FilterImpl) value);
+ child.getAttributesInternal(results);
+ return;
+ }
+ if (attr != null)
+ results.add(attr);
+ }
+
+ /**
+ * Parser class for OSGi filter strings. This class parses
+ * the complete filter string and builds a tree of Filter
+ * objects rooted at the parent.
+ */
+ private static class Parser {
+ private final String filterstring;
+ private final char[] filterChars;
+ private int pos;
+
+ Parser(String filterstring) {
+ this.filterstring = filterstring;
+ filterChars = filterstring.toCharArray();
+ pos = 0;
+ }
+
+ FilterImpl parse() throws InvalidSyntaxException {
+ FilterImpl filter;
+ try {
+ filter = parse_filter();
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new InvalidSyntaxException(Msg.FILTER_TERMINATED_ABRUBTLY, filterstring);
+ }
+
+ if (pos != filterChars.length) {
+ throw new InvalidSyntaxException(NLS.bind(Msg.FILTER_TRAILING_CHARACTERS, filterstring.substring(pos)), filterstring);
+ }
+ return filter;
+ }
+
+ private FilterImpl parse_filter() throws InvalidSyntaxException {
+ FilterImpl filter;
+ skipWhiteSpace();
+
+ if (filterChars[pos] != '(') {
+ throw new InvalidSyntaxException(NLS.bind(Msg.FILTER_MISSING_LEFTPAREN, filterstring.substring(pos)), filterstring);
+ }
+
+ pos++;
+
+ filter = parse_filtercomp();
+
+ skipWhiteSpace();
+
+ if (filterChars[pos] != ')') {
+ throw new InvalidSyntaxException(NLS.bind(Msg.FILTER_MISSING_RIGHTPAREN, filterstring.substring(pos)), filterstring);
+ }
+
+ pos++;
+
+ skipWhiteSpace();
+
+ return filter;
+ }
+
+ private FilterImpl parse_filtercomp() throws InvalidSyntaxException {
+ skipWhiteSpace();
+
+ char c = filterChars[pos];
+
+ switch (c) {
+ case '&' : {
+ pos++;
+ return parse_and();
+ }
+ case '|' : {
+ pos++;
+ return parse_or();
+ }
+ case '!' : {
+ pos++;
+ return parse_not();
+ }
+ }
+ return parse_item();
+ }
+
+ private FilterImpl parse_and() throws InvalidSyntaxException {
+ int lookahead = pos;
+ skipWhiteSpace();
+
+ if (filterChars[pos] != '(') {
+ pos = lookahead - 1;
+ return parse_item();
+ }
+
+ List<FilterImpl> operands = new ArrayList<FilterImpl>(10);
+
+ while (filterChars[pos] == '(') {
+ FilterImpl child = parse_filter();
+ operands.add(child);
+ }
+
+ return new FilterImpl(FilterImpl.AND, null, operands.toArray(new FilterImpl[operands.size()]));
+ }
+
+ private FilterImpl parse_or() throws InvalidSyntaxException {
+ int lookahead = pos;
+ skipWhiteSpace();
+
+ if (filterChars[pos] != '(') {
+ pos = lookahead - 1;
+ return parse_item();
+ }
+
+ List<FilterImpl> operands = new ArrayList<FilterImpl>(10);
+
+ while (filterChars[pos] == '(') {
+ FilterImpl child = parse_filter();
+ operands.add(child);
+ }
+
+ return new FilterImpl(FilterImpl.OR, null, operands.toArray(new FilterImpl[operands.size()]));
+ }
+
+ private FilterImpl parse_not() throws InvalidSyntaxException {
+ int lookahead = pos;
+ skipWhiteSpace();
+
+ if (filterChars[pos] != '(') {
+ pos = lookahead - 1;
+ return parse_item();
+ }
+
+ FilterImpl child = parse_filter();
+
+ return new FilterImpl(FilterImpl.NOT, null, child);
+ }
+
+ private FilterImpl parse_item() throws InvalidSyntaxException {
+ String attr = parse_attr();
+
+ skipWhiteSpace();
+
+ switch (filterChars[pos]) {
+ case '~' : {
+ if (filterChars[pos + 1] == '=') {
+ pos += 2;
+ return new FilterImpl(FilterImpl.APPROX, attr, parse_value());
+ }
+ break;
+ }
+ case '>' : {
+ if (filterChars[pos + 1] == '=') {
+ pos += 2;
+ return new FilterImpl(FilterImpl.GREATER, attr, parse_value());
+ }
+ break;
+ }
+ case '<' : {
+ if (filterChars[pos + 1] == '=') {
+ pos += 2;
+ return new FilterImpl(FilterImpl.LESS, attr, parse_value());
+ }
+ break;
+ }
+ case '=' : {
+ if (filterChars[pos + 1] == '*') {
+ int oldpos = pos;
+ pos += 2;
+ skipWhiteSpace();
+ if (filterChars[pos] == ')') {
+ return new FilterImpl(FilterImpl.PRESENT, attr, null);
+ }
+ pos = oldpos;
+ }
+
+ pos++;
+ Object string = parse_substring();
+
+ if (string instanceof String) {
+ return new FilterImpl(FilterImpl.EQUAL, attr, string);
+ }
+ return new FilterImpl(FilterImpl.SUBSTRING, attr, string);
+ }
+ }
+
+ throw new InvalidSyntaxException(NLS.bind(Msg.FILTER_INVALID_OPERATOR, filterstring.substring(pos)), filterstring);
+ }
+
+ private String parse_attr() throws InvalidSyntaxException {
+ skipWhiteSpace();
+
+ int begin = pos;
+ int end = pos;
+
+ char c = filterChars[pos];
+
+ while (c != '~' && c != '<' && c != '>' && c != '=' && c != '(' && c != ')') {
+ pos++;
+
+ if (!Character.isWhitespace(c)) {
+ end = pos;
+ }
+
+ c = filterChars[pos];
+ }
+
+ int length = end - begin;
+
+ if (length == 0) {
+ throw new InvalidSyntaxException(NLS.bind(Msg.FILTER_MISSING_ATTR, filterstring.substring(pos)), filterstring);
+ }
+
+ return new String(filterChars, begin, length);
+ }
+
+ private String parse_value() throws InvalidSyntaxException {
+ StringBuffer sb = new StringBuffer(filterChars.length - pos);
+
+ parseloop: while (true) {
+ char c = filterChars[pos];
+
+ switch (c) {
+ case ')' : {
+ break parseloop;
+ }
+
+ case '(' : {
+ throw new InvalidSyntaxException(NLS.bind(Msg.FILTER_INVALID_VALUE, filterstring.substring(pos)), filterstring);
+ }
+
+ case '\\' : {
+ pos++;
+ c = filterChars[pos];
+ /* fall through into default */
+ }
+
+ default : {
+ sb.append(c);
+ pos++;
+ break;
+ }
+ }
+ }
+
+ if (sb.length() == 0) {
+ throw new InvalidSyntaxException(NLS.bind(Msg.FILTER_MISSING_VALUE, filterstring.substring(pos)), filterstring);
+ }
+
+ return sb.toString();
+ }
+
+ private Object parse_substring() throws InvalidSyntaxException {
+ StringBuffer sb = new StringBuffer(filterChars.length - pos);
+
+ List<String> operands = new ArrayList<String>(10);
+
+ parseloop: while (true) {
+ char c = filterChars[pos];
+
+ switch (c) {
+ case ')' : {
+ if (sb.length() > 0) {
+ operands.add(sb.toString());
+ }
+
+ break parseloop;
+ }
+
+ case '(' : {
+ throw new InvalidSyntaxException(NLS.bind(Msg.FILTER_INVALID_VALUE, filterstring.substring(pos)), filterstring);
+ }
+
+ case '*' : {
+ if (sb.length() > 0) {
+ operands.add(sb.toString());
+ }
+
+ sb.setLength(0);
+
+ operands.add(null);
+ pos++;
+
+ break;
+ }
+
+ case '\\' : {
+ pos++;
+ c = filterChars[pos];
+ /* fall through into default */
+ }
+
+ default : {
+ sb.append(c);
+ pos++;
+ break;
+ }
+ }
+ }
+
+ int size = operands.size();
+
+ if (size == 0) {
+ return ""; //$NON-NLS-1$
+ }
+
+ if (size == 1) {
+ Object single = operands.get(0);
+
+ if (single != null) {
+ return single;
+ }
+ }
+
+ return operands.toArray(new String[size]);
+ }
+
+ private void skipWhiteSpace() {
+ for (int length = filterChars.length; (pos < length) && Character.isWhitespace(filterChars[pos]);) {
+ pos++;
+ }
+ }
+ }
+
+ /**
+ * This Dictionary is used for key lookup from a ServiceReference during
+ * filter evaluation. This Dictionary implementation only supports the get
+ * operation using a String key as no other operations are used by the
+ * Filter implementation.
+ *
+ */
+ private static class ServiceReferenceDictionary extends Dictionary<String, Object> {
+ private final ServiceReference<?> reference;
+
+ ServiceReferenceDictionary(ServiceReference<?> reference) {
+ this.reference = reference;
+ }
+
+ public Object get(Object key) {
+ if (reference == null) {
+ return null;
+ }
+ return reference.getProperty((String) key);
+ }
+
+ public boolean isEmpty() {
+ throw new UnsupportedOperationException();
+ }
+
+ public Enumeration<String> keys() {
+ throw new UnsupportedOperationException();
+ }
+
+ public Enumeration<Object> elements() {
+ throw new UnsupportedOperationException();
+ }
+
+ public Object put(String key, Object value) {
+ throw new UnsupportedOperationException();
+ }
+
+ public Object remove(Object key) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int size() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ private static class SetAccessibleAction implements PrivilegedAction<Object> {
+ private final AccessibleObject accessible;
+
+ SetAccessibleAction(AccessibleObject accessible) {
+ this.accessible = accessible;
+ }
+
+ public Object run() {
+ accessible.setAccessible(true);
+ return null;
+ }
+ }
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/Framework.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/Framework.java
new file mode 100644
index 000000000..d3aeaf645
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/Framework.java
@@ -0,0 +1,2007 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2012 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.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.*;
+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.framework.hooks.bundle.*;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ * Core OSGi Framework class.
+ */
+public class Framework implements 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;
+ private final int BSN_VERSION;
+ private static final int BSN_VERSION_SINGLE = 1;
+ private static final int BSN_VERSION_MULTIPLE = 2;
+ private static final int BSN_VERSION_MANAGED = 3;
+
+ /*
+ * The following maps objects keep track of event listeners
+ * by BundleContext. Each element is a Map that is the set
+ * of event listeners for a particular BundleContext. The max number of
+ * elements each of the following maps will have is the number of bundles
+ * installed in the Framework.
+ */
+ // Map of BundleContexts for bundle's BundleListeners.
+ private final Map<BundleContextImpl, CopyOnWriteIdentityMap<BundleListener, BundleListener>> allBundleListeners = new HashMap<BundleContextImpl, CopyOnWriteIdentityMap<BundleListener, BundleListener>>();
+ protected static final int BUNDLEEVENT = 1;
+ // Map of BundleContexts for bundle's SynchronousBundleListeners.
+ private final Map<BundleContextImpl, CopyOnWriteIdentityMap<SynchronousBundleListener, SynchronousBundleListener>> allSyncBundleListeners = new HashMap<BundleContextImpl, CopyOnWriteIdentityMap<SynchronousBundleListener, SynchronousBundleListener>>();
+ protected static final int BUNDLEEVENTSYNC = 2;
+ /* SERVICEEVENT(3) is now handled by ServiceRegistry */
+ // Map of BundleContexts for bundle's FrameworkListeners.
+ private final Map<BundleContextImpl, CopyOnWriteIdentityMap<FrameworkListener, FrameworkListener>> allFrameworkListeners = new HashMap<BundleContextImpl, CopyOnWriteIdentityMap<FrameworkListener, FrameworkListener>>();
+ 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;
+ static final String eventHookName = EventHook.class.getName();
+ static final String findHookName = FindHook.class.getName();
+ static final String collisionHookName = CollisionHook.class.getName();
+ /** EventManager for event delivery. */
+ protected EventManager eventManager;
+ /* Reservation object for install synchronization */
+ private Map<String, Thread> 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$
+ private final boolean allowRefreshDuplicateBSN = Boolean.TRUE.toString().equals(FrameworkProperties.getProperty(Constants.REFRESH_DUPLICATE_BSN, "true")); //$NON-NLS-1$
+ ClassLoaderDelegateHook[] delegateHooks;
+ private volatile boolean forcedRestart = false;
+ /**
+ * The AliasMapper used to alias OS Names.
+ */
+ protected static AliasMapper aliasMapper = new AliasMapper();
+ SecureAction secureAction = AccessController.doPrivileged(SecureAction.createSecureAction());
+ // cache of AdminPermissions keyed by Bundle ID
+ private final Map<Long, Map<String, AdminPermission>> adminPermissions = new HashMap<Long, Map<String, AdminPermission>>();
+
+ // we need to hold these so that we can unregister them at shutdown
+ private StreamHandlerFactory streamHandlerFactory;
+ private ContentHandlerFactory contentHandlerFactory;
+
+ private volatile ServiceTracker<SignedContentFactory, SignedContentFactory> 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<File> {
+ private AbstractBundle bundle;
+ private String filename;
+
+ public GetDataFileAction(AbstractBundle bundle, String filename) {
+ this.bundle = bundle;
+ this.filename = filename;
+ }
+
+ public File run() {
+ return bundle.getBundleData().getDataFile(filename);
+ }
+ }
+
+ /**
+ * Constructor for the Framework instance. This method initializes the
+ * framework to an unlaunched state.
+ *
+ */
+ public Framework(FrameworkAdaptor adaptor) {
+ if (Profile.PROFILE && Profile.STARTUP)
+ Profile.logEnter("Framework.initialze()", null); //$NON-NLS-1$
+ String bsnVersion = FrameworkProperties.getProperty(Constants.FRAMEWORK_BSNVERSION);
+ if (Constants.FRAMEWORK_BSNVERSION_SINGLE.equals(bsnVersion)) {
+ BSN_VERSION = BSN_VERSION_SINGLE;
+ } else if (Constants.FRAMEWORK_BSNVERSION_MULTIPLE.equals(bsnVersion)) {
+ BSN_VERSION = BSN_VERSION_MULTIPLE;
+ } else {
+ BSN_VERSION = BSN_VERSION_MANAGED;
+ }
+ long start = System.currentTimeMillis();
+ this.adaptor = adaptor;
+ delegateHooks = adaptor instanceof BaseAdaptor ? ((BaseAdaptor) adaptor).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 */
+ adaptor.initialize(this);
+ if (Profile.PROFILE && Profile.STARTUP)
+ Profile.logTime("Framework.initialze()", "adapter initialized"); //$NON-NLS-1$//$NON-NLS-2$
+ try {
+ adaptor.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(adaptor.getProperties());
+ /* initialize admin objects */
+ packageAdmin = new PackageAdminImpl(this);
+ try {
+ // always create security admin even with security off
+ securityAdmin = new SecurityAdmin(null, this, adaptor.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$
+ 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 HashMap<String, Thread>(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, adaptor);
+ /* install ContentHandlerFactory for OSGi URLStreamHandler support */
+ installContentHandlerFactory(systemBundle.context, adaptor);
+ 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 = adaptor.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$
+ properties.put(Constants.FRAMEWORK_UUID, new UniversalUniqueIdentifier().toString());
+ }
+
+ 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);
+ List<String> exactMatch = new ArrayList<String>(bootPackages.length);
+ List<String> stemMatch = new ArrayList<String>(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.isEmpty())
+ bootDelegation = exactMatch.toArray(new String[exactMatch.size()]);
+ if (!stemMatch.isEmpty())
+ bootDelegationStems = stemMatch.toArray(new String[stemMatch.size()]);
+ }
+
+ @SuppressWarnings("deprecation")
+ 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);
+ }
+ // set the org.osgi.framework.system.capabilities property according to the java profile
+ if (properties.getProperty(Constants.FRAMEWORK_SYSTEMCAPABILITIES) == null) {
+ String systemCapabilities = profileProps.getProperty(Constants.FRAMEWORK_SYSTEMCAPABILITIES);
+ if (systemCapabilities != null)
+ properties.put(Constants.FRAMEWORK_SYSTEMCAPABILITIES, systemCapabilities);
+ }
+ }
+
+ 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<AbstractBundle> allBundles = bundles.getBundles();
+ int size = allBundles.size();
+ for (int i = 0; i < size; i++) {
+ AbstractBundle bundle = allBundles.get(i);
+ bundle.close();
+ }
+ bundles.removeAllBundles();
+ }
+ serviceRegistry = null;
+ allBundleListeners.clear();
+ allSyncBundleListeners.clear();
+ allFrameworkListeners.clear();
+ 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<SignedContentFactory, SignedContentFactory>(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(int operationType, Bundle target, BundleData bundledata, boolean setBundle) throws BundleException {
+ // Check for a bundle already installed with the same symbolic name and version.
+ if (BSN_VERSION != BSN_VERSION_MULTIPLE && bundledata.getSymbolicName() != null) {
+ List<AbstractBundle> installedBundles = getBundleBySymbolicName(bundledata.getSymbolicName(), bundledata.getVersion());
+ if (operationType == CollisionHook.UPDATING) {
+ installedBundles.remove(target);
+ }
+ if (BSN_VERSION == BSN_VERSION_MANAGED && !installedBundles.isEmpty()) {
+ notifyCollisionHooks(operationType, target, installedBundles);
+ }
+ if (!installedBundles.isEmpty()) {
+ Bundle installedBundle = installedBundles.iterator().next();
+ 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 transient 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.
+ */
+ AbstractBundle installBundle(final String location, final InputStream in, final BundleContextImpl origin) 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<AbstractBundle>() {
+ public AbstractBundle 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, origin);
+ }
+ }, origin);
+ }
+
+ /**
+ * 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<AbstractBundle> action, BundleContext origin) 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) {
+ Bundle visible = origin.getBundle(bundle.getBundleId());
+ if (visible == null) {
+ BundleData data = bundle.getBundleData();
+ String msg = NLS.bind(Msg.BUNDLE_INSTALL_SAME_UNIQUEID, new Object[] {data.getSymbolicName(), data.getVersion().toString(), data.getLocation()});
+ throw new BundleException(msg, BundleException.REJECTED_BY_HOOK);
+ }
+ return bundle;
+ }
+ Thread current = Thread.currentThread();
+ /* Check for and make reservation */
+ Thread reservation = 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 = AccessController.doPrivileged(action);
+ publishBundleEvent(new BundleEvent(BundleEvent.INSTALLED, bundle, origin.getBundle()));
+ 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.
+ * @param callerContext
+ * The caller access control context
+ * @param origin
+ * The origin bundle context that is installing the the bundle
+ * @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, BundleContextImpl origin) throws BundleException {
+ BundleOperation storage = adaptor.installBundle(location, source);
+ final AbstractBundle bundle;
+ try {
+ BundleData bundledata = storage.begin();
+ bundle = createAndVerifyBundle(CollisionHook.INSTALLING, origin.getBundle(), 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<Object>() {
+ 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();
+ }
+ }
+ // must add the bundle before committing (bug 330905)
+ bundles.add(bundle);
+ storage.commit(false);
+ } catch (Throwable error) {
+ bundles.remove(bundle);
+ synchronized (bundles) {
+ bundle.unload();
+ }
+ bundle.close();
+ throw error;
+ } finally {
+ if (bundleStats != null)
+ bundleStats.watchBundle(bundle, BundleWatcher.END_INSTALLING);
+ }
+
+ } 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);
+ }
+ }
+
+ AbstractBundle getBundle(final BundleContextImpl context, long id) {
+ AbstractBundle bundle = getBundle(id);
+ // TODO we make the system bundle special because there are lots of places
+ // where we assume the system bundle can get all bundles
+ if (bundle == null || context.getBundle().getBundleId() == 0)
+ return bundle;
+ List<AbstractBundle> single = new ArrayList<AbstractBundle>(1);
+ single.add(bundle);
+ notifyFindHooks(context, single);
+ return single.size() == 0 ? null : bundle;
+ }
+
+ public BundleContextImpl getSystemBundleContext() {
+ if (systemBundle == null)
+ return null;
+ return systemBundle.context;
+ }
+
+ public PackageAdminImpl getPackageAdmin() {
+ return packageAdmin;
+ }
+
+ /**
+ * Retrieve the bundles 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 collection of {@link AbstractBundle} that match the symbolic name and version
+ */
+ public List<AbstractBundle> getBundleBySymbolicName(String symbolicName, Version version) {
+ synchronized (bundles) {
+ return bundles.getBundles(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<AbstractBundle> allBundles = bundles.getBundles();
+ int size = allBundles.size();
+ if (size == 0) {
+ return (null);
+ }
+ AbstractBundle[] bundlelist = new AbstractBundle[size];
+ allBundles.toArray(bundlelist);
+ return (bundlelist);
+ }
+ }
+
+ AbstractBundle[] getBundles(final BundleContextImpl context) {
+ List<AbstractBundle> allBundles;
+ synchronized (bundles) {
+ allBundles = new ArrayList<AbstractBundle>(bundles.getBundles());
+ }
+ notifyFindHooks(context, allBundles);
+ return allBundles.toArray(new AbstractBundle[allBundles.size()]);
+ }
+
+ private void notifyFindHooks(final BundleContextImpl context, List<AbstractBundle> allBundles) {
+ final Collection<Bundle> shrinkable = new ShrinkableCollection<Bundle>(allBundles);
+ if (System.getSecurityManager() == null) {
+ notifyFindHooksPriviledged(context, shrinkable);
+ } else {
+ AccessController.doPrivileged(new PrivilegedAction<Object>() {
+ public Object run() {
+ notifyFindHooksPriviledged(context, shrinkable);
+ return null;
+ }
+ });
+ }
+ }
+
+ void notifyFindHooksPriviledged(final BundleContextImpl context, final Collection<Bundle> allBundles) {
+ if (Debug.DEBUG_HOOKS) {
+ Debug.println("notifyBundleFindHooks(" + allBundles + ")"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ getServiceRegistry().notifyHooksPrivileged(new HookContext() {
+ public void call(Object hook, ServiceRegistration<?> hookRegistration) throws Exception {
+ if (hook instanceof FindHook) {
+ ((FindHook) hook).find(context, allBundles);
+ }
+ }
+
+ public String getHookClassName() {
+ return findHookName;
+ }
+
+ public String getHookMethodName() {
+ return "find"; //$NON-NLS-1$
+ }
+ });
+ }
+
+ private void notifyCollisionHooks(final int operationType, final Bundle target, List<AbstractBundle> collisionCandidates) {
+ final Collection<Bundle> shrinkable = new ShrinkableCollection<Bundle>(collisionCandidates);
+ if (System.getSecurityManager() == null) {
+ notifyCollisionHooksPriviledged(operationType, target, shrinkable);
+ } else {
+ AccessController.doPrivileged(new PrivilegedAction<Object>() {
+ public Object run() {
+ notifyCollisionHooksPriviledged(operationType, target, shrinkable);
+ return null;
+ }
+ });
+ }
+ }
+
+ void notifyCollisionHooksPriviledged(final int operationType, final Bundle target, final Collection<Bundle> collisionCandidates) {
+ if (Debug.DEBUG_HOOKS) {
+ Debug.println("notifyCollisionHooks(" + operationType + ", " + target + ", " + collisionCandidates + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ }
+ getServiceRegistry().notifyHooksPrivileged(new HookContext() {
+ public void call(Object hook, ServiceRegistration<?> hookRegistration) throws Exception {
+ if (hook instanceof CollisionHook) {
+ ((CollisionHook) hook).filterCollisions(operationType, target, collisionCandidates);
+ }
+ }
+
+ public String getHookClassName() {
+ return collisionHookName;
+ }
+
+ public String getHookMethodName() {
+ return "filterCollisions"; //$NON-NLS-1$
+ }
+ });
+ }
+
+ /**
+ * 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 AccessController.doPrivileged(new PrivilegedAction<AbstractBundle>() {
+ public AbstractBundle run() {
+ List<AbstractBundle> allBundles = bundles.getBundles();
+ int size = allBundles.size();
+ for (int i = 0; i < size; i++) {
+ AbstractBundle bundle = 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 symbolicName.
+ */
+ 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 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());
+ Map<String, AdminPermission> bundlePermissions = adminPermissions.get(ID);
+ if (bundlePermissions == null) {
+ bundlePermissions = new HashMap<String, AdminPermission>();
+ adminPermissions.put(ID, bundlePermissions);
+ }
+ AdminPermission result = 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(Constants.FRAMEWORK_SECURITY, FrameworkProperties.getProperty(PROP_EQUINOX_SECURITY, FrameworkProperties.getProperty("java.security.manager")));
+ 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;
+ }
+ }
+ }
+
+ void addFrameworkListener(FrameworkListener listener, BundleContextImpl context) {
+ synchronized (allFrameworkListeners) {
+ CopyOnWriteIdentityMap<FrameworkListener, FrameworkListener> listeners = allFrameworkListeners.get(context);
+ if (listeners == null) {
+ listeners = new CopyOnWriteIdentityMap<FrameworkListener, FrameworkListener>();
+ allFrameworkListeners.put(context, listeners);
+ }
+ listeners.put(listener, listener);
+ }
+ }
+
+ void removeFrameworkListener(FrameworkListener listener, BundleContextImpl context) {
+ synchronized (allFrameworkListeners) {
+ CopyOnWriteIdentityMap<FrameworkListener, FrameworkListener> listeners = allFrameworkListeners.get(context);
+ if (listeners != null)
+ listeners.remove(listener);
+ }
+ }
+
+ void removeAllListeners(BundleContextImpl context) {
+ synchronized (allBundleListeners) {
+ allBundleListeners.remove(context);
+ }
+ synchronized (allSyncBundleListeners) {
+ allSyncBundleListeners.remove(context);
+ }
+ synchronized (allFrameworkListeners) {
+ allFrameworkListeners.remove(context);
+ }
+ }
+
+ /**
+ * 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, Bundle bundle, Throwable throwable) {
+ publishFrameworkEvent(type, bundle, throwable, (FrameworkListener[]) null);
+ }
+
+ public void publishFrameworkEvent(int type, Bundle bundle, Throwable throwable, final FrameworkListener... listeners) {
+ if (bundle == null)
+ bundle = systemBundle;
+ final FrameworkEvent event = new FrameworkEvent(type, bundle, throwable);
+ if (System.getSecurityManager() == null) {
+ publishFrameworkEventPrivileged(event, listeners);
+ } else {
+ AccessController.doPrivileged(new PrivilegedAction<Object>() {
+ public Object run() {
+ publishFrameworkEventPrivileged(event, listeners);
+ return null;
+ }
+ });
+ }
+ }
+
+ public void publishFrameworkEventPrivileged(FrameworkEvent event, FrameworkListener... callerListeners) {
+ // Build the listener snapshot
+ Map<BundleContextImpl, Set<Map.Entry<FrameworkListener, FrameworkListener>>> listenerSnapshot;
+ synchronized (allFrameworkListeners) {
+ listenerSnapshot = new HashMap<BundleContextImpl, Set<Map.Entry<FrameworkListener, FrameworkListener>>>(allFrameworkListeners.size());
+ for (Map.Entry<BundleContextImpl, CopyOnWriteIdentityMap<FrameworkListener, FrameworkListener>> entry : allFrameworkListeners.entrySet()) {
+ CopyOnWriteIdentityMap<FrameworkListener, FrameworkListener> listeners = entry.getValue();
+ if (!listeners.isEmpty()) {
+ listenerSnapshot.put(entry.getKey(), listeners.entrySet());
+ }
+ }
+ }
+ // If framework event hook were defined they would be called here
+
+ // deliver the event to the snapshot
+ ListenerQueue<FrameworkListener, FrameworkListener, FrameworkEvent> queue = newListenerQueue();
+
+ // add the listeners specified by the caller first
+ if (callerListeners != null && callerListeners.length > 0) {
+ Map<FrameworkListener, FrameworkListener> listeners = new HashMap<FrameworkListener, FrameworkListener>();
+ for (FrameworkListener listener : callerListeners) {
+ if (listener != null)
+ listeners.put(listener, listener);
+ }
+ // We use the system bundle context as the dispatcher
+ if (listeners.size() > 0) {
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ EventDispatcher<FrameworkListener, FrameworkListener, FrameworkEvent> dispatcher = (EventDispatcher) getSystemBundleContext();
+ queue.queueListeners(listeners.entrySet(), dispatcher);
+ }
+ }
+
+ for (Map.Entry<BundleContextImpl, Set<Map.Entry<FrameworkListener, FrameworkListener>>> entry : listenerSnapshot.entrySet()) {
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ EventDispatcher<FrameworkListener, FrameworkListener, FrameworkEvent> dispatcher = (EventDispatcher) entry.getKey();
+ Set<Map.Entry<FrameworkListener, FrameworkListener>> listeners = entry.getValue();
+ queue.queueListeners(listeners, dispatcher);
+ }
+
+ queue.dispatchEventAsynchronous(FRAMEWORKEVENT, event);
+ }
+
+ void addBundleListener(BundleListener listener, BundleContextImpl context) {
+ if (listener instanceof SynchronousBundleListener) {
+ checkAdminPermission(context.getBundle(), AdminPermission.LISTENER);
+ synchronized (allSyncBundleListeners) {
+ CopyOnWriteIdentityMap<SynchronousBundleListener, SynchronousBundleListener> listeners = allSyncBundleListeners.get(context);
+ if (listeners == null) {
+ listeners = new CopyOnWriteIdentityMap<SynchronousBundleListener, SynchronousBundleListener>();
+ allSyncBundleListeners.put(context, listeners);
+ }
+ listeners.put((SynchronousBundleListener) listener, (SynchronousBundleListener) listener);
+ }
+ } else {
+ synchronized (allBundleListeners) {
+ CopyOnWriteIdentityMap<BundleListener, BundleListener> listeners = allBundleListeners.get(context);
+ if (listeners == null) {
+ listeners = new CopyOnWriteIdentityMap<BundleListener, BundleListener>();
+ allBundleListeners.put(context, listeners);
+ }
+ listeners.put(listener, listener);
+ }
+ }
+ }
+
+ void removeBundleListener(BundleListener listener, BundleContextImpl context) {
+ if (listener instanceof SynchronousBundleListener) {
+ checkAdminPermission(context.getBundle(), AdminPermission.LISTENER);
+ synchronized (allSyncBundleListeners) {
+ CopyOnWriteIdentityMap<SynchronousBundleListener, SynchronousBundleListener> listeners = allSyncBundleListeners.get(context);
+ if (listeners != null)
+ listeners.remove(listener);
+ }
+ } else {
+ synchronized (allBundleListeners) {
+ CopyOnWriteIdentityMap<BundleListener, BundleListener> listeners = allBundleListeners.get(context);
+ if (listeners != null)
+ listeners.remove(listener);
+ }
+ }
+ }
+
+ /**
+ * Deliver a BundleEvent to SynchronousBundleListeners (synchronous). and
+ * BundleListeners (asynchronous).
+ *
+ * @param type
+ * BundleEvent type.
+ * @param bundle
+ * Affected bundle or null.
+ */
+ public void publishBundleEvent(int type, Bundle bundle) {
+ publishBundleEvent(new BundleEvent(type, bundle));
+ }
+
+ private void publishBundleEvent(final BundleEvent event) {
+ if (System.getSecurityManager() == null) {
+ publishBundleEventPrivileged(event);
+ } else {
+ AccessController.doPrivileged(new PrivilegedAction<Object>() {
+ 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 */
+ /* Build the listener snapshot */
+ Map<BundleContextImpl, Set<Map.Entry<SynchronousBundleListener, SynchronousBundleListener>>> listenersSync;
+ synchronized (allSyncBundleListeners) {
+ listenersSync = new HashMap<BundleContextImpl, Set<Map.Entry<SynchronousBundleListener, SynchronousBundleListener>>>(allSyncBundleListeners.size());
+ for (Map.Entry<BundleContextImpl, CopyOnWriteIdentityMap<SynchronousBundleListener, SynchronousBundleListener>> entry : allSyncBundleListeners.entrySet()) {
+ CopyOnWriteIdentityMap<SynchronousBundleListener, SynchronousBundleListener> listeners = entry.getValue();
+ if (!listeners.isEmpty()) {
+ listenersSync.put(entry.getKey(), listeners.entrySet());
+ }
+ }
+ }
+ /* Collect snapshot of BundleListeners; only if the event is NOT STARTING or STOPPING or LAZY_ACTIVATION */
+ Map<BundleContextImpl, Set<Map.Entry<BundleListener, BundleListener>>> listenersAsync = null;
+ if ((event.getType() & (BundleEvent.STARTING | BundleEvent.STOPPING | BundleEvent.LAZY_ACTIVATION)) == 0) {
+ synchronized (allBundleListeners) {
+ listenersAsync = new HashMap<BundleContextImpl, Set<Map.Entry<BundleListener, BundleListener>>>(allBundleListeners.size());
+ for (Map.Entry<BundleContextImpl, CopyOnWriteIdentityMap<BundleListener, BundleListener>> entry : allBundleListeners.entrySet()) {
+ CopyOnWriteIdentityMap<BundleListener, BundleListener> listeners = entry.getValue();
+ if (!listeners.isEmpty()) {
+ listenersAsync.put(entry.getKey(), listeners.entrySet());
+ }
+ }
+ }
+ }
+
+ /* shrink the snapshot.
+ * keySet returns a Collection which cannot be added to and
+ * removals from that collection will result in removals of the
+ * entry from the snapshot.
+ */
+ Collection<BundleContext> shrinkable;
+ if (listenersAsync == null) {
+ shrinkable = asBundleContexts(listenersSync.keySet());
+ } else {
+ shrinkable = new ShrinkableCollection<BundleContext>(asBundleContexts(listenersSync.keySet()), asBundleContexts(listenersAsync.keySet()));
+ }
+ notifyEventHooksPrivileged(event, shrinkable);
+
+ /* Dispatch the event to the snapshot for sync listeners */
+ if (!listenersSync.isEmpty()) {
+ ListenerQueue<SynchronousBundleListener, SynchronousBundleListener, BundleEvent> queue = newListenerQueue();
+ for (Map.Entry<BundleContextImpl, Set<Map.Entry<SynchronousBundleListener, SynchronousBundleListener>>> entry : listenersSync.entrySet()) {
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ EventDispatcher<SynchronousBundleListener, SynchronousBundleListener, BundleEvent> dispatcher = (EventDispatcher) entry.getKey();
+ Set<Map.Entry<SynchronousBundleListener, SynchronousBundleListener>> listeners = entry.getValue();
+ queue.queueListeners(listeners, dispatcher);
+ }
+ queue.dispatchEventSynchronous(BUNDLEEVENTSYNC, event);
+ }
+
+ /* Dispatch the event to the snapshot for async listeners */
+ if ((listenersAsync != null) && !listenersAsync.isEmpty()) {
+ ListenerQueue<BundleListener, BundleListener, BundleEvent> queue = newListenerQueue();
+ for (Map.Entry<BundleContextImpl, Set<Map.Entry<BundleListener, BundleListener>>> entry : listenersAsync.entrySet()) {
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ EventDispatcher<BundleListener, BundleListener, BundleEvent> dispatcher = (EventDispatcher) entry.getKey();
+ Set<Map.Entry<BundleListener, BundleListener>> listeners = entry.getValue();
+ queue.queueListeners(listeners, dispatcher);
+ }
+ queue.dispatchEventAsynchronous(BUNDLEEVENT, event);
+ }
+ }
+
+ /**
+ * Coerce the generic type of a collection from Collection<BundleContextImpl>
+ * to Collection<BundleContext>
+ * @param c Collection to be coerced.
+ * @return c coerced to Collection<BundleContext>
+ */
+ @SuppressWarnings("unchecked")
+ public static Collection<BundleContext> asBundleContexts(Collection<? extends BundleContext> c) {
+ return (Collection<BundleContext>) c;
+ }
+
+ private void notifyEventHooksPrivileged(final BundleEvent event, final Collection<BundleContext> result) {
+ if (event.getType() == Framework.BATCHEVENT_BEGIN || event.getType() == Framework.BATCHEVENT_END)
+ return; // we don't need to send this event; it is used to book case special listeners
+ if (Debug.DEBUG_HOOKS) {
+ Debug.println("notifyBundleEventHooks(" + event.getType() + ":" + event.getBundle() + ", " + result + " )"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ }
+
+ getServiceRegistry().notifyHooksPrivileged(new HookContext() {
+ public void call(Object hook, ServiceRegistration<?> hookRegistration) throws Exception {
+ if (hook instanceof EventHook) {
+ ((EventHook) hook).event(event, result);
+ }
+ }
+
+ public String getHookClassName() {
+ return eventHookName;
+ }
+
+ public String getHookMethodName() {
+ return "event"; //$NON-NLS-1$
+ }
+ });
+ }
+
+ public <K, V, E> ListenerQueue<K, V, E> newListenerQueue() {
+ return new ListenerQueue<K, V, E>(eventManager);
+ }
+
+ 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) {
+ @SuppressWarnings("rawtypes")
+ 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) {
+ @SuppressWarnings("rawtypes")
+ 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) {
+ if (serviceClass != null && ServiceFactory.class.isAssignableFrom(serviceClass)) {
+ Bundle bundle = packageAdmin.getBundle(serviceClass);
+ if (bundle != null && bundle != registrant)
+ // in this case we have a wacky ServiceFactory that is doing something we cannot
+ // verify if it is correct. Instead of failing we allow the assignment and hope for the best
+ // bug 326918
+ return true;
+ }
+ // 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<SignedContentFactory, SignedContentFactory> currentTracker = signedContentFactory;
+ return (currentTracker == null ? null : currentTracker.getService());
+ }
+
+ ContextFinder getContextFinder() {
+ return contextFinder;
+ }
+
+ public boolean isRefreshDuplicateBSNAllowed() {
+ return allowRefreshDuplicateBSN;
+ }
+
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/InternalSystemBundle.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/InternalSystemBundle.java
new file mode 100644
index 000000000..fb352f0de
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/InternalSystemBundle.java
@@ -0,0 +1,433 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2011 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.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.security.Permission;
+import java.security.ProtectionDomain;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import org.eclipse.osgi.framework.debug.Debug;
+import org.osgi.framework.*;
+import org.osgi.framework.startlevel.FrameworkStartLevel;
+import org.osgi.framework.wiring.FrameworkWiring;
+
+/**
+ * This class subclasses Bundle to provide a system Bundle
+ * so that the framework can be represented as a bundle and
+ * can access the services provided by other bundles.
+ */
+
+public class InternalSystemBundle extends BundleHost implements org.osgi.framework.launch.Framework {
+ class SystemBundleHeaders extends Dictionary<String, String> {
+ private final Dictionary<String, String> headers;
+
+ public SystemBundleHeaders(Dictionary<String, String> headers) {
+ this.headers = headers;
+ }
+
+ public Enumeration<String> elements() {
+ return headers.elements();
+ }
+
+ public String get(Object key) {
+ if (!(key instanceof String))
+ return null;
+ if (org.osgi.framework.Constants.EXPORT_PACKAGE.equalsIgnoreCase((String) key)) {
+ return getExtra(org.osgi.framework.Constants.EXPORT_PACKAGE, org.osgi.framework.Constants.FRAMEWORK_SYSTEMPACKAGES, org.osgi.framework.Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA);
+ } else if (org.osgi.framework.Constants.PROVIDE_CAPABILITY.equalsIgnoreCase((String) key)) {
+ return getExtra(org.osgi.framework.Constants.PROVIDE_CAPABILITY, org.osgi.framework.Constants.FRAMEWORK_SYSTEMCAPABILITIES, org.osgi.framework.Constants.FRAMEWORK_SYSTEMCAPABILITIES_EXTRA);
+ }
+ return headers.get(key);
+ }
+
+ private String getExtra(String header, String systemProp, String systemExtraProp) {
+ String systemValue = FrameworkProperties.getProperty(systemProp);
+ String systemExtraValue = FrameworkProperties.getProperty(systemExtraProp);
+ if (systemValue == null)
+ systemValue = systemExtraValue;
+ else if (systemExtraValue != null && systemExtraValue.trim().length() > 0)
+ systemValue += ", " + systemExtraValue; //$NON-NLS-1$
+ String result = headers.get(header);
+ if (systemValue != null && systemValue.trim().length() > 0) {
+ if (result != null)
+ result += ", " + systemValue; //$NON-NLS-1$
+ else
+ result = systemValue;
+ }
+ return result;
+ }
+
+ public boolean isEmpty() {
+ return headers.isEmpty();
+ }
+
+ public Enumeration<String> keys() {
+ return headers.keys();
+ }
+
+ public String put(String key, String value) {
+ return headers.put(key, value);
+ }
+
+ public String remove(Object key) {
+ return headers.remove(key);
+ }
+
+ public int size() {
+ return headers.size();
+ }
+
+ }
+
+ private final FrameworkStartLevel fsl;
+ ProtectionDomain systemDomain;
+
+ /**
+ * Private SystemBundle object constructor.
+ * This method creates the SystemBundle and its BundleContext.
+ * The SystemBundle's state is set to STARTING.
+ * This method is called when the framework is constructed.
+ *
+ * @param framework Framework this bundle is running in
+ */
+ protected InternalSystemBundle(Framework framework) throws BundleException {
+ super(framework.adaptor.createSystemBundleData(), framework); // startlevel=0 means framework stopped
+ Constants.setInternalSymbolicName(bundledata.getSymbolicName());
+ state = Bundle.RESOLVED;
+ context = createContext();
+ fsl = new EquinoxStartLevel();
+ }
+
+ /**
+ * Load the bundle.
+ * This methods overrides the Bundle method and does nothing.
+ *
+ */
+ protected void load() {
+ SecurityManager sm = System.getSecurityManager();
+
+ if (sm != null) {
+ systemDomain = getClass().getProtectionDomain();
+ }
+ }
+
+ /**
+ * Reload from a new bundle.
+ * This methods overrides the Bundle method and does nothing.
+ *
+ * @param newBundle
+ * @return false
+ */
+ protected boolean reload(AbstractBundle newBundle) {
+ return (false);
+ }
+
+ /**
+ * Refresh the bundle.
+ * This methods overrides the Bundle method and does nothing.
+ *
+ */
+ protected void refresh() {
+ // do nothing
+ }
+
+ /**
+ * Unload the bundle.
+ * This methods overrides the Bundle method and does nothing.
+ *
+ * @return false
+ */
+ protected boolean unload() {
+ return (false);
+ }
+
+ /**
+ * Close the the Bundle's file.
+ * This method closes the BundleContext for the SystemBundle.
+ *
+ */
+ protected void close() {
+ context.close();
+ context = null;
+ }
+
+ /**
+ * This method loads a class from the bundle.
+ *
+ * @param name the name of the desired Class.
+ * @param checkPermission indicates whether a permission check should be done.
+ * @return the resulting Class
+ * @exception java.lang.ClassNotFoundException if the class definition was not found.
+ */
+ protected Class<?> loadClass(String name, boolean checkPermission) throws ClassNotFoundException {
+ if (checkPermission) {
+ framework.checkAdminPermission(this, AdminPermission.CLASS);
+ checkValid();
+ }
+ return (Class.forName(name));
+ }
+
+ /**
+ * Find the specified resource in this bundle.
+ * This methods returns null for the system bundle.
+ */
+ public URL getResource(String name) {
+ return (null);
+ }
+
+ /**
+ * Indicate SystemBundle is resolved.
+ *
+ */
+ protected boolean isUnresolved() {
+ return (false);
+ }
+
+ /**
+ * Start this bundle.
+ * This methods overrides the Bundle method and does nothing.
+ *
+ */
+ public void start() {
+ framework.checkAdminPermission(this, AdminPermission.EXECUTE);
+ }
+
+ public void start(int options) {
+ framework.checkAdminPermission(this, AdminPermission.EXECUTE);
+ }
+
+ /**
+ * Start the SystemBundle.
+ * This method launches the framework.
+ *
+ */
+ protected void resume() {
+ /* initialize the startlevel service */
+ framework.startLevelManager.initialize();
+
+ /* Load all installed bundles */
+ loadInstalledBundles(framework.startLevelManager.getInstalledBundles(framework.bundles, false));
+ /* Start the system bundle */
+ try {
+ framework.systemBundle.state = Bundle.STARTING;
+ framework.systemBundle.context.start();
+ framework.publishBundleEvent(BundleEvent.STARTING, framework.systemBundle);
+ } catch (BundleException be) {
+ if (Debug.DEBUG_STARTLEVEL) {
+ Debug.println("SLL: Bundle resume exception: " + be.getMessage()); //$NON-NLS-1$
+ Debug.printStackTrace(be.getNestedException() == null ? be : be.getNestedException());
+ }
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, framework.systemBundle, be);
+ throw new RuntimeException(be.getMessage(), be);
+ }
+
+ }
+
+ private void loadInstalledBundles(AbstractBundle[] installedBundles) {
+
+ for (int i = 0; i < installedBundles.length; i++) {
+ AbstractBundle bundle = installedBundles[i];
+ if (Debug.DEBUG_STARTLEVEL) {
+ Debug.println("SLL: Trying to load bundle " + bundle); //$NON-NLS-1$
+ }
+ bundle.load();
+ }
+ }
+
+ /**
+ * Stop the framework.
+ * This method spawns a thread which will call framework.shutdown.
+ *
+ */
+ public void stop() {
+ framework.checkAdminPermission(this, AdminPermission.EXECUTE);
+
+ if ((state & (ACTIVE | STARTING)) != 0) {
+ Thread shutdown = framework.secureAction.createThread(new Runnable() {
+ public void run() {
+ try {
+ framework.close();
+ } catch (Throwable t) {
+ // allow the adaptor to handle this unexpected error
+ framework.adaptor.handleRuntimeError(t);
+ }
+ }
+ }, "System Bundle Shutdown", framework.getContextFinder()); //$NON-NLS-1$
+
+ shutdown.start();
+ }
+ }
+
+ public void stop(int options) {
+ stop();
+ }
+
+ /**
+ * Stop the SystemBundle.
+ * This method shuts down the framework.
+ *
+ */
+ protected void suspend() {
+
+ framework.startLevelManager.shutdown();
+ framework.startLevelManager.cleanup();
+
+ /* clean up the exporting loaders */
+ framework.packageAdmin.cleanup();
+
+ if (Debug.DEBUG_GENERAL) {
+ Debug.println("->Framework shutdown"); //$NON-NLS-1$
+ }
+ // fire the STOPPED event here.
+ // All bundles have been unloaded, but there may be a boot strap listener that is interested (bug 182742)
+ framework.publishBundleEvent(BundleEvent.STOPPED, this);
+ }
+
+ protected void suspend(boolean lock) {
+ // do nothing
+ }
+
+ /**
+ * Update this bundle.
+ * This method spawns a thread which will call framework.shutdown
+ * followed by framework.launch.
+ *
+ */
+ public void update() {
+ framework.checkAdminPermission(this, AdminPermission.LIFECYCLE);
+
+ if ((state & (ACTIVE | STARTING)) != 0) {
+ Thread restart = framework.secureAction.createThread(new Runnable() {
+ public void run() {
+ int sl = framework.startLevelManager.getStartLevel();
+ FrameworkProperties.setProperty(Constants.PROP_OSGI_RELAUNCH, ""); //$NON-NLS-1$
+ framework.shutdown(FrameworkEvent.STOPPED_UPDATE);
+ framework.launch();
+ if (sl > 0)
+ framework.startLevelManager.doSetStartLevel(sl);
+ FrameworkProperties.clearProperty(Constants.PROP_OSGI_RELAUNCH);
+ }
+ }, "System Bundle Update", framework.getContextFinder()); //$NON-NLS-1$
+
+ restart.start();
+ }
+ }
+
+ /**
+ * Update this bundle from an InputStream.
+ * This methods overrides the Bundle method and does nothing.
+ *
+ * @param in The InputStream from which to read the new bundle.
+ */
+ public void update(InputStream in) {
+ update();
+
+ try {
+ in.close();
+ } catch (IOException e) {
+ // do nothing
+ }
+ }
+
+ /**
+ * Uninstall this bundle.
+ * This methods overrides the Bundle method and throws an exception.
+ *
+ */
+ public void uninstall() throws BundleException {
+ framework.checkAdminPermission(this, AdminPermission.LIFECYCLE);
+
+ throw new BundleException(Msg.BUNDLE_SYSTEMBUNDLE_UNINSTALL_EXCEPTION, BundleException.INVALID_OPERATION);
+ }
+
+ /**
+ * Determine whether the bundle has the requested
+ * permission.
+ * This methods overrides the Bundle method and returns <code>true</code>.
+ *
+ * @param permission The requested permission.
+ * @return <code>true</code>
+ */
+ public boolean hasPermission(Object permission) {
+ if (systemDomain != null) {
+ if (permission instanceof Permission) {
+ return systemDomain.implies((Permission) permission);
+ }
+
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * No work to do for the SystemBundle.
+ *
+ * @param refreshedBundles
+ * A list of bundles which have been refreshed as a result
+ * of a packageRefresh
+ */
+ protected void unresolvePermissions(AbstractBundle[] refreshedBundles) {
+ // Do nothing
+ }
+
+ public Dictionary<String, String> getHeaders(String localeString) {
+ return new SystemBundleHeaders(super.getHeaders(localeString));
+ }
+
+ public void init() {
+ // no op for internal representation
+ }
+
+ public FrameworkEvent waitForStop(long timeout) throws InterruptedException {
+ return framework.waitForStop(timeout);
+ }
+
+ public ClassLoader getClassLoader() {
+ return getClass().getClassLoader();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected <A> A adapt0(Class<A> adapterType) {
+ if (FrameworkStartLevel.class.equals(adapterType))
+ return (A) fsl;
+ else if (FrameworkWiring.class.equals(adapterType))
+ return (A) framework.getPackageAdmin();
+ return super.adapt0(adapterType);
+ }
+
+ class EquinoxStartLevel implements FrameworkStartLevel {
+ public void setStartLevel(int startlevel, FrameworkListener... listeners) {
+ framework.startLevelManager.setStartLevel(startlevel, InternalSystemBundle.this, listeners);
+ }
+
+ public int getInitialBundleStartLevel() {
+ return framework.startLevelManager.getInitialBundleStartLevel();
+ }
+
+ public void setInitialBundleStartLevel(int startlevel) {
+ framework.startLevelManager.setInitialBundleStartLevel(startlevel);
+ }
+
+ public Bundle getBundle() {
+ return InternalSystemBundle.this;
+ }
+
+ public int getStartLevel() {
+ return framework.startLevelManager.getStartLevel();
+ }
+ }
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/ManifestLocalization.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/ManifestLocalization.java
new file mode 100644
index 000000000..5212b1733
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/ManifestLocalization.java
@@ -0,0 +1,241 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2011 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.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.*;
+import org.eclipse.osgi.framework.util.Headers;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+
+/**
+ * This class is used by the Bundle Class to localize manifest headers.
+ */
+public class ManifestLocalization {
+ final static String DEFAULT_ROOT = FrameworkProperties.getProperty("equinox.root.locale", "en"); //$NON-NLS-1$ //$NON-NLS-2$
+ private final AbstractBundle bundle;
+ private final Dictionary<String, String> rawHeaders;
+ private Dictionary<String, String> defaultLocaleHeaders = null;
+ private final Hashtable<String, BundleResourceBundle> cache = new Hashtable<String, BundleResourceBundle>(5);
+
+ public ManifestLocalization(AbstractBundle bundle, Dictionary<String, String> rawHeaders) {
+ this.bundle = bundle;
+ this.rawHeaders = rawHeaders;
+ }
+
+ Dictionary<String, String> getHeaders(String localeString) {
+ if (localeString.length() == 0)
+ return rawHeaders;
+ boolean isDefaultLocale = localeString.equals(Locale.getDefault().toString());
+ Dictionary<String, String> currentDefault = defaultLocaleHeaders;
+ if (isDefaultLocale && currentDefault != null) {
+ return currentDefault;
+ }
+ try {
+ bundle.checkValid();
+ } catch (IllegalStateException ex) {
+ // defaultLocaleHeaders should have been initialized on uninstall
+ if (currentDefault != null)
+ return currentDefault;
+ return rawHeaders;
+ }
+ ResourceBundle localeProperties = getResourceBundle(localeString, isDefaultLocale);
+ Enumeration<String> e = this.rawHeaders.keys();
+ Headers<String, String> localeHeaders = new Headers<String, String>(this.rawHeaders.size());
+ while (e.hasMoreElements()) {
+ String key = e.nextElement();
+ String value = this.rawHeaders.get(key);
+ if (value.startsWith("%") && (value.length() > 1)) { //$NON-NLS-1$
+ String propertiesKey = value.substring(1);
+ try {
+ value = localeProperties == null ? propertiesKey : (String) localeProperties.getObject(propertiesKey);
+ } catch (MissingResourceException ex) {
+ value = propertiesKey;
+ }
+ }
+ localeHeaders.set(key, value);
+ }
+ localeHeaders.setReadOnly();
+ if (isDefaultLocale) {
+ defaultLocaleHeaders = localeHeaders;
+ }
+ return (localeHeaders);
+ }
+
+ private String[] buildNLVariants(String nl) {
+ List<String> result = new ArrayList<String>();
+ while (nl.length() > 0) {
+ result.add(nl);
+ int i = nl.lastIndexOf('_');
+ nl = (i < 0) ? "" : nl.substring(0, i); //$NON-NLS-1$
+ }
+ result.add(""); //$NON-NLS-1$
+ return result.toArray(new String[result.size()]);
+ }
+
+ /*
+ * This method find the appropriate Manifest Localization file inside the
+ * bundle. If not found, return null.
+ */
+ ResourceBundle getResourceBundle(String localeString, boolean isDefaultLocale) {
+ BundleResourceBundle resourceBundle = lookupResourceBundle(localeString);
+ if (isDefaultLocale)
+ return (ResourceBundle) resourceBundle;
+ // need to determine if this is resource bundle is an empty stem
+ // if it is then the default locale should be used
+ if (resourceBundle == null || resourceBundle.isStemEmpty())
+ return (ResourceBundle) lookupResourceBundle(Locale.getDefault().toString());
+ return (ResourceBundle) resourceBundle;
+ }
+
+ private BundleResourceBundle lookupResourceBundle(String localeString) {
+ // get the localization header as late as possible to avoid accessing the raw headers
+ // getting the first value from the raw headers forces the manifest to be parsed (bug 332039)
+ String localizationHeader = rawHeaders.get(Constants.BUNDLE_LOCALIZATION);
+ if (localizationHeader == null)
+ localizationHeader = Constants.BUNDLE_LOCALIZATION_DEFAULT_BASENAME;
+ synchronized (cache) {
+ BundleResourceBundle result = cache.get(localeString);
+ if (result != null)
+ return result.isEmpty() ? null : result;
+ String[] nlVarients = buildNLVariants(localeString);
+ BundleResourceBundle parent = null;
+ for (int i = nlVarients.length - 1; i >= 0; i--) {
+ BundleResourceBundle varientBundle = null;
+ URL varientURL = findResource(localizationHeader + (nlVarients[i].equals("") ? nlVarients[i] : '_' + nlVarients[i]) + ".properties"); //$NON-NLS-1$ //$NON-NLS-2$
+ if (varientURL == null) {
+ varientBundle = cache.get(nlVarients[i]);
+ } else {
+ InputStream resourceStream = null;
+ try {
+ resourceStream = varientURL.openStream();
+ varientBundle = new LocalizationResourceBundle(resourceStream);
+ } catch (IOException e) {
+ // ignore and continue
+ } finally {
+ if (resourceStream != null) {
+ try {
+ resourceStream.close();
+ } catch (IOException e3) {
+ //Ignore exception
+ }
+ }
+ }
+ }
+
+ if (varientBundle == null) {
+ varientBundle = new EmptyResouceBundle(nlVarients[i]);
+ }
+ if (parent != null)
+ varientBundle.setParent((ResourceBundle) parent);
+ cache.put(nlVarients[i], varientBundle);
+ parent = varientBundle;
+ }
+ result = cache.get(localeString);
+ return result.isEmpty() ? null : result;
+ }
+ }
+
+ private URL findResource(String resource) {
+ AbstractBundle searchBundle = bundle;
+ if (bundle.isResolved()) {
+ if (bundle.isFragment() && bundle.getHosts() != null) {
+ //if the bundle is a fragment, look in the host first
+ searchBundle = bundle.getHosts()[0];
+ if (searchBundle.getState() == Bundle.UNINSTALLED)
+ searchBundle = bundle;
+ }
+ return findInResolved(resource, searchBundle);
+ }
+ return searchBundle.getEntry0(resource);
+ }
+
+ private static URL findInResolved(String filePath, AbstractBundle bundleHost) {
+ URL result = bundleHost.getEntry0(filePath);
+ if (result != null)
+ return result;
+ return findInFragments(filePath, bundleHost);
+ }
+
+ private static URL findInFragments(String filePath, AbstractBundle searchBundle) {
+ BundleFragment[] fragments = searchBundle.getFragments();
+ URL fileURL = null;
+ for (int i = 0; fragments != null && i < fragments.length && fileURL == null; i++) {
+ if (fragments[i].getState() != Bundle.UNINSTALLED)
+ fileURL = fragments[i].getEntry0(filePath);
+ }
+ return fileURL;
+ }
+
+ private interface BundleResourceBundle {
+ void setParent(ResourceBundle parent);
+
+ boolean isEmpty();
+
+ boolean isStemEmpty();
+ }
+
+ private class LocalizationResourceBundle extends PropertyResourceBundle implements BundleResourceBundle {
+ public LocalizationResourceBundle(InputStream in) throws IOException {
+ super(in);
+ }
+
+ public void setParent(ResourceBundle parent) {
+ super.setParent(parent);
+ }
+
+ public boolean isEmpty() {
+ return false;
+ }
+
+ public boolean isStemEmpty() {
+ return parent == null;
+ }
+ }
+
+ class EmptyResouceBundle extends ResourceBundle implements BundleResourceBundle {
+ private final String localeString;
+
+ public EmptyResouceBundle(String locale) {
+ super();
+ this.localeString = locale;
+ }
+
+ @SuppressWarnings("unchecked")
+ public Enumeration<String> getKeys() {
+ return Collections.enumeration(Collections.EMPTY_LIST);
+ }
+
+ protected Object handleGetObject(String arg0) throws MissingResourceException {
+ return null;
+ }
+
+ public void setParent(ResourceBundle parent) {
+ super.setParent(parent);
+ }
+
+ public boolean isEmpty() {
+ if (parent == null)
+ return true;
+ return ((BundleResourceBundle) parent).isEmpty();
+ }
+
+ public boolean isStemEmpty() {
+ if (DEFAULT_ROOT.equals(localeString))
+ return false;
+ if (parent == null)
+ return true;
+ return ((BundleResourceBundle) parent).isStemEmpty();
+ }
+ }
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/PackageAdminImpl.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/PackageAdminImpl.java
new file mode 100644
index 000000000..f9e14605d
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/PackageAdminImpl.java
@@ -0,0 +1,766 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2012 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.IOException;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.*;
+import org.eclipse.osgi.framework.adaptor.*;
+import org.eclipse.osgi.framework.debug.Debug;
+import org.eclipse.osgi.internal.loader.BundleLoader;
+import org.eclipse.osgi.internal.loader.BundleLoaderProxy;
+import org.eclipse.osgi.internal.profile.Profile;
+import org.eclipse.osgi.service.resolver.*;
+import org.eclipse.osgi.service.resolver.VersionRange;
+import org.eclipse.osgi.util.NLS;
+import org.osgi.framework.*;
+import org.osgi.framework.wiring.FrameworkWiring;
+import org.osgi.service.packageadmin.*;
+
+/**
+ * PackageAdmin service for the OSGi specification.
+ *
+ * Framework service which allows bundle programmers to inspect the packages
+ * exported in the framework and eagerly update or uninstall bundles.
+ *
+ * If present, there will only be a single instance of this service
+ * registered in the framework.
+ *
+ * <p> The term <i>exported package</i> (and the corresponding interface
+ * {@link ExportedPackage}) refers to a package that has actually been
+ * exported (as opposed to one that is available for export).
+ *
+ * <p> Note that the information about exported packages returned by this
+ * service is valid only until the next time {@link #refreshPackages(org.osgi.framework.Bundle[])} is
+ * called.
+ * If an ExportedPackage becomes stale, (that is, the package it references
+ * has been updated or removed as a result of calling
+ * PackageAdmin.refreshPackages()),
+ * its getName() and getSpecificationVersion() continue to return their
+ * old values, isRemovalPending() returns true, and getExportingBundle()
+ * and getImportingBundles() return null.
+ */
+public class PackageAdminImpl implements PackageAdmin, FrameworkWiring {
+ /** framework object */
+ protected Framework framework;
+
+ /*
+ * We need to make sure that the GetBundleAction class loads early to prevent a ClassCircularityError when checking permissions.
+ * See bug 161561
+ */
+ static {
+ Class<?> c;
+ c = GetBundleAction.class;
+ c.getName(); // to prevent compiler warnings
+ }
+
+ static class GetBundleAction implements PrivilegedAction<Bundle> {
+ private Class<?> clazz;
+ private PackageAdminImpl impl;
+
+ public GetBundleAction(PackageAdminImpl impl, Class<?> clazz) {
+ this.impl = impl;
+ this.clazz = clazz;
+ }
+
+ public Bundle run() {
+ return impl.getBundlePriv(clazz);
+ }
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param framework Framework object.
+ */
+ protected PackageAdminImpl(Framework framework) {
+ this.framework = framework;
+ }
+
+ public ExportedPackage[] getExportedPackages(Bundle bundle) {
+ List<ExportedPackage> allExports = new ArrayList<ExportedPackage>();
+ FrameworkAdaptor adaptor = framework.adaptor;
+ if (adaptor == null)
+ return null;
+ ExportPackageDescription[] allDescriptions = adaptor.getState().getExportedPackages();
+ for (int i = 0; i < allDescriptions.length; i++) {
+ ExportedPackageImpl exportedPackage = createExportedPackage(allDescriptions[i]);
+ if (exportedPackage == null)
+ continue;
+ if (bundle == null || exportedPackage.getBundle() == bundle)
+ allExports.add(exportedPackage);
+ }
+ return (allExports.size() == 0 ? null : allExports.toArray(new ExportedPackage[allExports.size()]));
+ }
+
+ private ExportedPackageImpl createExportedPackage(ExportPackageDescription description) {
+ BundleDescription exporter = description.getExporter();
+ if (exporter == null || exporter.getHost() != null)
+ return null;
+ Object userObject = exporter.getUserObject();
+ if (!(userObject instanceof BundleLoaderProxy)) {
+ BundleHost bundle = (BundleHost) framework.getBundle(exporter.getBundleId());
+ if (bundle == null)
+ return null;
+ userObject = bundle.getLoaderProxy();
+ }
+ return new ExportedPackageImpl(description, (BundleLoaderProxy) userObject);
+ }
+
+ public ExportedPackage getExportedPackage(String name) {
+ ExportedPackage[] allExports = getExportedPackages((Bundle) null);
+ if (allExports == null)
+ return null;
+ ExportedPackage result = null;
+ for (int i = 0; i < allExports.length; i++) {
+ if (name.equals(allExports[i].getName())) {
+ if (result == null) {
+ result = allExports[i];
+ } else {
+ Version curVersion = result.getVersion();
+ Version newVersion = allExports[i].getVersion();
+ if (newVersion.compareTo(curVersion) >= 0)
+ result = allExports[i];
+ }
+ }
+ }
+ return result;
+ }
+
+ public ExportedPackage[] getExportedPackages(String name) {
+ ExportedPackage[] allExports = getExportedPackages((Bundle) null);
+ if (allExports == null)
+ return null;
+ List<ExportedPackage> result = new ArrayList<ExportedPackage>(1); // rare to have more than one
+ for (int i = 0; i < allExports.length; i++)
+ if (name.equals(allExports[i].getName()))
+ result.add(allExports[i]);
+ return (result.size() == 0 ? null : result.toArray(new ExportedPackage[result.size()]));
+ }
+
+ public void refreshPackages(Bundle[] input) {
+ refreshPackages(input, false, null);
+ }
+
+ public void refreshPackages(Bundle[] input, boolean synchronously, final FrameworkListener[] listeners) {
+ framework.checkAdminPermission(framework.systemBundle, AdminPermission.RESOLVE);
+
+ final AbstractBundle[] copy;
+ if (input != null) {
+ synchronized (input) {
+ copy = new AbstractBundle[input.length];
+ System.arraycopy(input, 0, copy, 0, input.length);
+ }
+ } else
+ copy = null;
+
+ if (synchronously) {
+ doResolveBundles(copy, true, listeners);
+ if (framework.isForcedRestart())
+ framework.systemBundle.stop();
+ } else {
+ Thread refresh = framework.secureAction.createThread(new Runnable() {
+ public void run() {
+ doResolveBundles(copy, true, listeners);
+ if (framework.isForcedRestart())
+ framework.shutdown(FrameworkEvent.STOPPED_BOOTCLASSPATH_MODIFIED);
+ }
+ }, "Refresh Packages", framework.getContextFinder()); //$NON-NLS-1$
+ refresh.start();
+ }
+ }
+
+ public boolean resolveBundles(Bundle[] bundles) {
+ return resolveBundles(bundles, false);
+ }
+
+ boolean resolveBundles(Bundle[] bundles, boolean propagateError) {
+ framework.checkAdminPermission(framework.systemBundle, AdminPermission.RESOLVE);
+ if (bundles == null)
+ bundles = framework.getAllBundles();
+ try {
+ doResolveBundles(bundles, false, null);
+ } catch (ResolverHookException e) {
+ if (propagateError)
+ throw e;
+ }
+ for (int i = 0; i < bundles.length; i++)
+ if (!((AbstractBundle) bundles[i]).isResolved())
+ return false;
+
+ return true;
+ }
+
+ // This method is protected to enable a work around to bug 245251
+ synchronized protected void doResolveBundles(Bundle[] bundles, boolean refreshPackages, FrameworkListener[] listeners) {
+ try {
+ if (Profile.PROFILE && Profile.STARTUP)
+ Profile.logEnter("resolve bundles"); //$NON-NLS-1$
+ framework.publishBundleEvent(Framework.BATCHEVENT_BEGIN, framework.systemBundle);
+ State systemState = framework.adaptor.getState();
+ BundleDescription[] descriptions = null;
+ int numBundles = bundles == null ? 0 : bundles.length;
+ if (!refreshPackages) {
+ List<BundleDescription> resolving = new ArrayList<BundleDescription>();
+ for (Bundle bundle : bundles) {
+ BundleDescription description = ((AbstractBundle) bundle).getBundleDescription();
+ if (((bundle.getState() & Bundle.INSTALLED) != 0) && description != null)
+ resolving.add(description);
+ }
+ descriptions = resolving.toArray(new BundleDescription[resolving.size()]);
+ } else if (numBundles > 0) {
+ // populate the resolved hosts package sources first (do this outside sync block: bug 280929)
+ populateLoaders(framework.getAllBundles());
+ synchronized (framework.bundles) {
+ // now collect the descriptions to refresh
+ List<BundleDescription> results = new ArrayList<BundleDescription>(numBundles);
+ for (int i = 0; i < numBundles; i++) {
+ BundleDescription description = ((AbstractBundle) bundles[i]).getBundleDescription();
+ if (description != null && description.getBundleId() != 0 && !results.contains(description))
+ results.add(description);
+ if (framework.isRefreshDuplicateBSNAllowed()) {
+ // add in any bundles that have the same symbolic name see bug (169593)
+ AbstractBundle[] sameNames = framework.bundles.getBundles(bundles[i].getSymbolicName());
+ if (sameNames != null && sameNames.length > 1) {
+ for (int j = 0; j < sameNames.length; j++)
+ if (sameNames[j] != bundles[i]) {
+ BundleDescription sameName = sameNames[j].getBundleDescription();
+ if (sameName != null && sameName.getBundleId() != 0 && !results.contains(sameName)) {
+ if (checkExtensionBundle(sameName))
+ results.add(sameName);
+ }
+ }
+ }
+ }
+ }
+ descriptions = (results.size() == 0 ? null : results.toArray(new BundleDescription[results.size()]));
+ }
+ }
+ StateDelta stateDelta = systemState.resolve(descriptions, refreshPackages);
+ BundleDelta[] delta = stateDelta.getChanges();
+ processDelta(delta, refreshPackages, systemState);
+ if (stateDelta.getResovlerHookException() != null)
+ throw stateDelta.getResovlerHookException();
+ } catch (Throwable t) {
+ if (Debug.DEBUG_PACKAGEADMIN) {
+ Debug.println("PackageAdminImpl.doResolveBundles: Error occured :"); //$NON-NLS-1$
+ Debug.printStackTrace(t);
+ }
+ if (t instanceof RuntimeException)
+ throw (RuntimeException) t;
+ if (t instanceof Error)
+ throw (Error) t;
+ } finally {
+ if (Profile.PROFILE && Profile.STARTUP)
+ Profile.logExit("resolve bundles"); //$NON-NLS-1$
+ if (framework.isActive()) {
+ framework.publishBundleEvent(Framework.BATCHEVENT_END, framework.systemBundle);
+ if (refreshPackages)
+ framework.publishFrameworkEvent(FrameworkEvent.PACKAGES_REFRESHED, framework.systemBundle, null, listeners);
+ }
+ }
+ }
+
+ private void populateLoaders(AbstractBundle[] bundles) {
+ // populate all the loaders with their package source information
+ // this is needed to fix bug 259903.
+ for (int i = 0; i < bundles.length; i++) {
+ // only need to do this for host bundles which are resolved
+ if (bundles[i] instanceof BundleHost && bundles[i].isResolved()) {
+ // getting the BundleLoader object populates the require-bundle sources
+ BundleLoader loader = ((BundleHost) bundles[i]).getBundleLoader();
+ if (loader != null)
+ // need to explicitly get the import package sources
+ loader.getImportedSources(null);
+ }
+ }
+ }
+
+ private boolean checkExtensionBundle(BundleDescription sameName) {
+ if (sameName.getHost() == null || !sameName.isResolved())
+ return true; // only do this extra check for resolved fragment bundles
+ // only add fragments if they are not for the system bundle
+ if (((BundleDescription) sameName.getHost().getSupplier()).getBundleId() != 0)
+ return true;
+ // never do this for resolved system bundle fragments
+ return false;
+ }
+
+ private void resumeBundles(AbstractBundle[] bundles, boolean refreshPackages, int[] previousStates) {
+ if (Debug.DEBUG_PACKAGEADMIN) {
+ Debug.println("PackageAdminImpl: restart the bundles"); //$NON-NLS-1$
+ }
+ if (bundles == null)
+ return;
+ for (int i = 0; i < bundles.length; i++) {
+ if (!bundles[i].isResolved() || (!refreshPackages && ((bundles[i].getBundleData().getStatus() & Constants.BUNDLE_LAZY_START) == 0 || bundles[i].testStateChanging(Thread.currentThread()))))
+ // skip bundles that are not resolved or
+ // if we are doing resolveBundles then skip non-lazy start bundles and bundles currently changing state by this thread
+ continue;
+ if (previousStates[i] == Bundle.ACTIVE)
+ try {
+ bundles[i].start(Bundle.START_TRANSIENT);
+ } catch (BundleException e) {
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, bundles[i], e);
+ }
+ else
+ framework.resumeBundle(bundles[i]);
+ }
+ }
+
+ private void suspendBundle(AbstractBundle bundle) {
+ // attempt to suspend the bundle or obtain the state change lock
+ // Note that this may fail but we cannot quit the
+ // refreshPackages operation because of it. (bug 84169)
+ if (bundle.isActive() && !bundle.isFragment()) {
+ framework.suspendBundle(bundle, true);
+ } else {
+ if (bundle.getStateChanging() != Thread.currentThread())
+ try {
+ bundle.beginStateChange();
+ } catch (BundleException e) {
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, bundle, e);
+ }
+ }
+
+ if (Debug.DEBUG_PACKAGEADMIN) {
+ if (bundle.stateChanging == null) {
+ Debug.println("Bundle state change lock is clear! " + bundle); //$NON-NLS-1$
+ Debug.printStackTrace(new Exception("Stack trace")); //$NON-NLS-1$
+ }
+ }
+ }
+
+ private void applyRemovalPending(BundleDelta bundleDelta) throws BundleException {
+ if ((bundleDelta.getType() & BundleDelta.REMOVAL_COMPLETE) != 0) {
+ BundleDescription bundle = bundleDelta.getBundle();
+ if (bundle.getDependents() != null && bundle.getDependents().length > 0) {
+ /* Reaching here is an internal error */
+ if (Debug.DEBUG_PACKAGEADMIN) {
+ Debug.println("Bundles still depend on removed bundle! " + bundle); //$NON-NLS-1$
+ Debug.printStackTrace(new Exception("Stack trace")); //$NON-NLS-1$
+ }
+ throw new BundleException(Msg.OSGI_INTERNAL_ERROR);
+ }
+ Object userObject = bundle.getUserObject();
+ if (userObject instanceof BundleLoaderProxy) {
+ BundleLoader.closeBundleLoader((BundleLoaderProxy) userObject);
+ try {
+ ((BundleLoaderProxy) userObject).getBundleData().close();
+ } catch (IOException e) {
+ // ignore
+ }
+ } else if (userObject instanceof BundleData) {
+ try {
+ ((BundleData) userObject).close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }
+ }
+
+ private AbstractBundle setResolved(BundleDescription bundleDescription) {
+ if (!bundleDescription.isResolved())
+ return null;
+ AbstractBundle bundle = framework.getBundle(bundleDescription.getBundleId());
+ if (bundle == null) {
+ BundleException be = new BundleException(NLS.bind(Msg.BUNDLE_NOT_IN_FRAMEWORK, bundleDescription));
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, framework.systemBundle, be);
+ return null;
+ }
+ boolean resolve = true;
+ if (bundle.isFragment()) {
+ BundleDescription[] hosts = bundleDescription.getHost().getHosts();
+ for (int i = 0; i < hosts.length; i++) {
+ BundleHost host = (BundleHost) framework.getBundle(hosts[i].getBundleId());
+ resolve = ((BundleFragment) bundle).addHost(host);
+ }
+ }
+ if (resolve)
+ bundle.resolve();
+ return bundle;
+ }
+
+ private void applyDeltas(BundleDelta[] bundleDeltas) throws BundleException {
+ Arrays.sort(bundleDeltas, new Comparator<BundleDelta>() {
+ public int compare(BundleDelta delta0, BundleDelta delta1) {
+ return (int) (delta0.getBundle().getBundleId() - delta1.getBundle().getBundleId());
+ }
+ });
+ for (int i = 0; i < bundleDeltas.length; i++) {
+ int type = bundleDeltas[i].getType();
+ if ((type & (BundleDelta.REMOVAL_PENDING | BundleDelta.REMOVAL_COMPLETE)) != 0)
+ applyRemovalPending(bundleDeltas[i]);
+ if ((type & BundleDelta.RESOLVED) != 0) {
+ AbstractBundle bundle = setResolved(bundleDeltas[i].getBundle());
+ if (bundle != null && bundle.isResolved()) {
+ NativeCodeSpecification nativeCode = bundleDeltas[i].getBundle().getNativeCodeSpecification();
+ if (nativeCode != null && nativeCode.getSupplier() != null)
+ try {
+ BundleData data = bundle.getBundleData();
+ data.installNativeCode(((NativeCodeDescription) nativeCode.getSupplier()).getNativePaths());
+ } catch (BundleException e) {
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, bundle, e);
+ }
+ }
+ }
+ }
+ }
+
+ private AbstractBundle[] processDelta(BundleDelta[] bundleDeltas, boolean refreshPackages, State systemState) {
+ List<AbstractBundle> bundlesList = new ArrayList<AbstractBundle>(bundleDeltas.length);
+ // get all the bundles that are going to be refreshed
+ for (int i = 0; i < bundleDeltas.length; i++) {
+ if ((bundleDeltas[i].getType() & BundleDelta.REMOVAL_COMPLETE) != 0 && (bundleDeltas[i].getType() & BundleDelta.REMOVED) == 0)
+ // this means the bundle was previously pending removal; do not add to list because it was already removed from before.
+ continue;
+ AbstractBundle changedBundle = framework.getBundle(bundleDeltas[i].getBundle().getBundleId());
+ if (changedBundle != null && !bundlesList.contains(changedBundle))
+ bundlesList.add(changedBundle);
+ }
+ AbstractBundle[] refresh = bundlesList.toArray(new AbstractBundle[bundlesList.size()]);
+ // first sort by id/start-level order
+ Util.sort(refresh, 0, refresh.length);
+ // then sort by dependency order
+ framework.startLevelManager.sortByDependency(refresh);
+ boolean[] previouslyResolved = new boolean[refresh.length];
+ int[] previousStates = new int[refresh.length];
+ try {
+ try {
+ if (Debug.DEBUG_PACKAGEADMIN) {
+ Debug.println("refreshPackages: Suspend each bundle and acquire its state change lock"); //$NON-NLS-1$
+ }
+ // find which bundles were previously resolved and handle extension bundles
+ boolean restart = false;
+ for (int i = refresh.length - 1; i >= 0; i--) {
+ previouslyResolved[i] = refresh[i].isResolved();
+ if (refresh[i] == framework.systemBundle)
+ restart = true;
+ else if (((refresh[i].bundledata.getType() & BundleData.TYPE_FRAMEWORK_EXTENSION) != 0) && previouslyResolved[i])
+ restart = true;
+ else if ((refresh[i].bundledata.getType() & BundleData.TYPE_BOOTCLASSPATH_EXTENSION) != 0)
+ restart = true;
+ else if ((refresh[i].bundledata.getType() & BundleData.TYPE_EXTCLASSPATH_EXTENSION) != 0 && previouslyResolved[i])
+ restart = true;
+ }
+ if (restart) {
+ FrameworkProperties.setProperty("osgi.forcedRestart", "true"); //$NON-NLS-1$ //$NON-NLS-2$
+ framework.setForcedRestart(true);
+ // do not shutdown the framework while holding the PackageAdmin lock (bug 194149)
+ return null;
+ }
+ // now suspend each bundle and grab its state change lock.
+ if (refreshPackages)
+ for (int i = refresh.length - 1; i >= 0; i--) {
+ previousStates[i] = refresh[i].getState();
+ suspendBundle(refresh[i]);
+ }
+ /*
+ * Refresh the bundles which will unexport the packages.
+ * This will move RESOLVED bundles to the INSTALLED state.
+ */
+ if (Debug.DEBUG_PACKAGEADMIN) {
+ Debug.println("refreshPackages: refresh the bundles"); //$NON-NLS-1$
+ }
+
+ synchronized (framework.bundles) {
+ for (int i = refresh.length - 1; i >= 0; i--)
+ refresh[i].refresh();
+ }
+ // send out unresolved events outside synch block (defect #80610)
+ // send out unresolved events in reverse dependency order (defect #207505)
+ for (int i = refresh.length - 1; i >= 0; i--) {
+ // send out unresolved events
+ if (previouslyResolved[i])
+ framework.publishBundleEvent(BundleEvent.UNRESOLVED, refresh[i]);
+ }
+
+ /*
+ * apply Deltas.
+ */
+ if (Debug.DEBUG_PACKAGEADMIN) {
+ Debug.println("refreshPackages: applying deltas to bundles"); //$NON-NLS-1$
+ }
+ synchronized (framework.bundles) {
+ applyDeltas(bundleDeltas);
+ }
+
+ } finally {
+ /*
+ * Release the state change locks.
+ */
+ if (Debug.DEBUG_PACKAGEADMIN) {
+ Debug.println("refreshPackages: release the state change locks"); //$NON-NLS-1$
+ }
+ if (refreshPackages)
+ for (int i = 0; i < refresh.length; i++) {
+ AbstractBundle changedBundle = refresh[i];
+ changedBundle.completeStateChange();
+ }
+ }
+ /*
+ * Take this opportunity to clean up the adaptor storage.
+ */
+ if (refreshPackages) {
+ if (Debug.DEBUG_PACKAGEADMIN)
+ Debug.println("refreshPackages: clean up adaptor storage"); //$NON-NLS-1$
+ try {
+ framework.adaptor.compactStorage();
+ } catch (IOException e) {
+ if (Debug.DEBUG_PACKAGEADMIN) {
+ Debug.println("refreshPackages exception: " + e.getMessage()); //$NON-NLS-1$
+ Debug.printStackTrace(e);
+ }
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, framework.systemBundle, new BundleException(Msg.BUNDLE_REFRESH_FAILURE, e));
+ }
+ }
+ } catch (BundleException e) {
+ if (Debug.DEBUG_PACKAGEADMIN) {
+ Debug.println("refreshPackages exception: " + e.getMessage()); //$NON-NLS-1$
+ Debug.printStackTrace(e.getNestedException() == null ? e : e.getNestedException());
+ }
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, framework.systemBundle, new BundleException(Msg.BUNDLE_REFRESH_FAILURE, e));
+ }
+
+ // send out any resolved. This must be done after the state change locks have been release.
+ if (Debug.DEBUG_PACKAGEADMIN)
+ Debug.println("refreshPackages: send out RESOLVED events"); //$NON-NLS-1$
+ for (int i = 0; i < refresh.length; i++)
+ if (refresh[i].isResolved())
+ framework.publishBundleEvent(BundleEvent.RESOLVED, refresh[i]);
+
+ // if we end up refreshing the system bundle or one of its fragments the framework will be shutdown and
+ // should be re-started. This call should return without doing further work.
+ if (!framework.isActive())
+ return refresh;
+ if (refreshPackages) {
+ // must clear permission class and condition cache
+ framework.securityAdmin.clearCaches();
+ // increment the system state timestamp if we are refreshing packages.
+ // this is needed incase we suspended a bundle from processing the delta (bug 167483)
+ if (bundleDeltas.length > 0)
+ systemState.setTimeStamp(systemState.getTimeStamp() == Long.MAX_VALUE ? 0 : systemState.getTimeStamp() + 1);
+ }
+ // always resume bundles incase we have lazy-start bundles
+ resumeBundles(refresh, refreshPackages, previousStates);
+ return refresh;
+ }
+
+ public RequiredBundle[] getRequiredBundles(String symbolicName) {
+ AbstractBundle[] bundles;
+ if (symbolicName == null)
+ bundles = framework.getAllBundles();
+ else
+ bundles = framework.getBundleBySymbolicName(symbolicName);
+ if (bundles == null || bundles.length == 0)
+ return null;
+
+ List<RequiredBundle> result = new ArrayList<RequiredBundle>(bundles.length);
+ for (int i = 0; i < bundles.length; i++) {
+ if (bundles[i].isFragment() || !bundles[i].isResolved() || bundles[i].getSymbolicName() == null)
+ continue;
+ if (bundles[i].hasPermission(new BundlePermission(bundles[i].getSymbolicName(), BundlePermission.PROVIDE)))
+ result.add(((BundleHost) bundles[i]).getLoaderProxy());
+ }
+ return result.size() == 0 ? null : result.toArray(new RequiredBundle[result.size()]);
+ }
+
+ public Bundle[] getBundles(String symbolicName, String versionRange) {
+ if (symbolicName == null) {
+ throw new IllegalArgumentException();
+ }
+ AbstractBundle bundles[] = framework.getBundleBySymbolicName(symbolicName);
+ if (bundles == null)
+ return null;
+
+ if (versionRange == null) {
+ AbstractBundle[] result = new AbstractBundle[bundles.length];
+ System.arraycopy(bundles, 0, result, 0, result.length);
+ return result;
+ }
+
+ // This code depends on the array of bundles being in descending
+ // version order.
+ List<AbstractBundle> result = new ArrayList<AbstractBundle>(bundles.length);
+ VersionRange range = new VersionRange(versionRange);
+ for (int i = 0; i < bundles.length; i++) {
+ if (range.isIncluded(bundles[i].getVersion())) {
+ result.add(bundles[i]);
+ }
+ }
+
+ if (result.size() == 0)
+ return null;
+ return result.toArray(new AbstractBundle[result.size()]);
+ }
+
+ public Bundle[] getFragments(Bundle bundle) {
+ return ((AbstractBundle) bundle).getFragments();
+ }
+
+ public Bundle[] getHosts(Bundle bundle) {
+ BundleHost[] hosts = ((AbstractBundle) bundle).getHosts();
+ if (hosts == null)
+ return null;
+ // copy the array to protect modification
+ Bundle[] result = new Bundle[hosts.length];
+ for (int i = 0; i < hosts.length; i++)
+ result[i] = hosts[i];
+ return result;
+ }
+
+ Bundle getBundlePriv(Class<?> clazz) {
+ ClassLoader cl = clazz.getClassLoader();
+ if (cl instanceof BundleClassLoader) {
+ ClassLoaderDelegate delegate = ((BundleClassLoader) cl).getDelegate();
+ if (delegate instanceof BundleLoader)
+ return ((BundleLoader) delegate).getBundle();
+ }
+ if (cl == getClass().getClassLoader())
+ return framework.systemBundle;
+ return null;
+ }
+
+ public Bundle getBundle(@SuppressWarnings("rawtypes") final Class clazz) {
+ if (System.getSecurityManager() == null)
+ return getBundlePriv(clazz);
+ return AccessController.doPrivileged(new GetBundleAction(this, clazz));
+ }
+
+ public int getBundleType(Bundle bundle) {
+ return ((AbstractBundle) bundle).isFragment() ? PackageAdmin.BUNDLE_TYPE_FRAGMENT : 0;
+ }
+
+ protected void cleanup() {
+ //This is only called when the framework is shutting down
+ }
+
+ protected void setResolvedBundles(InternalSystemBundle systemBundle) {
+ checkSystemBundle(systemBundle);
+ // Now set the actual state of the bundles from the persisted state.
+ State state = framework.adaptor.getState();
+ BundleDescription[] descriptions = state.getBundles();
+ for (int i = 0; i < descriptions.length; i++) {
+ if (descriptions[i].getBundleId() == 0)
+ setFrameworkVersion(descriptions[i]);
+ else
+ setResolved(descriptions[i]);
+ }
+ }
+
+ private void checkSystemBundle(InternalSystemBundle systemBundle) {
+ try {
+ // first check that the system bundle has not changed since last saved state.
+ State state = framework.adaptor.getState();
+ BundleDescription oldSystemBundle = state.getBundle(0);
+ boolean different = false;
+ if (oldSystemBundle == null || !systemBundle.getBundleData().getVersion().equals(oldSystemBundle.getVersion()))
+ different = true;
+ if (!different && FrameworkProperties.getProperty("osgi.dev") == null) //$NON-NLS-1$
+ return; // return quick if not in dev mode; system bundle version changes with each build
+ BundleDescription newSystemBundle = state.getFactory().createBundleDescription(state, systemBundle.getHeaders(""), systemBundle.getLocation(), 0); //$NON-NLS-1$
+ if (newSystemBundle == null)
+ throw new BundleException(Msg.OSGI_SYSTEMBUNDLE_DESCRIPTION_ERROR);
+ if (!different) {
+ // need to check to make sure the system bundle description is up to date in the state.
+ ExportPackageDescription[] oldPackages = oldSystemBundle.getExportPackages();
+ ExportPackageDescription[] newPackages = newSystemBundle.getExportPackages();
+ if (oldPackages.length >= newPackages.length) {
+ for (int i = 0; i < newPackages.length && !different; i++) {
+ if (oldPackages[i].getName().equals(newPackages[i].getName())) {
+ Object oldVersion = oldPackages[i].getVersion();
+ Object newVersion = newPackages[i].getVersion();
+ different = oldVersion == null ? newVersion != null : !oldVersion.equals(newVersion);
+ } else {
+ different = true;
+ }
+ }
+ } else {
+ different = true;
+ }
+ }
+ if (different) {
+ state.removeBundle(0);
+ state.addBundle(newSystemBundle);
+ // force resolution so packages are properly linked
+ state.resolve(false);
+ }
+ } catch (BundleException e) /* fatal error */{
+ e.printStackTrace();
+ throw new RuntimeException(NLS.bind(Msg.OSGI_SYSTEMBUNDLE_CREATE_EXCEPTION, e.getMessage()), e);
+ }
+ }
+
+ private void setFrameworkVersion(BundleDescription systemBundle) {
+ ExportPackageDescription[] packages = systemBundle.getExportPackages();
+ for (int i = 0; i < packages.length; i++)
+ if (packages[i].getName().equals(Constants.OSGI_FRAMEWORK_PACKAGE)) {
+ FrameworkProperties.setProperty(Constants.FRAMEWORK_VERSION, packages[i].getVersion().toString());
+ break;
+ }
+ FrameworkProperties.setProperty(Constants.OSGI_IMPL_VERSION_KEY, systemBundle.getVersion().toString());
+ }
+
+ public Bundle getBundle() {
+ return framework.getBundle(0);
+ }
+
+ public void refreshBundles(Collection<Bundle> bundles, FrameworkListener... listeners) {
+ refreshPackages(bundles == null ? null : bundles.toArray(new Bundle[bundles.size()]), false, listeners);
+ }
+
+ public boolean resolveBundles(Collection<Bundle> bundles) {
+ return resolveBundles(bundles == null ? null : bundles.toArray(new Bundle[bundles.size()]));
+ }
+
+ public Collection<Bundle> getRemovalPendingBundles() {
+ // TODO need to consolidate our removal pending tracking.
+ // We currently have three places this is kept (PackageAdminImpl, StateImpl and ResolverImpl)
+ // Using the state's because it has easy access to the uninstalled Bundle objects
+ BundleDescription[] removals = framework.adaptor.getState().getRemovalPending();
+ Set<Bundle> result = new HashSet<Bundle>();
+ for (int i = 0; i < removals.length; i++) {
+ Object ref = removals[i].getUserObject();
+ if (ref instanceof BundleReference)
+ result.add(((BundleReference) ref).getBundle());
+ }
+ return result;
+ }
+
+ public Collection<Bundle> getDependencyClosure(Collection<Bundle> bundles) {
+ Collection<BundleDescription> descriptions = getDescriptionClosure(bundles);
+ Set<Bundle> result = new HashSet<Bundle>();
+ for (BundleDescription description : descriptions) {
+ Object userObject = description.getUserObject();
+ if (userObject instanceof BundleReference) {
+ Bundle bundle = ((BundleReference) userObject).getBundle();
+ if (bundle != null)
+ result.add(bundle);
+ }
+ }
+ return result;
+ }
+
+ private Collection<BundleDescription> getDescriptionClosure(Collection<Bundle> bundles) {
+ State state = framework.adaptor.getState();
+ Collection<BundleDescription> descriptions = new ArrayList<BundleDescription>();
+ for (Bundle bundle : bundles) {
+ BundleDescription description = state.getBundle(bundle.getBundleId());
+ if (description != null)
+ descriptions.add(description);
+ }
+ return state.getDependencyClosure(descriptions);
+ }
+
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/ReferenceInputStream.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/ReferenceInputStream.java
new file mode 100644
index 000000000..9a1064856
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/ReferenceInputStream.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2006 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.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+/**
+ * InputStream subclass which provides a reference (via URL) to the data
+ * rather than allowing the input stream to be directly read.
+ */
+public class ReferenceInputStream extends InputStream {
+ protected URL reference;
+
+ public ReferenceInputStream(URL reference) {
+ this.reference = reference;
+ }
+
+ /* This method should not be called.
+ */
+ public int read() throws IOException {
+ throw new IOException();
+ }
+
+ public URL getReference() {
+ return reference;
+ }
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/StartLevelEvent.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/StartLevelEvent.java
new file mode 100644
index 000000000..10636bc64
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/StartLevelEvent.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * 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.util.EventObject;
+import org.osgi.framework.FrameworkListener;
+
+/**
+ * StartLevel Event for the OSGi framework.
+ *
+ * Event which signifies that a start level change has been requested for the framework or for a bundle.
+ *
+ */
+class StartLevelEvent extends EventObject {
+ private static final long serialVersionUID = 3258125839085155891L;
+ public final static int CHANGE_BUNDLE_SL = 0x00000000;
+ public final static int CHANGE_FW_SL = 0x00000001;
+
+ /**
+ * Event Type
+ */
+ private final transient int type;
+
+ /**
+ * StartLevel - value depends on event type:
+ * CHANGE_BUNDLE_SL - value is the new bundle startlevel
+ * CHANGE_FW_SL - value is the new framework startlevel
+ *
+ */
+ private final transient int newSl;
+
+ /**
+ * For a change in bundle startlevel, this is the bundle to be changed.
+ * For a change in framework startlevel, this is the bundle requesting the change.
+ */
+ private final transient AbstractBundle bundle;
+
+ /**
+ * A list of framework listeners that must be called at the end of the operation.
+ */
+ private final transient FrameworkListener[] listeners;
+
+ /**
+ * Creates a StartLevel event regarding the specified bundle.
+ *
+ * @param type The type of startlevel event (inc or dec)
+ * @param newSl the ultimate requested startlevel we are on our way to
+ * @param bundle The affected bundle, or system bundle if it is for the framework
+ */
+ public StartLevelEvent(int type, int newSl, AbstractBundle bundle, FrameworkListener... listeners) {
+ super(bundle);
+ this.type = type;
+ this.newSl = newSl;
+ this.bundle = bundle;
+ this.listeners = listeners;
+ }
+
+ public int getType() {
+ return this.type;
+ }
+
+ public int getNewSL() {
+ return this.newSl;
+ }
+
+ public AbstractBundle getBundle() {
+ return this.bundle;
+ }
+
+ public FrameworkListener[] getListeners() {
+ return listeners;
+ }
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/StartLevelManager.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/StartLevelManager.java
new file mode 100644
index 000000000..efd697c99
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/StartLevelManager.java
@@ -0,0 +1,678 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2011 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.IOException;
+import java.security.*;
+import java.util.*;
+import org.eclipse.osgi.framework.debug.Debug;
+import org.eclipse.osgi.framework.eventmgr.*;
+import org.eclipse.osgi.service.resolver.BundleDescription;
+import org.eclipse.osgi.util.NLS;
+import org.osgi.framework.*;
+import org.osgi.service.startlevel.StartLevel;
+
+/**
+ * StartLevel service implementation for the OSGi specification.
+ *
+ * Framework service which allows management of framework and bundle startlevels.
+ *
+ * This class also acts as the StartLevel service factory class, providing StartLevel objects
+ * to those requesting org.osgi.service.startlevel.StartLevel service.
+ *
+ * If present, there will only be a single instance of this service
+ * registered in the framework.
+ */
+public class StartLevelManager implements EventDispatcher<Object, Object, StartLevelEvent>, StartLevel {
+ protected static EventManager eventManager;
+ protected static Map<Object, Object> startLevelListeners;
+
+ /** The initial bundle start level for newly installed bundles */
+ protected int initialBundleStartLevel = 1;
+ // default value is 1 for compatibility mode
+
+ /** The currently active framework start level */
+ private int activeSL = 0;
+
+ /** An object used to lock the active startlevel while it is being referenced */
+ private final Object lock = new Object();
+ private final Framework framework;
+
+ /** This constructor is called by the Framework */
+ protected StartLevelManager(Framework framework) {
+ this.framework = framework;
+ }
+
+ protected void initialize() {
+ initialBundleStartLevel = framework.adaptor.getInitialBundleStartLevel();
+
+ // create an event manager and a start level listener
+ // note that we do not pass the ContextFinder because it is set each time doSetStartLevel is called
+ eventManager = new EventManager("Start Level Event Dispatcher"); //$NON-NLS-1$
+ startLevelListeners = new CopyOnWriteIdentityMap<Object, Object>();
+ startLevelListeners.put(this, this);
+ }
+
+ protected void cleanup() {
+ eventManager.close();
+ eventManager = null;
+ startLevelListeners.clear();
+ startLevelListeners = null;
+ }
+
+ /**
+ * Return the initial start level value that is assigned
+ * to a Bundle when it is first installed.
+ *
+ * @return The initial start level value for Bundles.
+ * @see #setInitialBundleStartLevel
+ */
+ public int getInitialBundleStartLevel() {
+ return initialBundleStartLevel;
+ }
+
+ /**
+ * Set the initial start level value that is assigned
+ * to a Bundle when it is first installed.
+ *
+ * <p>The initial bundle start level will be set to the specified start level. The
+ * initial bundle start level value will be persistently recorded
+ * by the Framework.
+ *
+ * <p>When a Bundle is installed via <tt>BundleContext.installBundle</tt>,
+ * it is assigned the initial bundle start level value.
+ *
+ * <p>The default initial bundle start level value is 1
+ * unless this method has been
+ * called to assign a different initial bundle
+ * start level value.
+ *
+ * <p>This method does not change the start level values of installed
+ * bundles.
+ *
+ * @param startlevel The initial start level for newly installed bundles.
+ * @throws IllegalArgumentException If the specified start level is less than or
+ * equal to zero.
+ * @throws SecurityException if the caller does not have the
+ * <tt>AdminPermission</tt> and the Java runtime environment supports
+ * permissions.
+ */
+ public void setInitialBundleStartLevel(int startlevel) {
+ framework.checkAdminPermission(framework.systemBundle, AdminPermission.STARTLEVEL);
+ if (startlevel <= 0) {
+ throw new IllegalArgumentException();
+ }
+ initialBundleStartLevel = startlevel;
+ framework.adaptor.setInitialBundleStartLevel(startlevel);
+ }
+
+ /**
+ * Return the active start level value of the Framework.
+ *
+ * If the Framework is in the process of changing the start level
+ * this method must return the active start level if this
+ * differs from the requested start level.
+ *
+ * @return The active start level value of the Framework.
+ */
+ public int getStartLevel() {
+ return activeSL;
+ }
+
+ /**
+ * Modify the active start level of the Framework.
+ *
+ * <p>The Framework will move to the requested start level. This method
+ * will return immediately to the caller and the start level
+ * change will occur asynchronously on another thread.
+ *
+ * <p>If the specified start level is
+ * higher than the active start level, the
+ * Framework will continue to increase the start level
+ * until the Framework has reached the specified start level,
+ * starting bundles at each
+ * start level which are persistently marked to be started as described in the
+ * <tt>Bundle.start</tt> method.
+ *
+ * At each intermediate start level value on the
+ * way to and including the target start level, the framework must:
+ * <ol>
+ * <li>Change the active start level to the intermediate start level value.
+ * <li>Start bundles at the intermediate start level in
+ * ascending order by <tt>Bundle.getBundleId</tt>.
+ * </ol>
+ * When this process completes after the specified start level is reached,
+ * the Framework will broadcast a Framework event of
+ * type <tt>FrameworkEvent.STARTLEVEL_CHANGED</tt> to announce it has moved to the specified
+ * start level.
+ *
+ * <p>If the specified start level is lower than the active start level, the
+ * Framework will continue to decrease the start level
+ * until the Framework has reached the specified start level
+ * stopping bundles at each
+ * start level as described in the <tt>Bundle.stop</tt> method except that their
+ * persistently recorded state indicates that they must be restarted in the
+ * future.
+ *
+ * At each intermediate start level value on the
+ * way to and including the specified start level, the framework must:
+ * <ol>
+ * <li>Stop bundles at the intermediate start level in
+ * descending order by <tt>Bundle.getBundleId</tt>.
+ * <li>Change the active start level to the intermediate start level value.
+ * </ol>
+ * When this process completes after the specified start level is reached,
+ * the Framework will broadcast a Framework event of
+ * type <tt>FrameworkEvent.STARTLEVEL_CHANGED</tt> to announce it has moved to the specified
+ * start level.
+ *
+ * <p>If the specified start level is equal to the active start level, then
+ * no bundles are started or stopped, however, the Framework must broadcast
+ * a Framework event of type <tt>FrameworkEvent.STARTLEVEL_CHANGED</tt> to
+ * announce it has finished moving to the specified start level. This
+ * event may arrive before the this method return.
+ *
+ * @param newSL The requested start level for the Framework.
+ * @throws IllegalArgumentException If the specified start level is less than or
+ * equal to zero.
+ * @throws SecurityException If the caller does not have the
+ * <tt>AdminPermission</tt> and the Java runtime environment supports
+ * permissions.
+ */
+ public void setStartLevel(int newSL, org.osgi.framework.Bundle callerBundle, FrameworkListener... listeners) {
+ if (newSL <= 0) {
+ throw new IllegalArgumentException(NLS.bind(Msg.STARTLEVEL_EXCEPTION_INVALID_REQUESTED_STARTLEVEL, "" + newSL)); //$NON-NLS-1$
+ }
+ framework.checkAdminPermission(framework.systemBundle, AdminPermission.STARTLEVEL);
+
+ if (Debug.DEBUG_STARTLEVEL) {
+ Debug.println("StartLevelImpl: setStartLevel: " + newSL + "; callerBundle = " + callerBundle.getBundleId()); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ issueEvent(new StartLevelEvent(StartLevelEvent.CHANGE_FW_SL, newSL, (AbstractBundle) callerBundle, listeners));
+
+ }
+
+ public void setStartLevel(int newSL) {
+ setStartLevel(newSL, framework.systemBundle);
+ }
+
+ /**
+ * Internal method to shut down the framework synchronously by setting the startlevel to zero
+ * and calling the StartLevelListener worker calls directly
+ *
+ * This method does not return until all bundles are stopped and the framework is shut down.
+ */
+ protected void shutdown() {
+ doSetStartLevel(0);
+ }
+
+ /**
+ * Internal worker method to set the startlevel
+ *
+ * @param newSL start level value
+ * @param callerBundle - the bundle initiating the change in start level
+ */
+ void doSetStartLevel(int newSL, FrameworkListener... listeners) {
+ synchronized (lock) {
+ ClassLoader previousTCCL = Thread.currentThread().getContextClassLoader();
+ ClassLoader contextFinder = framework.getContextFinder();
+ if (contextFinder == previousTCCL)
+ contextFinder = null;
+ else
+ Thread.currentThread().setContextClassLoader(contextFinder);
+ try {
+ int tempSL = activeSL;
+ if (newSL > tempSL) {
+ boolean launching = tempSL == 0;
+ for (int i = tempSL; i < newSL; i++) {
+ if (Debug.DEBUG_STARTLEVEL) {
+ Debug.println("sync - incrementing Startlevel from " + tempSL); //$NON-NLS-1$
+ }
+ tempSL++;
+ // Note that we must get a new list of installed bundles each time;
+ // this is because additional bundles could have been installed from the previous start-level
+ incFWSL(i + 1, getInstalledBundles(framework.bundles, false));
+ }
+ if (launching) {
+ framework.systemBundle.state = Bundle.ACTIVE;
+ framework.publishBundleEvent(BundleEvent.STARTED, framework.systemBundle);
+ framework.publishFrameworkEvent(FrameworkEvent.STARTED, framework.systemBundle, null);
+ }
+ } else {
+ AbstractBundle[] sortedBundles = getInstalledBundles(framework.bundles, true);
+ for (int i = tempSL; i > newSL; i--) {
+ if (Debug.DEBUG_STARTLEVEL) {
+ Debug.println("sync - decrementing Startlevel from " + tempSL); //$NON-NLS-1$
+ }
+ tempSL--;
+ decFWSL(i - 1, sortedBundles);
+ }
+ if (newSL == 0) {
+ // unload all bundles
+ unloadAllBundles(framework.bundles);
+ stopSystemBundle();
+ }
+ }
+ framework.publishFrameworkEvent(FrameworkEvent.STARTLEVEL_CHANGED, framework.systemBundle, null, listeners);
+ if (Debug.DEBUG_STARTLEVEL) {
+ Debug.println("StartLevelImpl: doSetStartLevel: STARTLEVEL_CHANGED event published"); //$NON-NLS-1$
+ }
+ } catch (Error e) {
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, framework.systemBundle, e, listeners);
+ throw e;
+ } catch (RuntimeException e) {
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, framework.systemBundle, e, listeners);
+ throw e;
+ } finally {
+ if (contextFinder != null)
+ Thread.currentThread().setContextClassLoader(previousTCCL);
+ }
+ }
+ }
+
+ /**
+ * This method is used within the package to save the actual active startlevel value for the framework.
+ * Externally the setStartLevel method must be used.
+ *
+ * @param newSL - the new startlevel to save
+ */
+ protected void saveActiveStartLevel(int newSL) {
+ synchronized (lock) {
+ activeSL = newSL;
+ }
+ }
+
+ /**
+ * Return the persistent state of the specified bundle.
+ *
+ * <p>This method returns the persistent state of a bundle.
+ * The persistent state of a bundle indicates whether a bundle
+ * is persistently marked to be started when it's start level is
+ * reached.
+ *
+ * @return <tt>true</tt> if the bundle is persistently marked to be started,
+ * <tt>false</tt> if the bundle is not persistently marked to be started.
+ * @exception java.lang.IllegalArgumentException If the specified bundle has been uninstalled.
+ */
+ public boolean isBundlePersistentlyStarted(org.osgi.framework.Bundle bundle) {
+ return ((AbstractBundle) bundle).isPersistentlyStarted();
+ }
+
+ public boolean isBundleActivationPolicyUsed(Bundle bundle) {
+ return ((AbstractBundle) bundle).isActivationPolicyUsed();
+ }
+
+ /**
+ * Return the assigned start level value for the specified Bundle.
+ *
+ * @param bundle The target bundle.
+ * @return The start level value of the specified Bundle.
+ * @exception java.lang.IllegalArgumentException If the specified bundle has been uninstalled.
+ */
+ public int getBundleStartLevel(org.osgi.framework.Bundle bundle) {
+ return ((AbstractBundle) bundle).getStartLevel();
+ }
+
+ /**
+ * Assign a start level value to the specified Bundle.
+ *
+ * <p>The specified bundle will be assigned the specified start level. The
+ * start level value assigned to the bundle will be persistently recorded
+ * by the Framework.
+ *
+ * If the new start level for the bundle is lower than or equal to the active start level of
+ * the Framework, the Framework will start the specified bundle as described
+ * in the <tt>Bundle.start</tt> method if the bundle is persistently marked
+ * to be started. The actual starting of this bundle must occur asynchronously.
+ *
+ * If the new start level for the bundle is higher than the active start level of
+ * the Framework, the Framework will stop the specified bundle as described
+ * in the <tt>Bundle.stop</tt> method except that the persistently recorded
+ * state for the bundle indicates that the bundle must be restarted in the
+ * future. The actual stopping of this bundle must occur asynchronously.
+ *
+ * @param bundle The target bundle.
+ * @param newSL The new start level for the specified Bundle.
+ * @throws IllegalArgumentException
+ * If the specified bundle has been uninstalled or
+ * if the specified start level is less than or equal to zero, or the specified bundle is
+ * the system bundle.
+ * @throws SecurityException if the caller does not have the
+ * <tt>AdminPermission</tt> and the Java runtime environment supports
+ * permissions.
+ */
+ public void setBundleStartLevel(org.osgi.framework.Bundle bundle, int newSL) {
+
+ String exceptionText = null;
+ if (bundle.getBundleId() == 0) { // system bundle has id=0
+ exceptionText = Msg.STARTLEVEL_CANT_CHANGE_SYSTEMBUNDLE_STARTLEVEL;
+ } else if (bundle.getState() == Bundle.UNINSTALLED) {
+ exceptionText = NLS.bind(Msg.BUNDLE_UNINSTALLED_EXCEPTION, ((AbstractBundle) bundle).getBundleData().getLocation());
+ } else if (newSL <= 0) {
+ exceptionText = NLS.bind(Msg.STARTLEVEL_EXCEPTION_INVALID_REQUESTED_STARTLEVEL, "" + newSL); //$NON-NLS-1$
+ }
+ if (exceptionText != null)
+ throw new IllegalArgumentException(exceptionText);
+ // first check the permission of the caller
+ framework.checkAdminPermission(bundle, AdminPermission.EXECUTE);
+ try {
+ // if the bundle's startlevel is not already at the requested startlevel
+ if (newSL != ((org.eclipse.osgi.framework.internal.core.AbstractBundle) bundle).getInternalStartLevel()) {
+ final AbstractBundle b = (AbstractBundle) bundle;
+ b.getBundleData().setStartLevel(newSL);
+ try {
+ AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
+ public Object run() throws Exception {
+ b.getBundleData().save();
+ return null;
+ }
+ });
+ } catch (PrivilegedActionException e) {
+ if (e.getException() instanceof IOException) {
+ throw (IOException) e.getException();
+ }
+ throw (RuntimeException) e.getException();
+ }
+ // handle starting or stopping the bundle asynchronously
+ issueEvent(new StartLevelEvent(StartLevelEvent.CHANGE_BUNDLE_SL, newSL, (AbstractBundle) bundle));
+ }
+ } catch (IOException e) {
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, bundle, e);
+ }
+
+ }
+
+ /**
+ * This method sends the StartLevelEvent to the EventManager for dispatching
+ *
+ * @param sle The event to be queued to the Event Manager
+ */
+ private void issueEvent(StartLevelEvent sle) {
+
+ /* queue to hold set of listeners */
+ ListenerQueue<Object, Object, StartLevelEvent> queue = new ListenerQueue<Object, Object, StartLevelEvent>(eventManager);
+
+ /* add set of StartLevelListeners to queue */
+ queue.queueListeners(startLevelListeners.entrySet(), this);
+
+ /* dispatch event to set of listeners */
+ queue.dispatchEventAsynchronous(sle.getType(), sle);
+ }
+
+ /**
+ * This method is the call back that is called once for each listener.
+ * This method must cast the EventListener object to the appropriate listener
+ * class for the event type and call the appropriate listener method.
+ *
+ * @param listener This listener must be cast to the appropriate listener
+ * class for the events created by this source and the appropriate listener method
+ * must then be called.
+ * @param listenerObject This is the optional object that was passed to
+ * EventListeners.addListener when the listener was added to the EventListeners.
+ * @param eventAction This value was passed to the ListenerQueue object via one of its
+ * dispatchEvent* method calls. It can provide information (such
+ * as which listener method to call) so that this method
+ * can complete the delivery of the event to the listener.
+ * @param event This object was passed to the ListenerQueue object via one of its
+ * dispatchEvent* method calls. This object was created by the event source and
+ * is passed to this method. It should contain all the necessary information (such
+ * as what event object to pass) so that this method
+ * can complete the delivery of the event to the listener.
+ */
+ public void dispatchEvent(Object listener, Object listenerObject, int eventAction, StartLevelEvent event) {
+ try {
+ switch (eventAction) {
+ case StartLevelEvent.CHANGE_BUNDLE_SL :
+ setBundleSL(event);
+ break;
+ case StartLevelEvent.CHANGE_FW_SL :
+ doSetStartLevel(event.getNewSL(), event.getListeners());
+ break;
+ }
+ } catch (Throwable t) {
+ // allow the adaptor to handle this unexpected error
+ framework.adaptor.handleRuntimeError(t);
+ }
+ }
+
+ /**
+ * Increment the active startlevel by one
+ */
+ protected void incFWSL(int incToSL, AbstractBundle[] launchBundles) {
+ if (Debug.DEBUG_STARTLEVEL) {
+ Debug.println("SLL: incFWSL: saving activeSL of " + incToSL); //$NON-NLS-1$
+ }
+ // save the startlevel
+ saveActiveStartLevel(incToSL);
+ // resume all bundles at the startlevel
+ resumeBundles(launchBundles, incToSL);
+ }
+
+ /**
+ * Build an array of all installed bundles to be launch.
+ * The returned array is sorted by increasing startlevel/id order.
+ * @param bundles - the bundles installed in the framework
+ * @return A sorted array of bundles
+ */
+ AbstractBundle[] getInstalledBundles(BundleRepository bundles, boolean sortByDependency) {
+
+ /* make copy of bundles vector in case it is modified during launch */
+ AbstractBundle[] installedBundles;
+
+ synchronized (bundles) {
+ List<AbstractBundle> allBundles = bundles.getBundles();
+ installedBundles = new AbstractBundle[allBundles.size()];
+ allBundles.toArray(installedBundles);
+
+ /* sort bundle array in ascending startlevel / bundle id order
+ * so that bundles are started in ascending order.
+ */
+ Util.sort(installedBundles, 0, installedBundles.length);
+ if (sortByDependency)
+ sortByDependency(installedBundles);
+ }
+ return installedBundles;
+ }
+
+ void sortByDependency(AbstractBundle[] bundles) {
+ synchronized (framework.bundles) {
+ if (bundles.length <= 1)
+ return;
+ int currentSL = bundles[0].getInternalStartLevel();
+ int currentSLindex = 0;
+ boolean lazy = false;
+ for (int i = 0; i < bundles.length; i++) {
+ if (currentSL != bundles[i].getInternalStartLevel()) {
+ if (lazy)
+ sortByDependencies(bundles, currentSLindex, i);
+ currentSL = bundles[i].getInternalStartLevel();
+ currentSLindex = i;
+ lazy = false;
+ }
+ lazy |= (bundles[i].getBundleData().getStatus() & Constants.BUNDLE_LAZY_START) != 0;
+ }
+ // sort the last set of bundles
+ if (lazy)
+ sortByDependencies(bundles, currentSLindex, bundles.length);
+ }
+ }
+
+ private void sortByDependencies(AbstractBundle[] bundles, int start, int end) {
+ if (end - start <= 1)
+ return;
+ List<BundleDescription> descList = new ArrayList<BundleDescription>(end - start);
+ List<AbstractBundle> missingDescs = new ArrayList<AbstractBundle>(0);
+ for (int i = start; i < end; i++) {
+ BundleDescription desc = bundles[i].getBundleDescription();
+ if (desc != null)
+ descList.add(desc);
+ else
+ missingDescs.add(bundles[i]);
+ }
+ if (descList.size() <= 1)
+ return;
+ BundleDescription[] descriptions = descList.toArray(new BundleDescription[descList.size()]);
+ framework.adaptor.getPlatformAdmin().getStateHelper().sortBundles(descriptions);
+ for (int i = start; i < descriptions.length + start; i++)
+ bundles[i] = framework.bundles.getBundle(descriptions[i - start].getBundleId());
+ if (missingDescs.size() > 0) {
+ Iterator<AbstractBundle> missing = missingDescs.iterator();
+ for (int i = start + descriptions.length; i < end && missing.hasNext(); i++)
+ bundles[i] = missing.next();
+ }
+ }
+
+ /**
+ * Resume all bundles in the launch list at the specified start-level
+ * @param launch a list of Bundle Objects to launch
+ * @param currentSL the current start-level that the bundles must meet to be resumed
+ */
+ private void resumeBundles(AbstractBundle[] launch, int currentSL) {
+ // Resume all bundles that were previously started and whose startlevel is <= the active startlevel
+ // first resume the lazy activated bundles
+ resumeBundles(launch, true, currentSL);
+ // now resume all non lazy bundles
+ resumeBundles(launch, false, currentSL);
+ }
+
+ private void resumeBundles(AbstractBundle[] launch, boolean lazyOnly, int currentSL) {
+ for (int i = 0; i < launch.length && !framework.isForcedRestart(); i++) {
+ int bsl = launch[i].getInternalStartLevel();
+ if (bsl < currentSL) {
+ // skip bundles who should have already been started
+ continue;
+ } else if (bsl == currentSL) {
+ if (Debug.DEBUG_STARTLEVEL) {
+ Debug.println("SLL: Active sl = " + currentSL + "; Bundle " + launch[i].getBundleId() + " sl = " + bsl); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ boolean isLazyStart = launch[i].isLazyStart();
+ if (lazyOnly ? isLazyStart : !isLazyStart)
+ framework.resumeBundle(launch[i]);
+ } else {
+ // can stop resuming bundles since any remaining bundles have a greater startlevel than the framework active startlevel
+ break;
+ }
+ }
+ }
+
+ /**
+ * Decrement the active startlevel by one
+ * @param decToSL - the startlevel value to set the framework to
+ */
+ protected void decFWSL(int decToSL, AbstractBundle[] shutdown) {
+ if (Debug.DEBUG_STARTLEVEL) {
+ Debug.println("SLL: decFWSL: saving activeSL of " + decToSL); //$NON-NLS-1$
+ }
+
+ saveActiveStartLevel(decToSL);
+
+ // just decrementing the active startlevel - framework is not shutting down
+ // Do not check framework.isForcedRestart here because we want to stop the active bundles regardless.
+ for (int i = shutdown.length - 1; i >= 0; i--) {
+ int bsl = shutdown[i].getInternalStartLevel();
+ if (bsl > decToSL + 1)
+ // skip bundles who should have already been stopped
+ continue;
+ else if (bsl <= decToSL)
+ // stopped all bundles we are going to for this start level
+ break;
+ else if (shutdown[i].isActive()) {
+ // if bundle is active or starting, then stop the bundle
+ if (Debug.DEBUG_STARTLEVEL)
+ Debug.println("SLL: stopping bundle " + shutdown[i].getBundleId()); //$NON-NLS-1$
+ framework.suspendBundle(shutdown[i], false);
+ }
+ }
+ }
+
+ /**
+ * Stops the system bundle
+ */
+ private void stopSystemBundle() {
+ try {
+ framework.systemBundle.context.stop();
+ } catch (BundleException sbe) {
+ if (Debug.DEBUG_STARTLEVEL) {
+ Debug.println("SLL: Bundle suspend exception: " + sbe.getMessage()); //$NON-NLS-1$
+ Debug.printStackTrace(sbe.getNestedException() == null ? sbe : sbe.getNestedException());
+ }
+
+ framework.publishFrameworkEvent(FrameworkEvent.ERROR, framework.systemBundle, sbe);
+ }
+
+ framework.systemBundle.state = Bundle.RESOLVED;
+ framework.publishBundleEvent(BundleEvent.STOPPED, framework.systemBundle);
+ }
+
+ /**
+ * Unloads all bundles in the vector passed in.
+ * @param bundles list of Bundle objects to be unloaded
+ */
+ private void unloadAllBundles(BundleRepository bundles) {
+ synchronized (bundles) {
+ /* unload all installed bundles */
+ List<AbstractBundle> allBundles = bundles.getBundles();
+ int size = allBundles.size();
+
+ for (int i = 0; i < size; i++) {
+ AbstractBundle bundle = allBundles.get(i);
+
+ if (Debug.DEBUG_STARTLEVEL) {
+ Debug.println("SLL: Trying to unload bundle " + bundle); //$NON-NLS-1$
+ }
+ bundle.refresh();
+ try {
+ // make sure we close all the bundle data objects
+ bundle.getBundleData().close();
+ } catch (IOException e) {
+ // ignore, we are shutting down anyway
+ }
+ }
+ }
+ }
+
+ /**
+ * Set the bundle's startlevel to the new value
+ * This may cause the bundle to start or stop based on the active framework startlevel
+ * @param startLevelEvent - the event requesting change in bundle startlevel
+ */
+ protected void setBundleSL(StartLevelEvent startLevelEvent) {
+ synchronized (lock) {
+ int currentSL = getStartLevel();
+ int newSL = startLevelEvent.getNewSL();
+ AbstractBundle bundle = startLevelEvent.getBundle();
+
+ if (Debug.DEBUG_STARTLEVEL) {
+ Debug.print("SLL: bundle active=" + bundle.isActive()); //$NON-NLS-1$
+ Debug.print("; newSL = " + newSL); //$NON-NLS-1$
+ Debug.println("; activeSL = " + currentSL); //$NON-NLS-1$
+ }
+
+ if (bundle.isActive() && (newSL > currentSL)) {
+ if (Debug.DEBUG_STARTLEVEL) {
+ Debug.println("SLL: stopping bundle " + bundle.getBundleId()); //$NON-NLS-1$
+ }
+ framework.suspendBundle(bundle, false);
+ } else {
+ if (!bundle.isActive() && (newSL <= currentSL)) {
+ if (Debug.DEBUG_STARTLEVEL) {
+ Debug.println("SLL: starting bundle " + bundle.getBundleId()); //$NON-NLS-1$
+ }
+ framework.resumeBundle(bundle);
+ }
+ }
+ if (Debug.DEBUG_STARTLEVEL) {
+ Debug.println("SLL: Bundle Startlevel set to " + newSL); //$NON-NLS-1$
+ }
+ }
+ }
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/SystemBundleActivator.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/SystemBundleActivator.java
new file mode 100644
index 000000000..f3233207d
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/SystemBundleActivator.java
@@ -0,0 +1,110 @@
+/*******************************************************************************
+ * 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.util.Dictionary;
+import java.util.Hashtable;
+import org.eclipse.osgi.framework.debug.FrameworkDebugOptions;
+import org.eclipse.osgi.internal.resolver.StateImpl;
+import org.eclipse.osgi.service.resolver.State;
+import org.osgi.framework.*;
+import org.osgi.service.condpermadmin.ConditionalPermissionAdmin;
+
+/**
+ * This class activates the System Bundle.
+ */
+
+public class SystemBundleActivator implements BundleActivator {
+ private BundleContext context;
+ private InternalSystemBundle bundle;
+ private Framework framework;
+ private ServiceRegistration<?> packageAdmin;
+ private ServiceRegistration<?> securityAdmin;
+ private ServiceRegistration<?> startLevel;
+ private ServiceRegistration<?> debugOptions;
+ private ServiceRegistration<?> contextFinder;
+
+ public void start(BundleContext bc) throws Exception {
+ this.context = bc;
+ bundle = (InternalSystemBundle) bc.getBundle();
+ framework = bundle.framework;
+
+ if (framework.packageAdmin != null)
+ packageAdmin = register(new String[] {Constants.OSGI_PACKAGEADMIN_NAME}, framework.packageAdmin, null);
+ if (framework.securityAdmin != null)
+ securityAdmin = register(new String[] {Constants.OSGI_PERMISSIONADMIN_NAME, ConditionalPermissionAdmin.class.getName()}, framework.securityAdmin, null);
+ if (framework.startLevelManager != null)
+ startLevel = register(new String[] {Constants.OSGI_STARTLEVEL_NAME}, framework.startLevelManager, null);
+ FrameworkDebugOptions dbgOptions = null;
+ if ((dbgOptions = FrameworkDebugOptions.getDefault()) != null) {
+ dbgOptions.start(bc);
+ debugOptions = register(new String[] {org.eclipse.osgi.service.debug.DebugOptions.class.getName()}, dbgOptions, null);
+ }
+ ClassLoader tccl = framework.getContextFinder();
+ if (tccl != null) {
+ Dictionary<String, Object> props = new Hashtable<String, Object>(7);
+ props.put("equinox.classloader.type", "contextClassLoader"); //$NON-NLS-1$ //$NON-NLS-2$
+ contextFinder = register(new String[] {ClassLoader.class.getName()}, tccl, props);
+ }
+
+ // Always call the adaptor.frameworkStart() at the end of this method.
+ framework.adaptor.frameworkStart(bc);
+ State state = framework.adaptor.getState();
+ if (state instanceof StateImpl)
+ ((StateImpl) state).setResolverHookFactory(new CoreResolverHookFactory((BundleContextImpl) context, framework.getServiceRegistry()));
+ // attempt to resolve all bundles
+ // this is done after the adaptor.frameworkStart has been called
+ // this should be the first time the resolver State is accessed
+ framework.packageAdmin.setResolvedBundles(bundle);
+ // reinitialize the system bundles localization to take into account system bundle fragments
+ framework.systemBundle.manifestLocalization = null;
+ }
+
+ public void stop(BundleContext bc) throws Exception {
+ // Always call the adaptor.frameworkStop() at the begining of this method.
+ framework.adaptor.frameworkStop(bc);
+
+ if (packageAdmin != null)
+ packageAdmin.unregister();
+ if (securityAdmin != null)
+ securityAdmin.unregister();
+ if (startLevel != null)
+ startLevel.unregister();
+ if (debugOptions != null) {
+ FrameworkDebugOptions dbgOptions = FrameworkDebugOptions.getDefault();
+ if (dbgOptions != null)
+ dbgOptions.stop(bc);
+ debugOptions.unregister();
+ }
+ if (contextFinder != null)
+ contextFinder.unregister();
+
+ framework = null;
+ bundle = null;
+ this.context = null;
+ }
+
+ /**
+ * Register a service object.
+ *
+ */
+ private ServiceRegistration<?> register(String[] names, Object service, Dictionary<String, Object> properties) {
+ if (properties == null)
+ properties = new Hashtable<String, Object>(7);
+ Dictionary<String, String> headers = bundle.getHeaders();
+ properties.put(Constants.SERVICE_VENDOR, headers.get(Constants.BUNDLE_VENDOR));
+ properties.put(Constants.SERVICE_RANKING, new Integer(Integer.MAX_VALUE));
+ properties.put(Constants.SERVICE_PID, bundle.getBundleId() + "." + service.getClass().getName()); //$NON-NLS-1$
+ return context.registerService(names, service, properties);
+ }
+
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/UniversalUniqueIdentifier.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/UniversalUniqueIdentifier.java
new file mode 100644
index 000000000..abeab38b1
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/UniversalUniqueIdentifier.java
@@ -0,0 +1,270 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2011 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.math.BigInteger;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.security.SecureRandom;
+import java.util.GregorianCalendar;
+import java.util.Random;
+
+public class UniversalUniqueIdentifier {
+
+ /* INSTANCE FIELDS =============================================== */
+
+ private byte[] fBits = new byte[BYTES_SIZE];
+
+ /* NON-FINAL PRIVATE STATIC FIELDS =============================== */
+
+ private volatile static BigInteger fgPreviousClockValue;
+ private volatile static int fgClockAdjustment = 0;
+ private volatile static int fgClockSequence = -1;
+ private final static byte[] nodeAddress;
+
+ static {
+ nodeAddress = computeNodeAddress();
+ }
+
+ /* PRIVATE STATIC FINAL FIELDS =================================== */
+
+ private final static Random fgRandomNumberGenerator = new Random();
+
+ /* PUBLIC STATIC FINAL FIELDS ==================================== */
+
+ public static final int BYTES_SIZE = 16;
+ public static final byte[] UNDEFINED_UUID_BYTES = new byte[16];
+ public static final int MAX_CLOCK_SEQUENCE = 0x4000;
+ public static final int MAX_CLOCK_ADJUSTMENT = 0x7FFF;
+ public static final int TIME_FIELD_START = 0;
+ public static final int TIME_FIELD_STOP = 6;
+ public static final int TIME_HIGH_AND_VERSION = 7;
+ public static final int CLOCK_SEQUENCE_HIGH_AND_RESERVED = 8;
+ public static final int CLOCK_SEQUENCE_LOW = 9;
+ public static final int NODE_ADDRESS_START = 10;
+ public static final int NODE_ADDRESS_BYTE_SIZE = 6;
+
+ public static final int BYTE_MASK = 0xFF;
+
+ public static final int HIGH_NIBBLE_MASK = 0xF0;
+
+ public static final int LOW_NIBBLE_MASK = 0x0F;
+
+ public static final int SHIFT_NIBBLE = 4;
+
+ public static final int ShiftByte = 8;
+
+ /**
+ UniversalUniqueIdentifier default constructor returns a
+ new instance that has been initialized to a unique value.
+ */
+ public UniversalUniqueIdentifier() {
+ this.setVersion(1);
+ this.setVariant(1);
+ this.setTimeValues();
+ this.setNode(getNodeAddress());
+ }
+
+ private void appendByteString(StringBuffer buffer, byte value) {
+ String hexString;
+
+ if (value < 0)
+ hexString = Integer.toHexString(256 + value);
+ else
+ hexString = Integer.toHexString(value);
+ if (hexString.length() == 1)
+ buffer.append("0"); //$NON-NLS-1$
+ buffer.append(hexString);
+ }
+
+ private static BigInteger clockValueNow() {
+ GregorianCalendar now = new GregorianCalendar();
+ BigInteger nowMillis = BigInteger.valueOf(now.getTime().getTime());
+ BigInteger baseMillis = BigInteger.valueOf(now.getGregorianChange().getTime());
+
+ return (nowMillis.subtract(baseMillis).multiply(BigInteger.valueOf(10000L)));
+ }
+
+ /**
+ * Answers the node address attempting to mask the IP
+ * address of this machine.
+ *
+ * @return byte[] the node address
+ */
+ private static byte[] computeNodeAddress() {
+
+ byte[] address = new byte[NODE_ADDRESS_BYTE_SIZE];
+
+ // Seed the secure randomizer with some oft-varying inputs
+ int thread = Thread.currentThread().hashCode();
+ long time = System.currentTimeMillis();
+ int objectId = System.identityHashCode(new String());
+ ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
+ DataOutputStream out = new DataOutputStream(byteOut);
+ byte[] ipAddress = getIPAddress();
+
+ try {
+ if (ipAddress != null)
+ out.write(ipAddress);
+ out.write(thread);
+ out.writeLong(time);
+ out.write(objectId);
+ out.close();
+ } catch (IOException exc) {
+ //ignore the failure, we're just trying to come up with a random seed
+ }
+ byte[] rand = byteOut.toByteArray();
+
+ SecureRandom randomizer = new SecureRandom(rand);
+ randomizer.nextBytes(address);
+
+ // set the MSB of the first octet to 1 to distinguish from IEEE node addresses
+ address[0] = (byte) (address[0] | (byte) 0x80);
+
+ return address;
+ }
+
+ /**
+ Answers the IP address of the local machine using the
+ Java API class <code>InetAddress</code>.
+
+ @return byte[] the network address in network order
+ @see java.net.InetAddress#getLocalHost()
+ @see java.net.InetAddress#getAddress()
+ */
+ private static byte[] getIPAddress() {
+ try {
+ return InetAddress.getLocalHost().getAddress();
+ } catch (UnknownHostException e) {
+ //valid for this to be thrown be a machine with no IP connection
+ //It is VERY important NOT to throw this exception
+ return null;
+ } catch (ArrayIndexOutOfBoundsException e) {
+ // there appears to be a bug in the VM if there is an alias
+ // see bug 354820. As above it is important not to throw this
+ return null;
+ }
+ }
+
+ private static byte[] getNodeAddress() {
+ return nodeAddress;
+ }
+
+ private static int nextClockSequence() {
+
+ if (fgClockSequence == -1)
+ fgClockSequence = (int) (fgRandomNumberGenerator.nextDouble() * MAX_CLOCK_SEQUENCE);
+
+ fgClockSequence = (fgClockSequence + 1) % MAX_CLOCK_SEQUENCE;
+
+ return fgClockSequence;
+ }
+
+ private static BigInteger nextTimestamp() {
+
+ BigInteger timestamp = clockValueNow();
+ int timestampComparison;
+
+ timestampComparison = timestamp.compareTo(fgPreviousClockValue);
+
+ if (timestampComparison == 0) {
+ if (fgClockAdjustment == MAX_CLOCK_ADJUSTMENT) {
+ while (timestamp.compareTo(fgPreviousClockValue) == 0)
+ timestamp = clockValueNow();
+ timestamp = nextTimestamp();
+ } else
+ fgClockAdjustment++;
+ } else {
+ fgClockAdjustment = 0;
+
+ if (timestampComparison < 0)
+ nextClockSequence();
+ }
+
+ return timestamp;
+ }
+
+ private void setClockSequence(int clockSeq) {
+ int clockSeqHigh = (clockSeq >>> ShiftByte) & LOW_NIBBLE_MASK;
+ int reserved = fBits[CLOCK_SEQUENCE_HIGH_AND_RESERVED] & HIGH_NIBBLE_MASK;
+
+ fBits[CLOCK_SEQUENCE_HIGH_AND_RESERVED] = (byte) (reserved | clockSeqHigh);
+ fBits[CLOCK_SEQUENCE_LOW] = (byte) (clockSeq & BYTE_MASK);
+ }
+
+ private void setNode(byte[] bytes) {
+
+ for (int index = 0; index < NODE_ADDRESS_BYTE_SIZE; index++)
+ fBits[index + NODE_ADDRESS_START] = bytes[index];
+ }
+
+ private void setTimestamp(BigInteger timestamp) {
+ BigInteger value = timestamp;
+ BigInteger bigByte = BigInteger.valueOf(256L);
+ BigInteger[] results;
+ int version;
+ int timeHigh;
+
+ for (int index = TIME_FIELD_START; index < TIME_FIELD_STOP; index++) {
+ results = value.divideAndRemainder(bigByte);
+ value = results[0];
+ fBits[index] = (byte) results[1].intValue();
+ }
+ version = fBits[TIME_HIGH_AND_VERSION] & HIGH_NIBBLE_MASK;
+ timeHigh = value.intValue() & LOW_NIBBLE_MASK;
+ fBits[TIME_HIGH_AND_VERSION] = (byte) (timeHigh | version);
+ }
+
+ private synchronized void setTimeValues() {
+ this.setTimestamp(timestamp());
+ this.setClockSequence(fgClockSequence);
+ }
+
+ private int setVariant(int variantIdentifier) {
+ int clockSeqHigh = fBits[CLOCK_SEQUENCE_HIGH_AND_RESERVED] & LOW_NIBBLE_MASK;
+ int variant = variantIdentifier & LOW_NIBBLE_MASK;
+
+ fBits[CLOCK_SEQUENCE_HIGH_AND_RESERVED] = (byte) ((variant << SHIFT_NIBBLE) | clockSeqHigh);
+ return (variant);
+ }
+
+ private void setVersion(int versionIdentifier) {
+ int timeHigh = fBits[TIME_HIGH_AND_VERSION] & LOW_NIBBLE_MASK;
+ int version = versionIdentifier & LOW_NIBBLE_MASK;
+
+ fBits[TIME_HIGH_AND_VERSION] = (byte) (timeHigh | (version << SHIFT_NIBBLE));
+ }
+
+ private static BigInteger timestamp() {
+ BigInteger timestamp;
+
+ if (fgPreviousClockValue == null) {
+ fgClockAdjustment = 0;
+ nextClockSequence();
+ timestamp = clockValueNow();
+ } else
+ timestamp = nextTimestamp();
+
+ fgPreviousClockValue = timestamp;
+ return fgClockAdjustment == 0 ? timestamp : timestamp.add(BigInteger.valueOf(fgClockAdjustment));
+ }
+
+ public String toString() {
+ StringBuffer buffer = new StringBuffer();
+ for (int i = 0; i < fBits.length; i++) {
+ if (i == 4 || i == 6 || i == 8 || i == 10)
+ buffer.append('-');
+ appendByteString(buffer, fBits[i]);
+ }
+ return buffer.toString();
+ }
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/Util.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/Util.java
new file mode 100644
index 000000000..63ccfd2d7
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/Util.java
@@ -0,0 +1,208 @@
+/*******************************************************************************
+ * 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;
+
+/**
+ * This class contains utility functions.
+ */
+public class Util {
+ /**
+ * Performs a quicksort of the given objects
+ * by their string representation in ascending order.
+ * <p>
+ *
+ * @param array The array of objects to sort
+ */
+ public static void sortByString(Object[] array) {
+ qSortByString(array, 0, array.length - 1);
+ }
+
+ /**
+ * Sorts the array of objects by their string representation
+ * in ascending order.
+ * <p>
+ * This is a version of C.A.R Hoare's Quick Sort algorithm.
+ *
+ * @param array the array of objects to sort
+ * @param start the start index to begin sorting
+ * @param stop the end index to stop sorting
+ *
+ * @exception ArrayIndexOutOfBoundsException when <code>start < 0</code>
+ * or <code>end >= array.length</code>
+ */
+ public static void qSortByString(Object[] array, int start, int stop) {
+ if (start >= stop)
+ return;
+
+ int left = start; // left index
+ int right = stop; // right index
+ Object temp; // for swapping
+
+ // arbitrarily establish a partition element as the midpoint of the array
+ String mid = String.valueOf(array[(start + stop) / 2]);
+
+ // loop through the array until indices cross
+ while (left <= right) {
+ // find the first element that is smaller than the partition element from the left
+ while ((left < stop) && (String.valueOf(array[left]).compareTo(mid) < 0)) {
+ ++left;
+ }
+ // find an element that is smaller than the partition element from the right
+ while ((right > start) && (mid.compareTo(String.valueOf(array[right])) < 0)) {
+ --right;
+ }
+ // if the indices have not crossed, swap
+ if (left <= right) {
+ temp = array[left];
+ array[left] = array[right];
+ array[right] = temp;
+ ++left;
+ --right;
+ }
+ }
+ // sort the left partition, if the right index has not reached the left side of array
+ if (start < right) {
+ qSortByString(array, start, right);
+ }
+ // sort the right partition, if the left index has not reached the right side of array
+ if (left < stop) {
+ qSortByString(array, left, stop);
+ }
+ }
+
+ /**
+ * Sorts the specified range in the array in ascending order.
+ *
+ * @param array the Object array to be sorted
+ * @param start the start index to sort
+ * @param end the last + 1 index to sort
+ *
+ * @exception ClassCastException when an element in the array does not
+ * implement Comparable or elements cannot be compared to each other
+ * @exception IllegalArgumentException when <code>start > end</code>
+ * @exception ArrayIndexOutOfBoundsException when <code>start < 0</code>
+ * or <code>end > array.size()</code>
+ */
+ @SuppressWarnings("unchecked")
+ public static void sort(Object[] array, int start, int end) {
+ int middle = (start + end) / 2;
+ if (start + 1 < middle)
+ sort(array, start, middle);
+ if (middle + 1 < end)
+ sort(array, middle, end);
+ if (start + 1 >= end)
+ return; // this case can only happen when this method is called by the user
+ if (((Comparable<Object>) array[middle - 1]).compareTo(array[middle]) <= 0)
+ return;
+ if (start + 2 == end) {
+ Object temp = array[start];
+ array[start] = array[middle];
+ array[middle] = temp;
+ return;
+ }
+ int i1 = start, i2 = middle, i3 = 0;
+ Object[] merge = new Object[end - start];
+ while (i1 < middle && i2 < end) {
+ merge[i3++] = ((Comparable<Object>) array[i1]).compareTo(array[i2]) <= 0 ? array[i1++] : array[i2++];
+ }
+ if (i1 < middle)
+ System.arraycopy(array, i1, merge, i3, middle - i1);
+ System.arraycopy(merge, 0, array, start, i2 - start);
+ }
+
+ /**
+ * Sorts the specified range in the array in descending order.
+ *
+ * @param array the Object array to be sorted
+ * @param start the start index to sort
+ * @param end the last + 1 index to sort
+ *
+ * @exception ClassCastException when an element in the array does not
+ * implement Comparable or elements cannot be compared to each other
+ * @exception IllegalArgumentException when <code>start > end</code>
+ * @exception ArrayIndexOutOfBoundsException when <code>start < 0</code>
+ * or <code>end > array.size()</code>
+ */
+ public static void dsort(Object[] array, int start, int end) {
+ // first sort in ascending order
+ sort(array, start, end);
+ // then swap the elements in the array
+ swap(array);
+ }
+
+ /**
+ * Reverse the elements in the array.
+ *
+ * @param array the Object array to be reversed
+ */
+ public static void swap(Object[] array) {
+ int start = 0;
+ int end = array.length - 1;
+ while (start < end) {
+ Object temp = array[start];
+ array[start++] = array[end];
+ array[end--] = temp;
+ }
+ }
+
+ /**
+ * Returns a string representation of the object
+ * in the given length.
+ * If the string representation of the given object
+ * is longer then it is truncated.
+ * If it is shorter then it is padded with the blanks
+ * to the given total length.
+ * If the given object is a number then the padding
+ * is done on the left, otherwise on the right.
+ *
+ * @param object the object to convert
+ * @param length the length the output string
+ */
+ public static String toString(Object object, int length) {
+ boolean onLeft = object instanceof Number;
+ return toString(object, length, ' ', onLeft);
+ }
+
+ /**
+ * Returns a string representation of the object
+ * in the given length.
+ * If the string representation of the given object
+ * is longer then it is truncated.
+ * If it is shorter then it is padded to the left or right
+ * with the given character to the given total length.
+ *
+ * @param object the object to convert
+ * @param length the length the output string
+ * @param pad the pad character
+ * @param onLeft if <code>true</code> pad on the left, otherwise an the right
+ */
+ public static String toString(Object object, int length, char pad, boolean onLeft) {
+ String input = String.valueOf(object);
+ int size = input.length();
+ if (size >= length) {
+ int start = (onLeft) ? size - length : 0;
+ return input.substring(start, length);
+ }
+
+ StringBuffer padding = new StringBuffer(length - size);
+ for (int i = size; i < length; i++)
+ padding.append(pad);
+
+ StringBuffer stringBuffer = new StringBuffer(length);
+ if (onLeft)
+ stringBuffer.append(padding.toString());
+ stringBuffer.append(input);
+ if (!onLeft)
+ stringBuffer.append(padding.toString());
+ return stringBuffer.toString();
+ }
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/osname.aliases b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/osname.aliases
new file mode 100644
index 000000000..0aff94e32
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/osname.aliases
@@ -0,0 +1,46 @@
+########################################################################
+# Copyright (c) 2003, 2012 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
+########################################################################
+
+# This file must be UTF8 encoded.
+
+#OS Aliases Description
+
+AIX # IBM
+DigitalUnix # Compaq
+embos # Segger Embedded Software Solutions
+Epoc32 SymbianOS # Symbian OS
+FreeBSD # Free BSD
+HPUX # Hewlett Packard
+IRIX # Sillicon Graphics
+Linux # Open source
+MacOS "Mac OS" # Apple
+MacOSX "Mac OS X" # Apple
+NetBSD # Open source
+Netware # Novell
+OpenBSD # Open source
+OS2 OS/2 # IBM
+QNX procnto # QNX Neutrino 2.1
+Solaris # Sun
+SunOS # Sun
+VxWorks # WindRiver Systems
+Windows95 "Windows 95" Win95 Win32 # Microsoft
+Windows98 "Windows 98" Win98 Win32 # Microsoft
+WindowsNT "Windows NT" WinNT Win32 # Microsoft
+WindowsCE "Windows CE" WinCE # Microsoft
+Windows2000 "Windows 2000" Win2000 Win32 # Microsoft
+WindowsXP "Windows XP" WinXP Win32 # Microsoft
+Windows2003 "Windows 2003" "Windows Server 2003" Win2003 Win32 # Microsoft
+WindowsVista WinVista "Windows Vista" Win32 # Microsoft
+Windows2008 "Windows 2008" "Windows Server 2008" Win2008 Win32 # Microsoft
+WindowsServer2008 "Windows 2008" "Windows Server 2008" Win2008 Win32 # Microsoft
+WindowsServer2008R2 "Windows 2008 R2" "Windows Server 2008 R2" Win2008R2 Win32 # Microsoft
+Windows7 "Windows 7" Win7 Win32 # Microsoft
+Windows8 "Windows 8" Win8 Win32 # Microsoft \ No newline at end of file
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/processor.aliases b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/processor.aliases
new file mode 100644
index 000000000..5fb0ff398
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/framework/internal/core/processor.aliases
@@ -0,0 +1,28 @@
+########################################################################
+# Copyright (c) 2003, 2005 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
+########################################################################
+
+# This file must be UTF8 encoded.
+
+#Processor Aliases Description
+
+68k # Motorola 68000 and up
+ARM # Intel Strong ARM
+Alpha # Compaq (ex DEC)
+Ignite psc1k # PTSC
+Mips # SGI
+PArisc # Hewlett Packard PA Risc
+PowerPC power ppc # Motorola/IBM Power PC
+Sparc # SUN
+x86 pentium i386 i486 i586 i686 # Intel
+s390 # IBM System 390
+s390x # IBM System 390 (64-bit)
+v850e # NEC V850E
+x86-64 amd64 em64t x86_64 # 64 bit x86 architecture

Back to the top