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

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

/*******************************************************************************
* 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;
  }

}
TOP

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

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.