All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.osgi.internal.framework.BundleContextImpl Maven / Gradle / Ivy

There is a newer version: 1.9.3.RC1
Show newest version
/*******************************************************************************
 * Copyright (c) 2003, 2021 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/

package org.eclipse.osgi.internal.framework;

import static org.eclipse.osgi.internal.debug.Debug.OPTION_DEBUG_BUNDLE_TIME;
import static org.eclipse.osgi.internal.debug.Debug.OPTION_DEBUG_EVENTS;
import static org.eclipse.osgi.internal.debug.Debug.OPTION_DEBUG_GENERAL;

import java.io.File;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.osgi.container.Module;
import org.eclipse.osgi.container.ModuleWiring;
import org.eclipse.osgi.container.namespaces.EquinoxModuleDataNamespace;
import org.eclipse.osgi.framework.eventmgr.EventDispatcher;
import org.eclipse.osgi.internal.debug.Debug;
import org.eclipse.osgi.internal.loader.BundleLoader;
import org.eclipse.osgi.internal.messages.Msg;
import org.eclipse.osgi.internal.serviceregistry.ServiceReferenceImpl;
import org.eclipse.osgi.internal.serviceregistry.ServiceRegistrationImpl;
import org.eclipse.osgi.internal.serviceregistry.ServiceRegistry;
import org.eclipse.osgi.internal.serviceregistry.ServiceUse;
import org.eclipse.osgi.internal.serviceregistry.ShrinkableCollection;
import org.eclipse.osgi.storage.BundleInfo.Generation;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleException;
import org.osgi.framework.BundleListener;
import org.osgi.framework.Constants;
import org.osgi.framework.Filter;
import org.osgi.framework.FrameworkEvent;
import org.osgi.framework.FrameworkListener;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceFactory;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceObjects;
import org.osgi.framework.ServicePermission;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.framework.hooks.bundle.FindHook;
import org.osgi.resource.Capability;

/**
 * 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 {
	/** 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 EquinoxBundle bundle;

	/** Internal equinox container object. */
	final EquinoxContainer container;
	final Debug debug;

	/**
	 * Services that bundle is using. Key is ServiceRegistrationImpl, Value is
	 * ServiceUse
	 */
	/* @GuardedBy("contextLock") */
	private HashMap, ServiceUse> servicesInUse;

	/** The current instantiation of the activator. */
	private 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.
	 */
	public BundleContextImpl(EquinoxBundle bundle, EquinoxContainer container) {
		this.bundle = bundle;
		this.container = container;
		this.debug = container.getConfiguration().getDebug();
		valid = true;
		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 = container.getServiceRegistry();

		registry.removeAllServiceListeners(this);
		container.getEventPublisher().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 null if the
	 *         property is undefined.
	 */
	@Override
	public String getProperty(String key) {
		SecurityManager sm = System.getSecurityManager();

		if (sm != null) {
			sm.checkPropertyAccess(key);
		}

		return (container.getConfiguration().getProperty(key));
	}

	/**
	 * Retrieve the Bundle object for the context bundle.
	 *
	 * @return The context bundle's Bundle object.
	 */
	@Override
	public Bundle getBundle() {
		return getBundleImpl();
	}

	public EquinoxBundle getBundleImpl() {
		return bundle;
	}

	@Override
	public Bundle installBundle(String location) throws BundleException {
		return installBundle(location, null);
	}

	@Override
	public Bundle installBundle(String location, InputStream in) throws BundleException {
		checkValid();

		Generation generation = container.getStorage().install(bundle.getModule(), location, in);
		return generation.getRevision().getBundle();
	}

	/**
	 * Retrieve the bundle that has the given unique identifier.
	 *
	 * @param id The identifier of the bundle to retrieve.
	 * @return A Bundle object, or null if the identifier doesn't match
	 *         any installed bundle.
	 */
	@Override
	public Bundle getBundle(long id) {
		Module m = container.getStorage().getModuleContainer().getModule(id);
		if (m == null) {
			return null;
		}

		List bundles = new ArrayList<>(1);
		bundles.add(m.getBundle());
		notifyFindHooks(this, bundles);
		if (bundles.isEmpty()) {
			return null;
		}
		return m.getBundle();
	}

	@Override
	public Bundle getBundle(String location) {
		Module m = container.getStorage().getModuleContainer().getModule(location);
		return m == null ? null : m.getBundle();
	}

	/**
	 * 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 Bundle} objects, one object per installed bundle.
	 */
	@Override
	public Bundle[] getBundles() {
		List modules = container.getStorage().getModuleContainer().getModules();
		List bundles = new ArrayList<>(modules.size());
		for (Module module : modules) {
			bundles.add(module.getBundle());
		}

		notifyFindHooks(this, bundles);
		return bundles.toArray(new Bundle[bundles.size()]);
	}

	private void notifyFindHooks(final BundleContextImpl context, List allBundles) {
		if (context.getBundleImpl().getBundleId() == 0) {
			// Make a copy for the purposes of calling the hooks;
			// The the removals from the hooks are ignored
			allBundles = new ArrayList<>(allBundles);
		}
		final Collection shrinkable = new ShrinkableCollection<>(allBundles);
		if (System.getSecurityManager() == null) {
			notifyFindHooksPriviledged(context, shrinkable);
		} else {
			AccessController.doPrivileged(new PrivilegedAction() {
				@Override
				public Void run() {
					notifyFindHooksPriviledged(context, shrinkable);
					return null;
				}
			});
		}
	}

	void notifyFindHooksPriviledged(final BundleContextImpl context, final Collection allBundles) {
		if (debug.DEBUG_HOOKS) {
			debug.trace(OPTION_DEBUG_EVENTS, "notifyBundleFindHooks(" + allBundles + ")"); //$NON-NLS-1$ //$NON-NLS-2$
		}
		ServiceRegistry sr = container.getServiceRegistry();
		if (sr == null) {
			allBundles.clear();
		} else {
			sr.notifyHooksPrivileged(FindHook.class, "find", //$NON-NLS-1$
					(hook, hookRegistration) -> hook.find(context, allBundles));
		}
	}

	@Override
	public void addServiceListener(ServiceListener listener, String filter) throws InvalidSyntaxException {
		checkValid();

		if (listener == null) {
			throw new IllegalArgumentException();
		}
		container.getServiceRegistry().addServiceListener(this, listener, filter);
	}

	/**
	 * Add a service listener.
	 *
	 * 

* This method is the same as calling * {@link #addServiceListener(ServiceListener, String)} with filter set to * null. * * @see #addServiceListener(ServiceListener, String) */ @Override public void addServiceListener(ServiceListener listener) { try { addServiceListener(listener, null); } catch (InvalidSyntaxException e) { // This would be very unexpected and should not be ignored. // Throw a runtime exception to the caller throw new RuntimeException(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. * *

* If this method is called with a listener which is not registered, then this * method does nothing. * * @param listener The service listener to remove. */ @Override public void removeServiceListener(ServiceListener listener) { if (listener == null) { throw new IllegalArgumentException(); } container.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 */ @Override public void addBundleListener(BundleListener listener) { checkValid(); if (listener == null) { throw new IllegalArgumentException(); } if (debug.DEBUG_EVENTS) { String listenerName = listener.getClass().getName() + "@" //$NON-NLS-1$ + Integer.toHexString(System.identityHashCode(listener)); debug.trace(OPTION_DEBUG_EVENTS, "addBundleListener[" + bundle + "](" + listenerName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } container.getEventPublisher().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. * *

* If this method is called with a listener which is not registered, then this * method does nothing. * * @param listener The bundle listener to remove. */ @Override public void removeBundleListener(BundleListener listener) { if (listener == null) { throw new IllegalArgumentException(); } if (debug.DEBUG_EVENTS) { String listenerName = listener.getClass().getName() + "@" //$NON-NLS-1$ + Integer.toHexString(System.identityHashCode(listener)); debug.trace(OPTION_DEBUG_EVENTS, "removeBundleListener[" + bundle + "](" + listenerName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } container.getEventPublisher().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 */ @Override public void addFrameworkListener(FrameworkListener listener) { checkValid(); if (listener == null) { throw new IllegalArgumentException(); } if (debug.DEBUG_EVENTS) { String listenerName = listener.getClass().getName() + "@" //$NON-NLS-1$ + Integer.toHexString(System.identityHashCode(listener)); debug.trace(OPTION_DEBUG_EVENTS, "addFrameworkListener[" + bundle + "](" + listenerName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } container.getEventPublisher().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. * *

* If this method is called with a listener which is not registered, then this * method does nothing. * * @param listener The framework listener to remove. */ @Override public void removeFrameworkListener(FrameworkListener listener) { if (listener == null) { throw new IllegalArgumentException(); } if (debug.DEBUG_EVENTS) { String listenerName = listener.getClass().getName() + "@" //$NON-NLS-1$ + Integer.toHexString(System.identityHashCode(listener)); debug.trace(OPTION_DEBUG_EVENTS, "removeFrameworkListener[" + bundle + "](" + listenerName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } container.getEventPublisher().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. * *

* A bundle can register a service object that implements the * {@link ServiceFactory} interface to have more flexiblity in providing service * objects to different bundles. * *

* The following steps are followed to register a service: *

    *
  1. If the service parameter is not a {@link ServiceFactory}, an * IllegalArgumentException is thrown if the service parameter is * not an instanceof all the classes named. *
  2. The service is added to the framework's service registry and may now be * used by other bundles. *
  3. A {@link ServiceEvent} of type {@link ServiceEvent#REGISTERED} is * synchronously sent. *
  4. A {@link ServiceRegistration} object for this registration is returned. *
* * @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 null 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: *
    *
  • The service parameter is * null. *
  • The service parameter is * not a {@link ServiceFactory} * and is not an * instanceof all the * named classes in the clazzes * parameter. *
* @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 */ @Override public ServiceRegistration registerService(String[] clazzes, Object service, Dictionary properties) { checkValid(); return container.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. * *

* 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) */ @Override public ServiceRegistration registerService(String clazz, Object service, Dictionary properties) { String[] clazzes = new String[] { clazz }; return registerService(clazzes, service, properties); } /** * Returns a list of ServiceReference objects. This method returns * a list of ServiceReference objects for services which implement * and were registered under the specified class and match the specified filter * criteria. * *

* 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. * *

* filter 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. * *

* If filter is null, all registered services are * considered to match the filter. *

* If filter cannot be parsed, an {@link InvalidSyntaxException} * will be thrown with a human readable message where the filter became * unparsable. * *

* The following steps are required to select a service: *

    *
  1. If the Java Runtime Environment supports permissions, the caller is * checked for the ServicePermission to get the service with the * specified class. If the caller does not have the correct permission, * null is returned. *
  2. If the filter string is not null, the filter string is * parsed and the set of registered services which satisfy the filter is * produced. If the filter string is null, then all registered * services are considered to satisfy the filter. *
  3. If clazz is not null, the set is further * reduced to those services which are an instanceof 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. *
  4. An array of ServiceReference to the selected services is * returned. *
* * @param clazz The class name with which the service was registered, or * null for all services. * @param filter The filter criteria. * @return An array of ServiceReference objects, or * null if no services are registered which satisfy the * search. * @exception InvalidSyntaxException If filter contains an invalid * filter string which cannot be parsed. */ @Override public ServiceReference[] getServiceReferences(String clazz, String filter) throws InvalidSyntaxException { checkValid(); return container.getServiceRegistry().getServiceReferences(this, clazz, filter, false); } @Override public ServiceReference[] getAllServiceReferences(String clazz, String filter) throws InvalidSyntaxException { checkValid(); return container.getServiceRegistry().getServiceReferences(this, clazz, filter, true); } /** * Get a service reference. Retrieves a {@link ServiceReference} for a service * which implements the named class. * *

* 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. * *

* 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 * null 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 null if no * services are registered which implement the named class. * @see #getServiceReferences */ @Override public ServiceReference getServiceReference(String clazz) { checkValid(); return container.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. * *

* This method will always return null when the service associated * with this reference has been unregistered. * *

* The following steps are followed to get the service object: *

    *
  1. If the service has been unregistered, null is returned. *
  2. The context bundle's use count for this service is incremented by one. *
  3. 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.
    * If the service object returned by the {@link ServiceFactory} is not an * instanceof all the classes named when the service was registered * or the {@link ServiceFactory} throws an exception, null is * returned and a {@link FrameworkEvent} of type {@link FrameworkEvent#ERROR} is * broadcast. *
  4. The service object for the service is returned. *
* * @param reference A reference to the service whose service object is desired. * @return A service object for the service associated with this reference, or * null 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 */ @Override public S getService(ServiceReference reference) { checkValid(); if (reference == null) throw new NullPointerException("A null service reference is not allowed."); //$NON-NLS-1$ provisionServicesInUseMap(); S service = container.getServiceRegistry().getService(this, (ServiceReferenceImpl) 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 false. Otherwise, the context bundle's use count for the * service is decremented by one. See {@link #getBundle()} for a definition of * context bundle. * *

* 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. * *

* The following steps are followed to unget the service object: *

    *
  1. If the context bundle's use count for the service is zero or the service * has been unregistered, false is returned. *
  2. The context bundle's use count for this service is decremented by one. *
  3. 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. *
  4. true is returned. *
* * @param reference A reference to the service to be released. * @return false if the context bundle's use count for the service * is zero or if the service has been unregistered, otherwise * true. * @exception java.lang.IllegalStateException If the bundle context has stopped. * @see #getService * @see ServiceFactory */ @Override public boolean ungetService(ServiceReference reference) { return container.getServiceRegistry().ungetService(this, (ServiceReferenceImpl) reference); } /** * Creates a File 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 null. * *

* A File 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. * *

* If the Java runtime environment supports permissions, the framework the will * ensure that the bundle has java.io.FilePermission 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 File object that represents the requested file or * null if the adaptor does not have file system support. * @exception java.lang.IllegalStateException If the bundle context has stopped. */ @Override public File getDataFile(String filename) { checkValid(); Generation generation = (Generation) bundle.getModule().getCurrentRevision().getRevisionInfo(); return generation.getBundleInfo().getDataFile(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 { long start = 0; try { if (debug.DEBUG_BUNDLE_TIME) { start = System.currentTimeMillis(); } activator = loadBundleActivator(); if (debug.DEBUG_BUNDLE_TIME) { debug.trace(OPTION_DEBUG_BUNDLE_TIME, (System.currentTimeMillis() - start) + " ms to load the activator of " + bundle); //$NON-NLS-1$ } } catch (Exception e) { if (e instanceof RuntimeException) { throw (RuntimeException) e; } throw new BundleException(Msg.BundleContextImpl_LoadActivatorError + ' ' + bundle, BundleException.ACTIVATOR_ERROR, e); } if (activator != null) { try { startActivator(activator); } catch (BundleException be) { activator = null; throw be; } finally { if (debug.DEBUG_BUNDLE_TIME) { debug.trace(OPTION_DEBUG_BUNDLE_TIME, (System.currentTimeMillis() - start) + " ms to load and start the activator of " + bundle); //$NON-NLS-1$ } } } /* * activator completed successfully. We must use this same activator object when * we stop this bundle. */ } private BundleActivator loadBundleActivator() throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { ModuleWiring wiring = bundle.getModule().getCurrentRevision().getWiring(); if (wiring == null) { return null; } BundleLoader loader = (BundleLoader) wiring.getModuleLoader(); if (loader == null) { return null; } List metadata = wiring.getRevision() .getCapabilities(EquinoxModuleDataNamespace.MODULE_DATA_NAMESPACE); if (metadata.isEmpty()) { return null; } String activatorName = (String) metadata.get(0).getAttributes() .get(EquinoxModuleDataNamespace.CAPABILITY_ACTIVATOR); if (activatorName == null) { return null; } Class activatorClass = loader.findClass(activatorName); return (BundleActivator) activatorClass.getConstructor().newInstance(); } /** * Calls the start method of a BundleActivator. * * @param bundleActivator that activator to start */ private void startActivator(final BundleActivator bundleActivator) throws BundleException { try { AccessController.doPrivileged(new PrivilegedExceptionAction() { @Override public Void run() throws Exception { if (bundleActivator != null) { // 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); } } return null; } }); } catch (Throwable t) { if (t instanceof PrivilegedActionException) { t = ((PrivilegedActionException) t).getException(); } if (debug.DEBUG_GENERAL) { debug.traceThrowable(OPTION_DEBUG_GENERAL, t); } String clazz = null; clazz = bundleActivator.getClass().getName(); throw new BundleException( NLS.bind(Msg.BUNDLE_ACTIVATOR_EXCEPTION, new Object[] { clazz, "start", //$NON-NLS-1$ bundle.getSymbolicName() == null ? "" + bundle.getBundleId() : bundle.getSymbolicName() }), //$NON-NLS-1$ BundleException.ACTIVATOR_ERROR, t); } } Object setContextFinder() { if (!container.getConfiguration().BUNDLE_SET_TCCL) return Boolean.FALSE; Thread currentThread = Thread.currentThread(); ClassLoader previousTCCL = currentThread.getContextClassLoader(); ClassLoader contextFinder = container.getContextFinder(); if (previousTCCL != contextFinder) { try { currentThread.setContextClassLoader(container.getContextFinder()); return previousTCCL; } catch (RuntimeException e) { // move on without setting TCCL (https://github.com/eclipse-equinox/equinox/issues/303) if (debug.DEBUG_GENERAL) { debug.traceThrowable(OPTION_DEBUG_GENERAL, e); } return Boolean.FALSE; } } 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 { final BundleActivator bundleActivator = activator; AccessController.doPrivileged(new PrivilegedExceptionAction() { @Override public Void run() throws Exception { if (bundleActivator != null) { // make sure the context class loader is set correctly Object previousTCCL = setContextFinder(); try { /* Stop the bundle synchronously */ bundleActivator.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.traceThrowable(OPTION_DEBUG_GENERAL, t); } String clazz = (activator == null) ? "" : activator.getClass().getName(); //$NON-NLS-1$ throw new BundleException( NLS.bind(Msg.BUNDLE_ACTIVATOR_EXCEPTION, new Object[] { clazz, "stop", //$NON-NLS-1$ bundle.getSymbolicName() == null ? "" + bundle.getBundleId() : bundle.getSymbolicName() }), //$NON-NLS-1$ BundleException.ACTIVATOR_ERROR, t); } 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, ServiceUse> getServicesInUseMap() { synchronized (contextLock) { return servicesInUse; } } /** * Provision the map of ServiceRegistrationImpl to ServiceUse for services being * used by this context. */ public void provisionServicesInUseMap() { synchronized (contextLock) { if (servicesInUse == null) // Cannot predict how many services a bundle will use, start with a small table. servicesInUse = new HashMap<>(10); } } /** * 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 */ @Override public void dispatchEvent(Object originalListener, Object l, int action, Object object) { Object previousTCCL = setContextFinder(); try { // if context still valid or the system bundle if (isValid() || bundle.getBundleId() == 0) { switch (action) { case EquinoxEventPublisher.BUNDLEEVENT: case EquinoxEventPublisher.BUNDLEEVENTSYNC: { BundleListener listener = (BundleListener) l; if (debug.DEBUG_EVENTS) { String listenerName = listener.getClass().getName() + "@" //$NON-NLS-1$ + Integer.toHexString(System.identityHashCode(listener)); debug.trace(OPTION_DEBUG_EVENTS, "dispatchBundleEvent[" + bundle + "](" + listenerName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } 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() + "@" //$NON-NLS-1$ + Integer.toHexString(System.identityHashCode(listener)); debug.trace(OPTION_DEBUG_EVENTS, "dispatchServiceEvent[" + bundle + "](" + listenerName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } listener.serviceChanged(event); break; } case EquinoxEventPublisher.FRAMEWORKEVENT: { FrameworkListener listener = (FrameworkListener) l; if (debug.DEBUG_EVENTS) { String listenerName = listener.getClass().getName() + "@" //$NON-NLS-1$ + Integer.toHexString(System.identityHashCode(listener)); debug.trace(OPTION_DEBUG_EVENTS, "dispatchFrameworkEvent[" + bundle + "](" + 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_EVENTS) { debug.trace(OPTION_DEBUG_EVENTS, "Exception in bottom level event dispatcher: " + t.getMessage()); //$NON-NLS-1$ // It may seem redundant to trace the exception here giving we publish // an error event below, but that event may not get fired if the current event // is a framework event error debug.traceThrowable(OPTION_DEBUG_EVENTS, t); } if (action == EquinoxEventPublisher.FRAMEWORKEVENT && ((FrameworkEvent) object).getType() == FrameworkEvent.ERROR) { // avoid infinite loop by publishing another error } else { container.getEventPublisher().publishFrameworkEvent(FrameworkEvent.ERROR, bundle, 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. */ @Override public Filter createFilter(String filter) throws InvalidSyntaxException { 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 + ' ' + bundle); } } /** * This method checks that the context is still valid. * * @return true if the context is still valid; false otherwise */ public boolean isValid() { return valid; } @Override public ServiceRegistration registerService(Class clazz, S service, Dictionary properties) { @SuppressWarnings("unchecked") ServiceRegistration registration = (ServiceRegistration) registerService(clazz.getName(), service, properties); return registration; } @Override public ServiceRegistration registerService(Class clazz, ServiceFactory factory, Dictionary properties) { @SuppressWarnings("unchecked") ServiceRegistration registration = (ServiceRegistration) registerService(clazz.getName(), factory, properties); return registration; } @Override public ServiceReference getServiceReference(Class clazz) { @SuppressWarnings("unchecked") ServiceReference reference = (ServiceReference) getServiceReference(clazz.getName()); return reference; } @Override public Collection> getServiceReferences(Class clazz, String filter) throws InvalidSyntaxException { @SuppressWarnings("unchecked") ServiceReference[] refs = (ServiceReference[]) getServiceReferences(clazz.getName(), filter); if (refs == null) { return Collections.emptyList(); } List> result = new ArrayList<>(refs.length); Collections.addAll(result, refs); return result; } public EquinoxContainer getContainer() { return container; } @Override public ServiceObjects getServiceObjects(ServiceReference reference) { checkValid(); if (reference == null) throw new NullPointerException("A null service reference is not allowed."); //$NON-NLS-1$ provisionServicesInUseMap(); ServiceObjects serviceObjects = container.getServiceRegistry().getServiceObjects(this, (ServiceReferenceImpl) reference); return serviceObjects; } }