Package org.eclipse.osgi.framework.internal.core

Source Code of org.eclipse.osgi.framework.internal.core.BundleContextImpl

/*******************************************************************************
* Copyright (c) 2003, 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.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.framework.eventmgr.EventListeners;
import org.eclipse.osgi.internal.profile.Profile;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.*;

/**
* Bundle's execution context.
*
* This object is given out to bundles and wraps the internal
* BundleContext object. It is destroyed when a bundle is stopped.
*/

public class BundleContextImpl implements BundleContext, EventDispatcher {
  public static final String PROP_SCOPE_SERVICE_EVENTS = "osgi.scopeServiceEvents"; //$NON-NLS-1$
  public static final boolean scopeEvents = Boolean.valueOf(FrameworkProperties.getProperty(PROP_SCOPE_SERVICE_EVENTS, "true")).booleanValue(); //$NON-NLS-1$
  /** true if the bundle context is still valid */
  private 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.
  protected BundleHost bundle;

  /** Internal framework object. */
  protected Framework framework;

  /** Services that bundle has used. Key is ServiceReference,
   Value is ServiceUse */
  protected Hashtable servicesInUse;

  /** Listener list for bundle's BundleListeners */
  protected EventListeners bundleEvent;

  /** Listener list for bundle's SynchronousBundleListeners */
  protected EventListeners bundleEventSync;

  /** Listener list for bundle's ServiceListeners */
  protected EventListeners serviceEvent;

  /** Listener list for bundle's FrameworkListeners */
  protected EventListeners frameworkEvent;

  /** The current instantiation of the activator. */
  protected BundleActivator activator;

  /** private object for locking */
  protected 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;
    bundleEvent = null;
    bundleEventSync = null;
    serviceEvent = null;
    frameworkEvent = null;
    servicesInUse = null;
    activator = null;
  }

  /**
   * Destroy the wrapper. This is called when the bundle is stopped.
   *
   */
  protected void close() {
    valid = false; /* invalidate context */

    synchronized (framework.serviceEvent) {
      if (serviceEvent != null) {
        framework.serviceEvent.removeListener(this);
        serviceEvent = null;
      }
    }
    synchronized (framework.frameworkEvent) {
      if (frameworkEvent != null) {
        framework.frameworkEvent.removeListener(this);
        frameworkEvent = null;
      }
    }
    synchronized (framework.bundleEvent) {
      if (bundleEvent != null) {
        framework.bundleEvent.removeListener(this);
        bundleEvent = null;
      }
    }
    synchronized (framework.bundleEventSync) {
      if (bundleEventSync != null) {
        framework.bundleEventSync.removeListener(this);
        bundleEventSync = null;
      }
    }

    /* service's registered by the bundle, if any, are unregistered. */
    ServiceReference[] publishedReferences = null;
    synchronized (framework.serviceRegistry) {
      publishedReferences = framework.serviceRegistry.lookupServiceReferences(this);
    }

    if (publishedReferences != null) {
      for (int i = 0; i < publishedReferences.length; i++) {
        try {
          ((ServiceReferenceImpl) publishedReferences[i]).registration.unregister();
        } catch (IllegalStateException e) {
          /* already unregistered */
        }
      }
    }

    /* service's used by the bundle, if any, are released. */
    if (servicesInUse != null) {
      int usedSize;
      ServiceReference[] usedRefs = null;

      synchronized (servicesInUse) {
        usedSize = servicesInUse.size();

        if (usedSize > 0) {
          if (Debug.DEBUG && Debug.DEBUG_SERVICES) {
            Debug.println("Releasing services"); //$NON-NLS-1$
          }

          usedRefs = new ServiceReference[usedSize];

          Enumeration refsEnum = servicesInUse.keys();
          for (int i = 0; i < usedSize; i++) {
            usedRefs[i] = (ServiceReference) refsEnum.nextElement();
          }
        }
      }

      for (int i = 0; i < usedSize; i++) {
        ((ServiceReferenceImpl) usedRefs[i]).registration.releaseService(this);
      }

      servicesInUse = null;
    }

    bundle = 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 org.osgi.framework.Bundle getBundle() {
    checkValid();

    return (bundle);
  }

  /**
   * Install a bundle from a location.
   *
   * The bundle is obtained from the location
   * parameter as interpreted by the framework
   * in an implementation dependent way. Typically, location
   * will most likely be a URL.
   *
   * @param location The location identifier of the bundle to install.
   * @return The Bundle object of the installed bundle.
   */
  public org.osgi.framework.Bundle installBundle(String location) throws BundleException {
    checkValid();
    //note AdminPermission is checked after bundle is loaded
    return framework.installBundle(location);
  }

  /**
   * Install a bundle from an InputStream.
   *
   * <p>This method performs all the steps listed in
   * {@link #installBundle(java.lang.String)}, except the
   * bundle's content will be read from the InputStream.
   * The location identifier specified will be used
   * as the identity of the bundle.
   *
   * @param location The location identifier of the bundle to install.
   * @param in The InputStream from which the bundle will be read.
   * @return The Bundle of the installed bundle.
   */
  public org.osgi.framework.Bundle installBundle(String location, InputStream in) throws BundleException {
    checkValid();
    //note AdminPermission is checked after bundle is loaded
    return framework.installBundle(location, in);
  }

  /**
   * 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 org.osgi.framework.Bundle getBundle(long id) {
    return (framework.getBundle(id));
  }

  /**
   * 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 org.osgi.framework.Bundle[] getBundles() {
    return framework.getAllBundles();
  }

  /**
   * 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 (Debug.DEBUG && Debug.DEBUG_EVENTS) {
      String listenerName = listener.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(listener)); //$NON-NLS-1$
      Debug.println("addServiceListener[" + bundle + "](" + listenerName + ", \"" + filter + "\")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
    }

    ServiceListener filteredListener = new FilteredServiceListener(filter, listener, this);

    synchronized (framework.serviceEvent) {
      if (serviceEvent == null) {
        serviceEvent = new EventListeners();
        framework.serviceEvent.addListener(this, this);
      }

      serviceEvent.addListener(listener, filteredListener);
    }
  }

  /**
   * 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 && 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 (Debug.DEBUG && Debug.DEBUG_SERVICES) {
      String listenerName = listener.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(listener)); //$NON-NLS-1$
      Debug.println("removeServiceListener[" + bundle + "](" + listenerName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
    }

    synchronized (framework.serviceEvent) {
      if (serviceEvent != null) {
        serviceEvent.removeListener(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 (Debug.DEBUG && 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$
    }

    if (listener instanceof SynchronousBundleListener) {
      framework.checkAdminPermission(getBundle(), AdminPermission.LISTENER);
      synchronized (framework.bundleEventSync) {
        if (bundleEventSync == null) {
          bundleEventSync = new EventListeners();
          framework.bundleEventSync.addListener(this, this);
        }

        bundleEventSync.addListener(listener, listener);
      }
    } else {
      synchronized (framework.bundleEvent) {
        if (bundleEvent == null) {
          bundleEvent = new EventListeners();
          framework.bundleEvent.addListener(this, this);
        }

        bundleEvent.addListener(listener, listener);
      }
    }
  }

  /**
   * 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 (Debug.DEBUG && 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$
    }

    if (listener instanceof SynchronousBundleListener) {
      framework.checkAdminPermission(getBundle(), AdminPermission.LISTENER);

      synchronized (framework.bundleEventSync) {
        if (bundleEventSync != null) {
          bundleEventSync.removeListener(listener);
        }
      }
    } else {
      synchronized (framework.bundleEvent) {
        if (bundleEvent != null) {
          bundleEvent.removeListener(listener);
        }
      }
    }
  }

  /**
   * 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 (Debug.DEBUG && 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$
    }

    synchronized (framework.frameworkEvent) {
      if (frameworkEvent == null) {
        frameworkEvent = new EventListeners();
        framework.frameworkEvent.addListener(this, this);
      }

      frameworkEvent.addListener(listener, listener);
    }
  }

  /**
   * 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 (Debug.DEBUG && 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$
    }

    synchronized (framework.frameworkEvent) {
      if (frameworkEvent != null) {
        frameworkEvent.removeListener(listener);
      }
    }
  }

  /**
   * Register a service with multiple names.
   * This method registers the given service object with the given properties
   * under the given class names.
   * A {@link ServiceRegistrationImpl} object is returned.
   * The {@link ServiceRegistrationImpl} 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 ServiceRegistrationImpl} 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 ServiceRegistrationImpl#setProperties ServiceRegistration.setProperties}
   *        method.
   *        This parameter may be <code>null</code> if the service has no properties.
   * @return A {@link ServiceRegistrationImpl} 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 ServiceRegistrationImpl
   * @see ServiceFactory
   */
  public org.osgi.framework.ServiceRegistration registerService(String[] clazzes, Object service, Dictionary properties) {
    checkValid();

    if (service == null) {
      if (Debug.DEBUG && Debug.DEBUG_SERVICES) {
        Debug.println("Service object is null"); //$NON-NLS-1$
      }

      throw new IllegalArgumentException(Msg.SERVICE_ARGUMENT_NULL_EXCEPTION);
    }

    int size = clazzes.length;

    if (size == 0) {
      if (Debug.DEBUG && Debug.DEBUG_SERVICES) {
        Debug.println("Classes array is empty"); //$NON-NLS-1$
      }

      throw new IllegalArgumentException(Msg.SERVICE_EMPTY_CLASS_LIST_EXCEPTION);
    }

    /* copy the array so that changes to the original will not affect us. */
    String[] copy = new String[clazzes.length];
    // doing this the hard way so we can intern the strings
    for (int i = clazzes.length - 1; i >= 0; i--)
      copy[i] = clazzes[i].intern();
    clazzes = copy;

    /* check for ServicePermissions. */
    framework.checkRegisterServicePermission(clazzes);

    if (!(service instanceof ServiceFactory)) {
      String invalidService = checkServiceClass(clazzes, service);
      if (invalidService != null) {
        if (Debug.DEBUG && Debug.DEBUG_SERVICES) {
          Debug.println("Service object is not an instanceof " + invalidService); //$NON-NLS-1$
        }
        throw new IllegalArgumentException(NLS.bind(Msg.SERVICE_NOT_INSTANCEOF_CLASS_EXCEPTION, invalidService));
      }
    }

    return (createServiceRegistration(clazzes, service, properties));
  }

  //Return the name of the class that is not satisfied by the service object
  static String checkServiceClass(final String[] clazzes, final Object serviceObject) {
    ClassLoader cl = (ClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
      public Object run() {
        return serviceObject.getClass().getClassLoader();
      }
    });
    for (int i = 0; i < clazzes.length; i++) {
      try {
        Class serviceClazz = cl == null ? Class.forName(clazzes[i]) : cl.loadClass(clazzes[i]);
        if (!serviceClazz.isInstance(serviceObject))
          return clazzes[i];
      } catch (ClassNotFoundException e) {
        //This check is rarely done
        if (extensiveCheckServiceClass(clazzes[i], serviceObject.getClass()))
          return clazzes[i];
      }
    }
    return null;
  }

  private static boolean extensiveCheckServiceClass(String clazz, Class serviceClazz) {
    if (clazz.equals(serviceClazz.getName()))
      return false;
    Class[] interfaces = serviceClazz.getInterfaces();
    for (int i = 0; i < interfaces.length; i++)
      if (!extensiveCheckServiceClass(clazz, interfaces[i]))
        return false;
    Class superClazz = serviceClazz.getSuperclass();
    if (superClazz != null)
      if (!extensiveCheckServiceClass(clazz, superClazz))
        return false;
    return true;
  }

  /**
   * Create a new ServiceRegistration object. This method is used so that it may be overridden
   * by a secure implementation.
   *
   * @param clazzes The class names under which the service can be located.
   * @param service The service object or a {@link ServiceFactory} object.
   * @param properties The properties for this service.
   * @return A {@link ServiceRegistrationImpl} object for use by the bundle.
   */
  protected ServiceRegistrationImpl createServiceRegistration(String[] clazzes, Object service, Dictionary properties) {
    return (new ServiceRegistrationImpl(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 org.osgi.framework.ServiceRegistration registerService(String clazz, Object service, Dictionary 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 FilterImpl}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 org.osgi.framework.ServiceReference[] getServiceReferences(String clazz, String filter) throws InvalidSyntaxException {
    checkValid();
    if (Debug.DEBUG && Debug.DEBUG_SERVICES) {
      Debug.println("getServiceReferences(" + clazz + ", \"" + filter + "\")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
    }
    return (framework.getServiceReferences(clazz, filter, this, false));
  }

  public ServiceReference[] getAllServiceReferences(String clazz, String filter) throws InvalidSyntaxException {
    checkValid();
    if (Debug.DEBUG && Debug.DEBUG_SERVICES) {
      Debug.println("getAllServiceReferences(" + clazz + ", \"" + filter + "\")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
    }
    return (framework.getServiceReferences(clazz, filter, this, true));
  }

  /**
   * Get a service reference.
   * Retrieves a {@link ServiceReferenceImpl} 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 ServiceReferenceImpl}
   * is returned.
   *
   * @param clazz The class name which the service must implement.
   * @return A {@link ServiceReferenceImpl} object, or <code>null</code>
   * if no services are registered which implement the named class.
   * @see #getServiceReferences
   */
  public org.osgi.framework.ServiceReference getServiceReference(String clazz) {
    checkValid();

    if (Debug.DEBUG && Debug.DEBUG_SERVICES) {
      Debug.println("getServiceReference(" + clazz + ")"); //$NON-NLS-1$ //$NON-NLS-2$
    }

    try {
      ServiceReference[] references = framework.getServiceReferences(clazz, null, this, false);

      if (references != null) {
        int index = 0;

        int length = references.length;

        if (length > 1) /* if more than one service, select highest ranking */{
          int rankings[] = new int[length];
          int count = 0;
          int maxRanking = Integer.MIN_VALUE;

          for (int i = 0; i < length; i++) {
            int ranking = ((ServiceReferenceImpl) references[i]).getRanking();

            rankings[i] = ranking;

            if (ranking > maxRanking) {
              index = i;
              maxRanking = ranking;
              count = 1;
            } else {
              if (ranking == maxRanking) {
                count++;
              }
            }
          }

          if (count > 1) /* if still more than one service, select lowest id */{
            long minId = Long.MAX_VALUE;

            for (int i = 0; i < length; i++) {
              if (rankings[i] == maxRanking) {
                long id = ((ServiceReferenceImpl) references[i]).getId();

                if (id < minId) {
                  index = i;
                  minId = id;
                }
              }
            }
          }
        }

        return (references[index]);
      }
    } catch (InvalidSyntaxException e) {
      if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
        Debug.println("InvalidSyntaxException w/ null filter" + e.getMessage()); //$NON-NLS-1$
        Debug.printStackTrace(e);
      }
    }

    return (null);
  }

  /**
   * 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 Object getService(org.osgi.framework.ServiceReference reference) {
    checkValid();

    synchronized (contextLock) {
      if (servicesInUse == null)
        // Cannot predict how many services a bundle will use, start with a small table.
        servicesInUse = new Hashtable(10);
    }

    ServiceRegistrationImpl registration = ((ServiceReferenceImpl) reference).registration;

    framework.checkGetServicePermission(registration.clazzes);

    return registration.getService(BundleContextImpl.this);
  }

  /**
   * 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(org.osgi.framework.ServiceReference reference) {
    checkValid();

    ServiceRegistrationImpl registration = ((ServiceReferenceImpl) reference).registration;

    return registration.ungetService(BundleContextImpl.this);
  }

  /**
   * 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 org.osgi.framework.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() {
        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$
            /* Start the bundle synchronously */
            bundleActivator.start(BundleContextImpl.this);
            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 && 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()}), t); //$NON-NLS-1$ //$NON-NLS-2$
    } finally {
      if (Profile.PROFILE && Profile.STARTUP)
        Profile.logExit("BundleContextImpl.startActivator()"); //$NON-NLS-1$
    }

  }

  /**
   * Call bundle's BundleActivator.stop()
   * This method is called by Bundle.stopWorker to stop the bundle.
   *
   * @exception org.osgi.framework.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() {
        public Object run() throws Exception {
          if (activator != null) {
            /* Stop the bundle synchronously */
            activator.stop(BundleContextImpl.this);
          }
          return null;
        }
      });
    } catch (Throwable t) {
      if (t instanceof PrivilegedActionException) {
        t = ((PrivilegedActionException) t).getException();
      }

      if (Debug.DEBUG && 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()}), t); //$NON-NLS-1$ //$NON-NLS-2$
    } finally {
      activator = null;
    }
  }

  /**
   * 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 ServiceRegistrationImpl
   * @see ServiceReferenceImpl
   */
  protected ServiceReference[] getRegisteredServices() {
    ServiceReference[] services = null;

    synchronized (framework.serviceRegistry) {
      services = framework.serviceRegistry.lookupServiceReferences(this);
      if (services == null) {
        return null;
      }
      int removed = 0;
      for (int i = services.length - 1; i >= 0; i--) {
        ServiceReferenceImpl ref = (ServiceReferenceImpl) services[i];
        String[] classes = ref.getClasses();
        try { /* test for permission to the classes */
          framework.checkGetServicePermission(classes);
        } catch (SecurityException se) {
          services[i] = null;
          removed++;
        }
      }
      if (removed > 0) {
        ServiceReference[] temp = services;
        services = new ServiceReference[temp.length - removed];
        for (int i = temp.length - 1; i >= 0; i--) {
          if (temp[i] == null)
            removed--;
          else
            services[i - removed] = temp[i];
        }
      }
    }
    return (services);

  }

  /**
   * Provides a list of {@link ServiceReferenceImpl}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 ServiceReferenceImpl} or <code>null</code>.
   * @exception java.lang.IllegalStateException If the
   * bundle has been uninstalled.
   * @see ServiceReferenceImpl
   */
  protected ServiceReferenceImpl[] getServicesInUse() {
    if (servicesInUse == null) {
      return (null);
    }

    synchronized (servicesInUse) {
      int size = servicesInUse.size();

      if (size == 0) {
        return (null);
      }

      ServiceReferenceImpl[] references = new ServiceReferenceImpl[size];
      int refcount = 0;

      Enumeration refsEnum = servicesInUse.keys();

      for (int i = 0; i < size; i++) {
        ServiceReferenceImpl reference = (ServiceReferenceImpl) refsEnum.nextElement();

        try {
          framework.checkGetServicePermission(reference.registration.clazzes);
        } catch (SecurityException se) {
          continue;
        }

        references[refcount] = reference;
        refcount++;
      }

      if (refcount < size) {
        if (refcount == 0) {
          return (null);
        }

        ServiceReferenceImpl[] refs = references;
        references = new ServiceReferenceImpl[refcount];

        System.arraycopy(refs, 0, references, 0, refcount);
      }

      return (references);
    }
  }

  /**
   * 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;
    try {
      if (isValid()) /* if context still valid */{
        switch (action) {
          case Framework.BUNDLEEVENT :
          case Framework.BUNDLEEVENTSYNC : {
            BundleListener listener = (BundleListener) l;

            if (Debug.DEBUG && 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 Framework.SERVICEEVENT : {
            ServiceEvent event = (ServiceEvent) object;

            ServiceListener listener = (ServiceListener) l;
            if (Debug.DEBUG && 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 && 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;
          }
        }
      }
    } catch (Throwable t) {
      if (Debug.DEBUG && 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);
      }
    }
  }

  /**
   * Check for permission to listen to a service.
   */
  protected boolean hasListenServicePermission(ServiceEvent event) {
    ProtectionDomain domain = bundle.getProtectionDomain();

    if (domain != null) {
      ServiceReferenceImpl reference = (ServiceReferenceImpl) event.getServiceReference();

      String[] names = reference.getClasses();

      int len = names.length;

      for (int i = 0; i < len; i++) {
        if (domain.implies(new ServicePermission(names[i], ServicePermission.GET))) {
          return true;
        }
      }

      return false;
    }

    return (true);
  }

  /**
   * 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 org.osgi.framework.Filter createFilter(String filter) throws InvalidSyntaxException {
    checkValid();

    return (new FilterImpl(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.
   */
  protected 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;
  }

  boolean isAssignableTo(ServiceReferenceImpl reference) {
    if (!scopeEvents)
      return true;
    String[] clazzes = reference.getClasses();
    for (int i = 0; i < clazzes.length; i++)
      if (!reference.isAssignableTo(bundle, clazzes[i]))
        return false;
    return true;
  }
}
TOP

Related Classes of org.eclipse.osgi.framework.internal.core.BundleContextImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.