| /******************************************************************************* |
| * Copyright (c) 2003,2004 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Common Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/cpl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.osgi.framework.internal.core; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.net.URL; |
| import java.net.URLConnection; |
| import java.security.*; |
| import java.util.*; |
| import org.eclipse.osgi.framework.adaptor.BundleData; |
| import org.eclipse.osgi.framework.adaptor.BundleOperation; |
| import org.eclipse.osgi.framework.debug.Debug; |
| import org.eclipse.osgi.service.resolver.*; |
| import org.osgi.framework.*; |
| /** |
| * This object is given out to bundles and wraps the internal Bundle object. It |
| * is destroyed when a bundle is uninstalled and reused if a bundle is updated. |
| * This class is abstract and is extended by BundleHost and BundleFragment. |
| */ |
| public abstract class AbstractBundle implements Bundle, Comparable, KeyedElement { |
| /** The Framework this bundle is part of */ |
| protected Framework framework; |
| /** The state of the bundle. */ |
| protected volatile int state; |
| /** A flag to denote whether a bundle state change is in progress */ |
| protected volatile Thread stateChanging; |
| /** Bundle's BundleData object */ |
| protected BundleData bundledata; |
| /** Internal object used for state change synchronization */ |
| protected Object statechangeLock = new Object(); |
| /** ProtectionDomain for the bundle */ |
| protected ProtectionDomain domain; |
| |
| /** |
| * This String captures the dependencies that could not be resolved |
| * as a result of not having the proper permissions. |
| * This information is collected by checkPermissions, but an exception |
| * cannot be thrown during the resolve phase. It is saved here to be thrown |
| * later (by Bundle.start for example). |
| */ |
| protected String permissionMsg; |
| protected ManifestLocalization manifestLocalization = null; |
| /** |
| * Bundle object constructor. This constructor should not perform any real |
| * work. |
| * |
| * @param bundledata |
| * BundleData for this bundle |
| * @param framework |
| * Framework this bundle is running in |
| */ |
| protected static AbstractBundle createBundle(BundleData bundledata, Framework framework) throws BundleException { |
| if (bundledata.isFragment()) |
| return new BundleFragment(bundledata, framework); |
| else |
| return new BundleHost(bundledata, framework); |
| } |
| /** |
| * Bundle object constructor. This constructor should not perform any real |
| * work. |
| * |
| * @param bundledata |
| * BundleData for this bundle |
| * @param framework |
| * Framework this bundle is running in |
| */ |
| protected AbstractBundle(BundleData bundledata, Framework framework) throws BundleException { |
| state = INSTALLED; |
| stateChanging = null; |
| this.bundledata = bundledata; |
| this.framework = framework; |
| bundledata.setBundle(this); |
| } |
| /** |
| * Load the bundle. |
| * |
| * @exception org.osgi.framework.BundleException |
| */ |
| protected abstract void load() throws BundleException; |
| /** |
| * 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 |
| * @exception org.osgi.framework.BundleException |
| */ |
| protected abstract boolean reload(AbstractBundle newBundle) throws BundleException; |
| /** |
| * Refresh the bundle. This is called by Framework.refreshPackages. This |
| * method must be called while holding the bundles lock. |
| * this.loader.unimportPackages must have already been called before |
| * calling this method! |
| * |
| * @exception org.osgi.framework.BundleException |
| * if an exported package is "in use". i.e. it has been |
| * imported by a bundle |
| */ |
| protected abstract void refresh() throws BundleException; |
| /** |
| * Unload the bundle. This method must be called while holding the bundles |
| * lock. |
| * |
| * @return true if an exported package is "in use". i.e. it has been |
| * imported by a bundle |
| */ |
| protected abstract boolean unload(); |
| /** |
| * Close the the Bundle's file. |
| * |
| */ |
| protected void close() { |
| if (Debug.DEBUG && Debug.DEBUG_GENERAL) { |
| if ((state & (INSTALLED)) == 0) { |
| Debug.println("Bundle.close called when state != INSTALLED: " + this); |
| Debug.printStackTrace(new Exception("Stack trace")); |
| } |
| } |
| state = UNINSTALLED; |
| } |
| /** |
| * Load and instantiate bundle's BundleActivator class |
| */ |
| protected BundleActivator loadBundleActivator() throws BundleException { |
| /* load Bundle's BundleActivator if it has one */ |
| String activatorClassName = bundledata.getActivator(); |
| if (activatorClassName != null) { |
| try { |
| Class activatorClass = loadClass(activatorClassName, false); |
| /* Create the activator for the bundle */ |
| return (BundleActivator) (activatorClass.newInstance()); |
| } catch (Throwable t) { |
| if (Debug.DEBUG && Debug.DEBUG_GENERAL) { |
| Debug.printStackTrace(t); |
| } |
| throw new BundleException(Msg.formatter.getString("BUNDLE_INVALID_ACTIVATOR_EXCEPTION", activatorClassName, bundledata.getSymbolicName()), t); |
| } |
| } |
| return (null); |
| } |
| /** |
| * This method loads a class from the bundle. |
| * |
| * @param name |
| * the name of the desired Class. |
| * @param checkPermission |
| * indicates whether a permission check should be done. |
| * @return the resulting Class |
| * @exception java.lang.ClassNotFoundException |
| * if the class definition was not found. |
| */ |
| protected abstract Class loadClass(String name, boolean checkPermission) throws ClassNotFoundException; |
| /** |
| * 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 abstract URL getResource(String name); |
| /** |
| * Returns the current state of the bundle. |
| * |
| * A bundle can only be in one state at any time. |
| * |
| * @return bundle's state. |
| */ |
| public int getState() { |
| return (state); |
| } |
| /** |
| * Return true if the bundle is starting or active. |
| * |
| */ |
| protected boolean isActive() { |
| return ((state & (ACTIVE | STARTING)) != 0); |
| } |
| /** |
| * Return true if the bundle is resolved. |
| * |
| */ |
| protected boolean isResolved() { |
| return (state & (INSTALLED | UNINSTALLED)) == 0; |
| } |
| /** |
| * Start this bundle. |
| * |
| * If the current start level is less than this bundle's start level, then |
| * the Framework must persistently mark this bundle as started and delay |
| * the starting of this bundle until the Framework's current start level |
| * becomes equal or more than the bundle's start level. |
| * <p> |
| * Otherwise, the following steps are required to start a bundle: |
| * <ol> |
| * <li>If the bundle is {@link #UNINSTALLED}then an <code>IllegalStateException</code> |
| * is thrown. |
| * <li>If the bundle is {@link #ACTIVE}or {@link #STARTING}then this |
| * method returns immediately. |
| * <li>If the bundle is {@link #STOPPING}then this method may wait for |
| * the bundle to return to the {@link #RESOLVED}state before continuing. |
| * If this does not occur in a reasonable time, a {@link BundleException} |
| * is thrown to indicate the bundle was unable to be started. |
| * <li>If the bundle is not {@link #RESOLVED}, an attempt is made to |
| * resolve the bundle. If the bundle cannot be resolved, a |
| * {@link BundleException}is thrown. |
| * <li>The state of the bundle is set to {@link #STARTING}. |
| * <li>The {@link BundleActivator#start(BundleContext) start}method of the bundle's |
| * {@link BundleActivator}, if one is specified, is called. If the |
| * {@link BundleActivator}is invalid or throws an exception, the state of |
| * the bundle is set back to {@link #RESOLVED}, the bundle's listeners, if |
| * any, are removed, service's registered by the bundle, if any, are |
| * unregistered, and service's used by the bundle, if any, are released. A |
| * {@link BundleException}is then thrown. |
| * <li>It is recorded that this bundle has been started, so that when the |
| * framework is restarted, this bundle will be automatically started. |
| * <li>The state of the bundle is set to {@link #ACTIVE}. |
| * <li>A {@link BundleEvent}of type {@link BundleEvent#STARTED}is |
| * broadcast. |
| * </ol> |
| * |
| * <h5>Preconditons</h5> |
| * <ul> |
| * <li>getState() in {{@link #INSTALLED},{@link #RESOLVED}}. |
| * </ul> |
| * <h5>Postconditons, no exceptions thrown</h5> |
| * <ul> |
| * <li>getState() in {{@link #ACTIVE}}. |
| * <li>{@link BundleActivator#start(BundleContext) BundleActivator.start}has been called |
| * and did not throw an exception. |
| * </ul> |
| * <h5>Postconditions, when an exception is thrown</h5> |
| * <ul> |
| * <li>getState() not in {{@link #STARTING},{@link #ACTIVE}}. |
| * </ul> |
| * |
| * @exception BundleException |
| * If the bundle couldn't be started. This could be because |
| * a code dependency could not be resolved or the specified |
| * BundleActivator could not be loaded or threw an |
| * exception. |
| * @exception java.lang.IllegalStateException |
| * If the bundle has been uninstalled or the bundle tries to |
| * change its own state. |
| * @exception java.lang.SecurityException |
| * If the caller does not have {@link AdminPermission} |
| * permission and the Java runtime environment supports |
| * permissions. |
| */ |
| public void start() throws BundleException { |
| long start = 0; |
| if (Debug.DEBUG && Debug.DEBUG_BUNDLE_TIME) { |
| start = System.currentTimeMillis(); |
| System.out.println("Starting " + getSymbolicName()); |
| } |
| framework.checkAdminPermission(); |
| checkValid(); |
| beginStateChange(); |
| try { |
| startWorker(true); |
| } finally { |
| completeStateChange(); |
| } |
| if (Debug.DEBUG && Debug.DEBUG_BUNDLE_TIME) |
| System.out.println("End starting " + getSymbolicName() + " " + (System.currentTimeMillis() - start)); |
| } |
| /** |
| * Internal worker to start a bundle. |
| * |
| * @param persistent |
| * if true persistently record the bundle was started. |
| */ |
| protected abstract void startWorker(boolean persistent) throws BundleException; |
| /** |
| * Start this bundle w/o marking is persistently started. |
| * |
| * <p> |
| * The following steps are followed to start a bundle: |
| * <ol> |
| * <li>If the bundle is {@link #UNINSTALLED}then an <code>IllegalStateException</code> |
| * is thrown. |
| * <li>If the bundle is {@link #ACTIVE}or {@link #STARTING}then this |
| * method returns immediately. |
| * <li>If the bundle is {@link #STOPPING}then this method may wait for |
| * the bundle to return to the {@link #RESOLVED}state before continuing. |
| * If this does not occur in a reasonable time, a {@link BundleException} |
| * is thrown to indicate the bundle was unable to be started. |
| * <li>If the bundle is not {@link #RESOLVED}, an attempt is made to |
| * resolve the bundle. If the bundle cannot be resolved, a |
| * {@link BundleException}is thrown. |
| * <li>The state of the bundle is set to {@link #STARTING}. |
| * <li>The {@link BundleActivator#start(BundleContext) start}method of the bundle's |
| * {@link BundleActivator}, if one is specified, is called. If the |
| * {@link BundleActivator}is invalid or throws an exception, the state of |
| * the bundle is set back to {@link #RESOLVED}, the bundle's listeners, if |
| * any, are removed, service's registered by the bundle, if any, are |
| * unregistered, and service's used by the bundle, if any, are released. A |
| * {@link BundleException}is then thrown. |
| * <li>The state of the bundle is set to {@link #ACTIVE}. |
| * <li>A {@link BundleEvent}of type {@link BundleEvent#STARTED}is |
| * broadcast. |
| * </ol> |
| * |
| * <h5>Preconditons</h5> |
| * <ul> |
| * <li>getState() in {{@link #INSTALLED},{@link #RESOLVED}}. |
| * </ul> |
| * <h5>Postconditons, no exceptions thrown</h5> |
| * <ul> |
| * <li>getState() in {{@link #ACTIVE}}. |
| * <li>{@link BundleActivator#start(BundleContext) BundleActivator.start}has been called |
| * and did not throw an exception. |
| * </ul> |
| * <h5>Postconditions, when an exception is thrown</h5> |
| * <ul> |
| * <li>getState() not in {{@link #STARTING},{@link #ACTIVE}}. |
| * </ul> |
| * |
| * @exception BundleException |
| * If the bundle couldn't be started. This could be because |
| * a code dependency could not be resolved or the specified |
| * BundleActivator could not be loaded or threw an |
| * exception. |
| * @exception java.lang.IllegalStateException |
| * If the bundle tries to change its own state. |
| */ |
| protected void resume() throws BundleException { |
| if (state == UNINSTALLED) { |
| return; |
| } |
| beginStateChange(); |
| try { |
| startWorker(false); |
| } finally { |
| completeStateChange(); |
| } |
| } |
| /** |
| * Stop this bundle. |
| * |
| * Any services registered by this bundle will be unregistered. Any |
| * services used by this bundle will be released. Any listeners registered |
| * by this bundle will be removed. |
| * |
| * <p> |
| * The following steps are followed to stop a bundle: |
| * <ol> |
| * <li>If the bundle is {@link #UNINSTALLED}then an <code>IllegalStateException</code> |
| * is thrown. |
| * <li>If the bundle is {@link #STOPPING},{@link #RESOLVED}, or |
| * {@link #INSTALLED}then this method returns immediately. |
| * <li>If the bundle is {@link #STARTING}then this method may wait for |
| * the bundle to reach the {@link #ACTIVE}state before continuing. If this |
| * does not occur in a reasonable time, a {@link BundleException}is thrown |
| * to indicate the bundle was unable to be stopped. |
| * <li>The state of the bundle is set to {@link #STOPPING}. |
| * <li>It is recorded that this bundle has been stopped, so that when the |
| * framework is restarted, this bundle will not be automatically started. |
| * <li>The {@link BundleActivator#stop(BundleContext) stop}method of the bundle's |
| * {@link BundleActivator}, if one is specified, is called. If the |
| * {@link BundleActivator}throws an exception, this method will continue |
| * to stop the bundle. A {@link BundleException}will be thrown after |
| * completion of the remaining steps. |
| * <li>The bundle's listeners, if any, are removed, service's registered |
| * by the bundle, if any, are unregistered, and service's used by the |
| * bundle, if any, are released. |
| * <li>The state of the bundle is set to {@link #RESOLVED}. |
| * <li>A {@link BundleEvent}of type {@link BundleEvent#STOPPED}is |
| * broadcast. |
| * </ol> |
| * |
| * <h5>Preconditons</h5> |
| * <ul> |
| * <li>getState() in {{@link #ACTIVE}}. |
| * </ul> |
| * <h5>Postconditons, no exceptions thrown</h5> |
| * <ul> |
| * <li>getState() not in {{@link #ACTIVE},{@link #STOPPING}}. |
| * <li>{@link BundleActivator#stop(BundleContext) BundleActivator.stop}has been called |
| * and did not throw an exception. |
| * </ul> |
| * <h5>Postconditions, when an exception is thrown</h5> |
| * <ul> |
| * <li>None. |
| * </ul> |
| * |
| * @exception BundleException |
| * If the bundle's BundleActivator could not be loaded or |
| * threw an exception. |
| * @exception java.lang.IllegalStateException |
| * If the bundle has been uninstalled or the bundle tries to |
| * change its own state. |
| * @exception java.lang.SecurityException |
| * If the caller does not have {@link AdminPermission} |
| * permission and the Java runtime environment supports |
| * permissions. |
| */ |
| public void stop() throws BundleException { |
| framework.checkAdminPermission(); |
| checkValid(); |
| beginStateChange(); |
| try { |
| stopWorker(true); |
| } finally { |
| completeStateChange(); |
| } |
| } |
| /** |
| * Internal worker to stop a bundle. |
| * |
| * @param persistent |
| * if true persistently record the bundle was stopped. |
| */ |
| protected abstract void stopWorker(boolean persistent) throws BundleException; |
| /** |
| * Set the persistent status bit for the bundle. |
| * |
| * @param mask |
| * Mask for bit to set/clear |
| * @param state |
| * true to set bit, false to clear bit |
| */ |
| protected void setStatus(final int mask, final boolean state) { |
| try { |
| AccessController.doPrivileged(new PrivilegedExceptionAction() { |
| public Object run() throws BundleException, IOException { |
| int status = bundledata.getStatus(); |
| boolean test = ((status & mask) != 0); |
| if (test != state) { |
| bundledata.setStatus(state ? (status | mask) : (status & ~mask)); |
| bundledata.save(); |
| } |
| return null; |
| } |
| }); |
| } catch (PrivilegedActionException pae) { |
| framework.publishFrameworkEvent(FrameworkEvent.ERROR, this, pae.getException()); |
| } |
| } |
| /** |
| * Stop this bundle w/o marking is persistently stopped. |
| * |
| * Any services registered by this bundle will be unregistered. Any |
| * services used by this bundle will be released. Any listeners registered |
| * by this bundle will be removed. |
| * |
| * <p> |
| * The following steps are followed to stop a bundle: |
| * <ol> |
| * <li>If the bundle is {@link #UNINSTALLED}then an <code>IllegalStateException</code> |
| * is thrown. |
| * <li>If the bundle is {@link #STOPPING},{@link #RESOLVED}, or |
| * {@link #INSTALLED}then this method returns immediately. |
| * <li>If the bundle is {@link #STARTING}then this method may wait for |
| * the bundle to reach the {@link #ACTIVE}state before continuing. If this |
| * does not occur in a reasonable time, a {@link BundleException}is thrown |
| * to indicate the bundle was unable to be stopped. |
| * <li>The state of the bundle is set to {@link #STOPPING}. |
| * <li>The {@link BundleActivator#stop(BundleContext) stop}method of the bundle's |
| * {@link BundleActivator}, if one is specified, is called. If the |
| * {@link BundleActivator}throws an exception, this method will continue |
| * to stop the bundle. A {@link BundleException}will be thrown after |
| * completion of the remaining steps. |
| * <li>The bundle's listeners, if any, are removed, service's registered |
| * by the bundle, if any, are unregistered, and service's used by the |
| * bundle, if any, are released. |
| * <li>The state of the bundle is set to {@link #RESOLVED}. |
| * <li>A {@link BundleEvent}of type {@link BundleEvent#STOPPED}is |
| * broadcast. |
| * </ol> |
| * |
| * <h5>Preconditons</h5> |
| * <ul> |
| * <li>getState() in {{@link #ACTIVE}}. |
| * </ul> |
| * <h5>Postconditons, no exceptions thrown</h5> |
| * <ul> |
| * <li>getState() not in {{@link #ACTIVE},{@link #STOPPING}}. |
| * <li>{@link BundleActivator#stop(BundleContext) BundleActivator.stop}has been called |
| * and did not throw an exception. |
| * </ul> |
| * <h5>Postconditions, when an exception is thrown</h5> |
| * <ul> |
| * <li>None. |
| * </ul> |
| * |
| * @param lock |
| * true if state change lock should be held when returning from |
| * this method. |
| * @exception BundleException |
| * If the bundle's BundleActivator could not be loaded or |
| * threw an exception. |
| * @exception java.lang.IllegalStateException |
| * If the bundle tries to change its own state. |
| */ |
| protected void suspend(boolean lock) throws BundleException { |
| if (state == UNINSTALLED) { |
| return; |
| } |
| beginStateChange(); |
| try { |
| stopWorker(false); |
| } finally { |
| if (!lock) { |
| completeStateChange(); |
| } |
| } |
| } |
| /** |
| * Update this bundle. If the bundle is {@link #ACTIVE}, the bundle will |
| * be stopped before the update and started after the update successfully |
| * completes. |
| * |
| * <p> |
| * The following steps are followed to update a bundle: |
| * <ol> |
| * <li>If the bundle is {@link #UNINSTALLED}then an <code>IllegalStateException</code> |
| * is thrown. |
| * <li>If the bundle is {@link #ACTIVE}or {@link #STARTING}, the bundle |
| * is stopped as described in the {@link #stop()}method. If {@link #stop()} |
| * throws an exception, the exception is rethrown terminating the update. |
| * <li>The location for the new version of the bundle is determined from |
| * either the manifest header <code>Bundle-UpdateLocation</code> if |
| * available or the original location. |
| * <li>The location is interpreted in an implementation dependent way |
| * (typically as a URL) and the new version of the bundle is obtained from |
| * the location. |
| * <li>The new version of the bundle is installed. If the framework is |
| * unable to install the new version of the bundle, the original version of |
| * the bundle will be restored and a {@link BundleException}will be thrown |
| * after completion of the remaining steps. |
| * <li>The state of the bundle is set to {@link #INSTALLED}. |
| * <li>If the new version of the bundle was successfully installed, a |
| * {@link BundleEvent}of type {@link BundleEvent#UPDATED}is broadcast. |
| * <li>If the bundle was originally {@link #ACTIVE}, the updated bundle |
| * is started as described in the {@link #start()}method. If {@link #start()} |
| * throws an exception, a {@link FrameworkEvent}of type |
| * {@link FrameworkEvent#ERROR}is broadcast containing the exception. |
| * </ol> |
| * |
| * <h5>Preconditions</h5> |
| * <ul> |
| * <li>getState() not in {{@link #UNINSTALLED}}. |
| * </ul> |
| * <h5>Postconditons, no exceptions thrown</h5> |
| * <ul> |
| * <li>getState() in {{@link #INSTALLED},{@link #RESOLVED}, |
| * {@link #ACTIVE}}. |
| * <li>The bundle has been updated. |
| * </ul> |
| * <h5>Postconditions, when an exception is thrown</h5> |
| * <ul> |
| * <li>getState() in {{@link #INSTALLED},{@link #RESOLVED}, |
| * {@link #ACTIVE}}. |
| * <li>Original bundle is still used, no update took place. |
| * </ul> |
| * |
| * @exception BundleException |
| * If the update fails. |
| * @exception java.lang.IllegalStateException |
| * If the bundle has been uninstalled or the bundle tries to |
| * change its own state. |
| * @exception java.lang.SecurityException |
| * If the caller does not have {@link AdminPermission} |
| * permission and the Java runtime environment supports |
| * permissions. |
| * @see #stop() |
| * @see #start() |
| */ |
| public void update() throws BundleException { |
| if (Debug.DEBUG && Debug.DEBUG_GENERAL) { |
| Debug.println("update location " + bundledata.getLocation()); |
| } |
| framework.checkAdminPermission(); |
| checkValid(); |
| beginStateChange(); |
| try { |
| updateWorker(new PrivilegedExceptionAction() { |
| public Object run() throws BundleException { |
| /* compute the update location */ |
| String updateLocation = bundledata.getLocation(); |
| if (bundledata.getManifest().get(Constants.BUNDLE_UPDATELOCATION) != null) { |
| updateLocation = (String) bundledata.getManifest().get(Constants.BUNDLE_UPDATELOCATION); |
| if (Debug.DEBUG && Debug.DEBUG_GENERAL) { |
| Debug.println(" from location: " + updateLocation); |
| } |
| } |
| /* Map the identity to a URLConnection */ |
| URLConnection source = framework.adaptor.mapLocationToURLConnection(updateLocation); |
| /* call the worker */ |
| updateWorkerPrivileged(source); |
| return null; |
| } |
| }); |
| } catch (BundleException e) { |
| if (!(e.getNestedException() instanceof NothingToUpdateException)) { |
| throw e; |
| } |
| //There is no work to be done with this update. |
| //Quietly return with no error. |
| } finally { |
| completeStateChange(); |
| } |
| } |
| /** |
| * Update this bundle from an InputStream. |
| * |
| * <p> |
| * This method performs all the steps listed in {@link #update()}, except |
| * the bundle will be read in through the supplied <code>InputStream</code>, |
| * rather than a <code>URL</code>. |
| * |
| * @param in |
| * The InputStream from which to read the new bundle. |
| * @see #update() |
| */ |
| public void update(final InputStream in) throws BundleException { |
| if (Debug.DEBUG && Debug.DEBUG_GENERAL) { |
| Debug.println("update location " + bundledata.getLocation()); |
| Debug.println(" from: " + in); |
| } |
| framework.checkAdminPermission(); |
| checkValid(); |
| beginStateChange(); |
| try { |
| updateWorker(new PrivilegedExceptionAction() { |
| public Object run() throws BundleException { |
| /* Map the InputStream to a URLConnection */ |
| URLConnection source = new BundleSource(in); |
| /* call the worker */ |
| updateWorkerPrivileged(source); |
| return null; |
| } |
| }); |
| } finally { |
| completeStateChange(); |
| } |
| } |
| /** |
| * Update worker. Assumes the caller has the state change lock. |
| */ |
| protected void updateWorker(PrivilegedExceptionAction action) throws BundleException { |
| boolean bundleActive = false; |
| AbstractBundle host = null; |
| if (isFragment()) { |
| host = (AbstractBundle) getHost(); |
| bundleActive = (host == null ? false : (host.state == ACTIVE)); |
| } else { |
| bundleActive = (state == ACTIVE); |
| } |
| if (bundleActive) { |
| try { |
| if (isFragment()) { |
| host.stopWorker(false); |
| } else { |
| stopWorker(false); |
| } |
| } catch (BundleException e) { |
| framework.publishFrameworkEvent(FrameworkEvent.ERROR, this, e); |
| if (state == ACTIVE) /* if the bundle is still active */{ |
| throw e; |
| } |
| } |
| } |
| try { |
| AccessController.doPrivileged(action); |
| framework.publishBundleEvent(BundleEvent.UPDATED, this); |
| } catch (PrivilegedActionException pae) { |
| throw (BundleException) pae.getException(); |
| } finally { |
| if (bundleActive) { |
| try { |
| if (isFragment()) { |
| host.startWorker(false); |
| } else { |
| startWorker(false); |
| } |
| } catch (BundleException e) { |
| framework.publishFrameworkEvent(FrameworkEvent.ERROR, this, e); |
| } |
| } |
| } |
| } |
| /** |
| * Update worker. Assumes the caller has the state change lock. |
| */ |
| protected void updateWorkerPrivileged(URLConnection source) throws BundleException { |
| AbstractBundle oldBundle = AbstractBundle.createBundle(bundledata, framework); |
| boolean reloaded = false; |
| BundleOperation storage = framework.adaptor.updateBundle(this.bundledata, source); |
| BundleRepository bundles = framework.getBundles(); |
| try { |
| BundleData newBundleData = storage.begin(); |
| // Must call framework createBundle to check execution environment. |
| AbstractBundle newBundle = framework.createBundle(newBundleData); |
| // Check for a bundle already installed with the same symbolicName |
| // and version. |
| String symbolicName = newBundle.getSymbolicName(); |
| AbstractBundle installedBundle = symbolicName == null ? null : framework.getBundleByUniqueId(symbolicName, newBundle.getVersion().toString()); |
| if (installedBundle != null && installedBundle != this) { |
| throw new BundleException(Msg.formatter.getString("BUNDLE_INSTALL_SAME_UNIQUEID", new Object[] {installedBundle.getSymbolicName(), installedBundle.getVersion(), installedBundle.getLocation()})); |
| } |
| String[] nativepaths = framework.selectNativeCode(newBundle); |
| if (nativepaths != null) { |
| bundledata.installNativeCode(nativepaths); |
| } |
| boolean exporting; |
| synchronized (bundles) { |
| bundles.markDependancies(); |
| exporting = reload(newBundle); |
| } |
| reloaded = true; /* |
| * indicate we have loaded from the new version of |
| * the bundle |
| */ |
| storage.commit(exporting); |
| } catch (BundleException e) { |
| try { |
| storage.undo(); |
| if (reloaded) /* |
| * if we loaded from the new version of the |
| * bundle |
| */{ |
| synchronized (bundles) { |
| reload(oldBundle); /* revert to old version */ |
| } |
| } |
| } catch (BundleException ee) { |
| /* if we fail to revert then we are in big trouble */ |
| framework.publishFrameworkEvent(FrameworkEvent.ERROR, this, ee); |
| } |
| throw e; |
| } |
| } |
| /** |
| * Uninstall this bundle. |
| * <p> |
| * This method removes all traces of the bundle, including any data in the |
| * persistent storage area provided for the bundle by the framework. |
| * |
| * <p> |
| * The following steps are followed to uninstall a bundle: |
| * <ol> |
| * <li>If the bundle is {@link #UNINSTALLED}then an <code>IllegalStateException</code> |
| * is thrown. |
| * <li>If the bundle is {@link #ACTIVE}or {@link #STARTING}, the bundle |
| * is stopped as described in the {@link #stop()}method. If {@link #stop()} |
| * throws an exception, a {@link FrameworkEvent}of type |
| * {@link FrameworkEvent#ERROR}is broadcast containing the exception. |
| * <li>A {@link BundleEvent}of type {@link BundleEvent#UNINSTALLED}is |
| * broadcast. |
| * <li>The state of the bundle is set to {@link #UNINSTALLED}. |
| * <li>The bundle and the persistent storage area provided for the bundle |
| * by the framework, if any, is removed. |
| * </ol> |
| * |
| * <h5>Preconditions</h5> |
| * <ul> |
| * <li>getState() not in {{@link #UNINSTALLED}}. |
| * </ul> |
| * <h5>Postconditons, no exceptions thrown</h5> |
| * <ul> |
| * <li>getState() in {{@link #UNINSTALLED}}. |
| * <li>The bundle has been uninstalled. |
| * </ul> |
| * <h5>Postconditions, when an exception is thrown</h5> |
| * <ul> |
| * <li>getState() not in {{@link #UNINSTALLED}}. |
| * <li>The Bundle has not been uninstalled. |
| * </ul> |
| * |
| * @exception BundleException |
| * If the uninstall failed. |
| * @exception java.lang.IllegalStateException |
| * If the bundle has been uninstalled or the bundle tries to |
| * change its own state. |
| * @exception java.lang.SecurityException |
| * If the caller does not have {@link AdminPermission} |
| * permission and the Java runtime environment supports |
| * permissions. |
| * @see #stop() |
| */ |
| public void uninstall() throws BundleException { |
| if (Debug.DEBUG && Debug.DEBUG_GENERAL) { |
| Debug.println("uninstall location: " + bundledata.getLocation()); |
| } |
| framework.checkAdminPermission(); |
| checkValid(); |
| beginStateChange(); |
| try { |
| uninstallWorker(new PrivilegedExceptionAction() { |
| public Object run() throws BundleException { |
| uninstallWorkerPrivileged(); |
| return null; |
| } |
| }); |
| } finally { |
| completeStateChange(); |
| } |
| } |
| /** |
| * Uninstall worker. Assumes the caller has the state change lock. |
| */ |
| protected void uninstallWorker(PrivilegedExceptionAction action) throws BundleException { |
| boolean bundleActive = false; |
| AbstractBundle host = null; |
| if (isFragment()) { |
| host = (AbstractBundle) getHost(); |
| bundleActive = (host == null ? false : (host.state == ACTIVE)); |
| } else { |
| bundleActive = (state == ACTIVE); |
| } |
| if (bundleActive) { |
| try { |
| if (isFragment()) { |
| host.stopWorker(true); |
| } else { |
| stopWorker(true); |
| } |
| } catch (BundleException e) { |
| framework.publishFrameworkEvent(FrameworkEvent.ERROR, this, e); |
| } |
| } |
| try { |
| AccessController.doPrivileged(action); |
| } catch (PrivilegedActionException pae) { |
| if (bundleActive) /* if we stopped the bundle */{ |
| try { |
| if (isFragment()) { |
| host.startWorker(false); |
| } else { |
| startWorker(false); |
| } |
| } catch (BundleException e) { |
| /* |
| * if we fail to start the original bundle then we are in |
| * big trouble |
| */ |
| framework.publishFrameworkEvent(FrameworkEvent.ERROR, this, e); |
| } |
| // set the bundleActive to false so that the finally will not |
| // try |
| // to start the bundle again. |
| bundleActive = false; |
| } |
| throw (BundleException) pae.getException(); |
| } finally { |
| if (isFragment() && bundleActive) { |
| try { |
| host.startWorker(false); |
| } catch (BundleException e) { |
| /* |
| * if we fail to start the original host bundle then we are |
| * in big trouble |
| */ |
| framework.publishFrameworkEvent(FrameworkEvent.ERROR, this, e); |
| } |
| } |
| } |
| framework.publishBundleEvent(BundleEvent.UNINSTALLED, this); |
| } |
| /** |
| * Uninstall worker. Assumes the caller has the state change lock. |
| */ |
| protected void uninstallWorkerPrivileged() throws BundleException { |
| boolean unloaded = false; |
| //cache the bundle's headers |
| getHeaders(); |
| BundleOperation storage = framework.adaptor.uninstallBundle(this.bundledata); |
| BundleRepository bundles = framework.getBundles(); |
| try { |
| storage.begin(); |
| boolean exporting; |
| synchronized (bundles) { |
| bundles.markDependancies(); |
| bundles.remove(this); /* remove before calling unload */ |
| exporting = unload(); |
| } |
| unloaded = true; |
| storage.commit(exporting); |
| close(); |
| } catch (BundleException e) { |
| try { |
| storage.undo(); |
| if (unloaded) /* if we unloaded the bundle */{ |
| synchronized (bundles) { |
| load(); /* reload the bundle */ |
| bundles.add(this); |
| } |
| } |
| } catch (BundleException ee) { |
| /* |
| * if we fail to load the original bundle then we are in big |
| * trouble |
| */ |
| framework.publishFrameworkEvent(FrameworkEvent.ERROR, this, ee); |
| } |
| throw e; |
| } |
| } |
| /** |
| * Return the bundle's manifest headers and values from the manifest's |
| * preliminary section. That is all the manifest's headers and values prior |
| * to the first blank line. |
| * |
| * <p> |
| * Manifest header names are case-insensitive. The methods of the returned |
| * <code>Dictionary</code> object will operate on header names in a |
| * case-insensitive manner. |
| * |
| * <p> |
| * For example, the following manifest headers and values are included if |
| * they are present in the manifest: |
| * |
| * <pre> |
| * Bundle-Name |
| * Bundle-Vendor |
| * Bundle-Version |
| * Bundle-Description |
| * Bundle-DocURL |
| * Bundle-ContactAddress |
| * </pre> |
| * |
| * <p> |
| * This method will continue to return this information when the bundle is |
| * in the {@link #UNINSTALLED}state. |
| * |
| * @return A <code>Dictionary</code> object containing the bundle's |
| * manifest headers and values. |
| * @exception java.lang.SecurityException |
| * If the caller does not have {@link AdminPermission} |
| * permission and the Java runtime environment supports |
| * permissions. |
| */ |
| public Dictionary getHeaders() { |
| return getHeaders(Locale.getDefault().toString()); |
| } |
| /** |
| * Returns this bundle's Manifest headers and values. This method returns |
| * all the Manifest headers and values from the main section of the |
| * bundle's Manifest file; that is, all lines prior to the first blank |
| * line. |
| * |
| * <p> |
| * Manifest header names are case-insensitive. The methods of the returned |
| * <tt>Dictionary</tt> object will operate on header names in a |
| * case-insensitive manner. |
| * |
| * If a Manifest header begins with a '%', it will be evaluated with the |
| * specified properties file for the specied Locale. |
| * |
| * <p> |
| * For example, the following Manifest headers and values are included if |
| * they are present in the Manifest file: |
| * |
| * <pre> |
| * Bundle-Name |
| * Bundle-Vendor |
| * Bundle-Version |
| * Bundle-Description |
| * Bundle-DocURL |
| * Bundle-ContactAddress |
| * </pre> |
| * |
| * <p> |
| * This method will continue to return Manifest header information while |
| * this bundle is in the <tt>UNINSTALLED</tt> state. |
| * |
| * @return A <tt>Dictionary</tt> object containing this bundle's Manifest |
| * headers and values. |
| * |
| * @exception java.lang.SecurityException |
| * If the caller does not have the <tt>AdminPermission</tt>, |
| * and the Java Runtime Environment supports permissions. |
| */ |
| public Dictionary getHeaders(String localeString) { |
| framework.checkAdminPermission(); |
| try { |
| initializeManifestLocalization(); |
| } catch (BundleException e) { |
| framework.publishFrameworkEvent(FrameworkEvent.ERROR, this, e); |
| // return an empty dictinary. |
| return new Hashtable(); |
| } |
| return manifestLocalization.getHeaders(localeString); |
| } |
| /** |
| * Retrieve the bundle's unique identifier, which the framework assigned to |
| * this bundle when it was installed. |
| * |
| * <p> |
| * The unique identifier has the following attributes: |
| * <ul> |
| * <li>It is unique and persistent. |
| * <li>The identifier is a long. |
| * <li>Once its value is assigned to a bundle, that value is not reused |
| * for another bundle, even after the bundle is uninstalled. |
| * <li>Its value does not change as long as the bundle remains installed. |
| * <li>Its value does not change when the bundle is updated |
| * </ul> |
| * |
| * <p> |
| * This method will continue to return the bundle's unique identifier when |
| * the bundle is in the {@link #UNINSTALLED}state. |
| * |
| * @return This bundle's unique identifier. |
| */ |
| public long getBundleId() { |
| return (bundledata.getBundleID()); |
| } |
| /** |
| * Retrieve the location identifier of the bundle. This is typically the |
| * location passed to |
| * {@link BundleContextImpl#installBundle(String) BundleContext.installBundle}when the |
| * bundle was installed. The location identifier of the bundle may change |
| * during bundle update. Calling this method while framework is updating |
| * the bundle results in undefined behavior. |
| * |
| * <p> |
| * This method will continue to return the bundle's location identifier |
| * when the bundle is in the {@link #UNINSTALLED}state. |
| * |
| * @return A string that is the location identifier of the bundle. |
| * @exception java.lang.SecurityException |
| * If the caller does not have {@link AdminPermission} |
| * permission and the Java runtime environment supports |
| * permissions. |
| */ |
| public String getLocation() { |
| framework.checkAdminPermission(); |
| return (bundledata.getLocation()); |
| } |
| /** |
| * Provides a list of {@link ServiceReferenceImpl}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 ServiceReferenceImpl}or <code>null</code>. |
| * @exception java.lang.IllegalStateException |
| * If the bundle has been uninstalled. |
| * @see ServiceRegistrationImpl |
| * @see ServiceReferenceImpl |
| */ |
| public abstract org.osgi.framework.ServiceReference[] getRegisteredServices(); |
| /** |
| * 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 |
| */ |
| public abstract org.osgi.framework.ServiceReference[] getServicesInUse(); |
| /** |
| * Determine whether the bundle has the requested permission. |
| * |
| * <p> |
| * If the Java runtime environment does not supports permissions this |
| * method always returns <code>true</code>. The permission parameter is |
| * of type <code>Object</code> to avoid referencing the <code>java.security.Permission</code> |
| * class directly. This is to allow the framework to be implemented in Java |
| * environments which do not support permissions. |
| * |
| * @param permission |
| * The requested permission. |
| * @return <code>true</code> if the bundle has the requested permission |
| * or <code>false</code> if the bundle does not have the |
| * permission or the permission parameter is not an <code>instanceof java.security.Permission</code>. |
| * @exception java.lang.IllegalStateException |
| * If the bundle has been uninstalled. |
| */ |
| public boolean hasPermission(Object permission) { |
| checkValid(); |
| if (domain != null) { |
| if (permission instanceof Permission) { |
| return domain.implies((Permission) permission); |
| } |
| return false; |
| } |
| return true; |
| } |
| /** |
| * This method marks the bundle's state as changing so that other calls to |
| * start/stop/suspend/update/uninstall can wait until the state change is |
| * complete. If stateChanging is non-null when this method is called, we |
| * will wait for the state change to complete. If the timeout expires |
| * without changing state (this may happen if the state change is back up |
| * our call stack), a BundleException is thrown so that we don't wait |
| * forever. |
| * |
| * A call to this method should be immediately followed by a try block |
| * whose finally block calls completeStateChange(). |
| * |
| * beginStateChange(); try { // change the bundle's state here... } finally { |
| * completeStateChange(); } |
| * |
| * @exception org.osgi.framework.BundleException |
| * if the bundles state is still changing after waiting for |
| * the timeout. |
| */ |
| protected void beginStateChange() throws BundleException { |
| synchronized (statechangeLock) { |
| boolean doubleFault = false; |
| while (true) { |
| if (stateChanging == null) { |
| stateChanging = Thread.currentThread(); |
| return; |
| } |
| if (doubleFault || (stateChanging == Thread.currentThread())) { |
| throw new BundleException(Msg.formatter.getString("BUNDLE_STATE_CHANGE_EXCEPTION",getLocation(),stateChanging.getName())); //$NON-NLS-1$ |
| } |
| try { |
| if (Debug.DEBUG && Debug.DEBUG_GENERAL) { |
| Debug.println(" Waiting for state to change in bundle " + this); //$NON-NLS-1$ |
| } |
| long start = 0; |
| if (Debug.DEBUG) |
| start = System.currentTimeMillis(); |
| statechangeLock.wait(5000); /* |
| * wait for other thread to |
| * finish changing state |
| */ |
| if (Debug.DEBUG) { |
| long end = System.currentTimeMillis(); |
| if (end - start > 0) { |
| System.out.println("Waiting... : " + getSymbolicName() + ' ' + (end - start)); |
| } |
| } |
| } catch (InterruptedException e) { |
| } |
| doubleFault = true; |
| } |
| } |
| } |
| /** |
| * This method completes the bundle state change by setting stateChanging |
| * to null and notifying one waiter that the state change has completed. |
| */ |
| protected void completeStateChange() { |
| synchronized (statechangeLock) { |
| if (stateChanging != null) { |
| stateChanging = null; |
| statechangeLock.notify(); /* |
| * notify one waiting thread that the |
| * state change is complete |
| */ |
| } |
| } |
| } |
| /** |
| * Return a string representation of this bundle. |
| * |
| * @return String |
| */ |
| public String toString() { |
| return (bundledata.getLocation() + " [" + getBundleId() + "]"); |
| } |
| /** |
| * Answers an integer indicating the relative positions of the receiver and |
| * the argument in the natural order of elements of the receiver's class. |
| * |
| * @return int which should be <0 if the receiver should sort before the |
| * argument, 0 if the receiver should sort in the same position as |
| * the argument, and >0 if the receiver should sort after the |
| * argument. |
| * @param obj |
| * another Bundle an object to compare the receiver to |
| * @exception ClassCastException |
| * if the argument can not be converted into something |
| * comparable with the receiver. |
| */ |
| public int compareTo(Object obj) { |
| int slcomp = getStartLevel() - ((AbstractBundle) obj).getStartLevel(); |
| if (slcomp != 0) { |
| return slcomp; |
| } |
| long idcomp = getBundleId() - ((AbstractBundle) obj).getBundleId(); |
| return (idcomp < 0L) ? -1 : ((idcomp > 0L) ? 1 : 0); |
| } |
| /** |
| * This method checks that the bundle is not uninstalled. If the bundle is |
| * uninstalled, an IllegalStateException is thrown. |
| * |
| * @exception java.lang.IllegalStateException |
| * If the bundle is uninstalled. |
| */ |
| protected void checkValid() { |
| if (state == UNINSTALLED) { |
| throw new IllegalStateException(Msg.formatter.getString("BUNDLE_UNINSTALLED_EXCEPTION")); |
| } |
| } |
| /** |
| * Get the bundle's ProtectionDomain. |
| * |
| * @return bundle's ProtectionDomain. |
| */ |
| protected ProtectionDomain getProtectionDomain() { |
| return domain; |
| } |
| /** |
| * The bundle must unresolve the permissions in these packages. |
| * |
| * @param unresolvedPackages |
| * A list of the package which have been unresolved as a result |
| * of a packageRefresh |
| */ |
| protected void unresolvePermissions(Hashtable unresolvedPackages) { |
| if (domain != null) { |
| BundlePermissionCollection collection = (BundlePermissionCollection) domain.getPermissions(); |
| if (Debug.DEBUG && Debug.DEBUG_GENERAL) { |
| Debug.println("Unresolving permissions in bundle " + this); |
| } |
| collection.unresolvePermissions(unresolvedPackages); |
| } |
| } |
| public org.osgi.framework.Bundle[] getFragments() { |
| checkValid(); |
| return null; |
| } |
| public boolean isFragment() { |
| return false; |
| } |
| public org.osgi.framework.Bundle getHost() { |
| checkValid(); |
| return null; |
| } |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.osgi.framework.Bundle#findClass(java.lang.String) |
| */ |
| public Class loadClass(String classname) throws ClassNotFoundException { |
| return loadClass(classname, true); |
| } |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.osgi.framework.Bundle#getResourcePaths(java.lang.String) |
| */ |
| public Enumeration getEntryPaths(final String path) { |
| try { |
| framework.checkAdminPermission(); |
| } catch (SecurityException e) { |
| return null; |
| } |
| checkValid(); |
| if (bundledata == null) { |
| if (Debug.DEBUG && Debug.DEBUG_GENERAL) { |
| Debug.println("Bundle.getResourcePaths(" + path + ") called when bundledata == null: " + this); |
| Debug.printStackTrace(new Exception("Stack trace")); |
| } |
| return (null); |
| } |
| return (Enumeration) AccessController.doPrivileged(new PrivilegedAction() { |
| public Object run() { |
| return bundledata.getEntryPaths(path); |
| } |
| }); |
| } |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.osgi.framework.Bundle#getFile(java.lang.String) |
| */ |
| public URL getEntry(String fileName) { |
| try { |
| framework.checkAdminPermission(); |
| } catch (SecurityException e) { |
| return null; |
| } |
| checkValid(); |
| if (bundledata == null) { |
| if (Debug.DEBUG && Debug.DEBUG_GENERAL) { |
| Debug.println("Bundle.getFile(" + fileName + ") called when bundledata == null: " + this); |
| Debug.printStackTrace(new Exception("Stack trace")); |
| } |
| return (null); |
| } |
| if (System.getSecurityManager() == null) |
| return bundledata.getEntry(fileName); |
| final String ffileName = fileName; |
| return (URL) AccessController.doPrivileged(new PrivilegedAction() { |
| public Object run() { |
| return bundledata.getEntry(ffileName); |
| } |
| }); |
| } |
| public String getSymbolicName() { |
| return bundledata.getSymbolicName(); |
| } |
| public BundleData getBundleData() { |
| return bundledata; |
| } |
| public Version getVersion() { |
| return bundledata.getVersion(); |
| } |
| protected BundleDescription getBundleDescription() { |
| return framework.adaptor.getState().getBundle(getBundleId()); |
| } |
| protected int getStartLevel(){ |
| return bundledata.getStartLevel(); |
| } |
| public abstract BundleLoader getBundleLoader(); |
| /** |
| * Mark this bundle as resolved. |
| */ |
| protected void resolve() { |
| if (domain != null && !checkPermissions()) { |
| state = INSTALLED; |
| return; |
| } |
| if (Debug.DEBUG && Debug.DEBUG_GENERAL) { |
| if ((state & (INSTALLED)) == 0) { |
| Debug.println("Bundle.resolve called when state != INSTALLED: " + this); |
| Debug.printStackTrace(new Exception("Stack trace")); |
| } |
| } |
| if (state == INSTALLED) { |
| state = RESOLVED; |
| // Do not publish RESOLVED event here. This is done by caller |
| // to resolve if appropriate. |
| } |
| } |
| protected abstract boolean unresolve() throws BundleException; |
| /** |
| * Return the current context for this bundle. |
| * |
| * @return BundleContext for this bundle. |
| */ |
| abstract protected BundleContextImpl getContext(); |
| protected String getResolutionFailureMessage() { |
| String defaultMessage = Msg.formatter.getString("BUNDLE_UNRESOLVED_EXCEPTION"); |
| // don't spend time if debug info is not needed |
| if (!Debug.DEBUG) { |
| return defaultMessage; |
| } |
| if (permissionMsg != null) { |
| return permissionMsg; // do not null this field out until a successful resolve is done. |
| } |
| BundleDescription bundleDescription = getBundleDescription(); |
| if (bundleDescription == null) { |
| return defaultMessage; |
| } |
| // just a sanity check - this would be an inconsistency between the |
| // framework and the state |
| if (bundleDescription.isResolved()) { |
| throw new IllegalStateException("bundle *is* resolved"); |
| } |
| VersionConstraint[] unsatisfied = framework.adaptor.getPlatformAdmin().getStateHelper().getUnsatisfiedConstraints(bundleDescription); |
| if (unsatisfied.length == 0) { |
| return Msg.formatter.getString("BUNDLE_UNRESOLVED_NOT_CHOSEN_EXCEPTION"); |
| } |
| StringBuffer missing = new StringBuffer(); |
| for (int i = 0; i < unsatisfied.length; i++) { |
| if (unsatisfied[i] instanceof PackageSpecification) { |
| missing.append(Msg.formatter.getString("BUNDLE_UNRESOLVED_PACKAGE", toString(unsatisfied[i]))); |
| } else if (unsatisfied[i] instanceof BundleSpecification) { |
| missing.append(Msg.formatter.getString("BUNDLE_UNRESOLVED_BUNDLE", toString(unsatisfied[i]))); |
| } else { |
| missing.append(Msg.formatter.getString("BUNDLE_UNRESOLVED_HOST", toString(unsatisfied[i]))); |
| } |
| missing.append(','); |
| } |
| missing.deleteCharAt(missing.length() - 1); |
| return Msg.formatter.getString("BUNDLE_UNRESOLVED_UNSATISFIED_CONSTRAINT_EXCEPTION", missing.toString()); |
| } |
| private String toString(VersionConstraint constraint) { |
| org.eclipse.osgi.service.resolver.Version versionSpec = constraint.getVersionSpecification(); |
| if (versionSpec == null) |
| return constraint.getName(); |
| return constraint.getName() + '_' + versionSpec; |
| } |
| public int getKeyHashCode() { |
| return (int) getBundleId(); |
| } |
| public boolean compare(KeyedElement other) { |
| return getBundleId() == ((AbstractBundle) other).getBundleId(); |
| } |
| public Object getKey() { |
| return new Long(getBundleId()); |
| } |
| protected boolean checkPermissions() { |
| permissionMsg = null; |
| BundleDescription bundleDesc = getBundleDescription(); |
| if (bundleDesc == null) |
| return false; |
| PackageSpecification[] pkgs = bundleDesc.getPackages(); |
| for (int i = 0; i < pkgs.length; i++) { |
| // check to make sure the exporter has permissions |
| BundleDescription supplier = pkgs[i].getSupplier(); |
| AbstractBundle supplierBundle = supplier == null ? null : framework.getBundle(supplier.getBundleId()); |
| if (supplierBundle == null || !supplierBundle.checkExportPackagePermission(pkgs[i].getName())) { |
| permissionMsg = Msg.formatter.getString("BUNDLE_PERMISSION_EXCEPTION_EXPORT", supplierBundle, pkgs[i].getName()); |
| return false; |
| } |
| // check to make sure the importer has permissions |
| if (!checkImportPackagePermission(pkgs[i].getName())) { |
| permissionMsg = Msg.formatter.getString("BUNDLE_PERMISSION_EXCEPTION_IMPORT", this, pkgs[i].getName()); |
| return false; |
| } |
| } |
| BundleSpecification[] bundles = bundleDesc.getRequiredBundles(); |
| for (int i = 0; i < bundles.length; i++) { |
| // check to make sure the provider has permissions |
| BundleDescription supplier = bundles[i].getSupplier(); |
| AbstractBundle supplierBundle = supplier == null ? null : framework.getBundle(supplier.getBundleId()); |
| if (supplierBundle == null || !supplierBundle.checkProvideBundlePermission(bundles[i].getName())) { |
| permissionMsg = Msg.formatter.getString("BUNDLE_PERMISSION_EXCEPTION_PROVIDE", supplierBundle, bundles[i].getName()); |
| return false; |
| } |
| // check to make sure the requirer has permissions |
| if (!checkRequireBundlePermission(bundles[i].getName())) { |
| permissionMsg = Msg.formatter.getString("BUNDLE_PERMISSION_EXCEPTION_REQUIRE", this, bundles[i].getName()); |
| return false; |
| } |
| } |
| HostSpecification host = bundleDesc.getHost(); |
| if (host != null) { |
| // check to make sure the host has permissions |
| BundleDescription supplier = host.getSupplier(); |
| AbstractBundle supplierBundle = supplier == null ? null : framework.getBundle(supplier.getBundleId()); |
| if (supplierBundle == null || !supplierBundle.checkFragmentHostPermission(host.getName())) { |
| permissionMsg = Msg.formatter.getString("BUNDLE_PERMISSION_EXCEPTION_HOST", supplierBundle, host.getName()); |
| return false; |
| } |
| // check to make sure the fragment has permissions |
| if (!checkFragmentBundlePermission(host.getName())) { |
| permissionMsg = Msg.formatter.getString("BUNDLE_PERMISSION_EXCEPTION_FRAGMENT", this, host.getName()); |
| return false; |
| } |
| } |
| return true; |
| } |
| protected boolean checkExportPackagePermission(String pkgName) { |
| if (domain != null) |
| return domain.implies(new PackagePermission(pkgName, PackagePermission.EXPORT)); |
| return true; |
| } |
| protected boolean checkProvideBundlePermission(String symbolicName) { |
| if (domain != null) |
| return domain.implies(new BundlePermission(symbolicName, BundlePermission.PROVIDE_BUNDLE)); |
| return true; |
| } |
| protected boolean checkImportPackagePermission(String pkgName) { |
| if (domain != null) |
| return domain.implies(new PackagePermission(pkgName, PackagePermission.IMPORT)); |
| return true; |
| } |
| protected boolean checkRequireBundlePermission(String symbolicName) { |
| if (domain != null) |
| return domain.implies(new BundlePermission(symbolicName, BundlePermission.REQUIRE_BUNDLE)); |
| return true; |
| } |
| protected boolean checkFragmentHostPermission(String symbolicName) { |
| if (domain != null) |
| return domain.implies(new BundlePermission(symbolicName, BundlePermission.FRAGMENT_HOST)); |
| return true; |
| } |
| protected boolean checkFragmentBundlePermission(String symbolicName) { |
| if (domain != null) |
| return domain.implies(new BundlePermission(symbolicName, BundlePermission.FRAGMENT_BUNDLE)); |
| return true; |
| } |
| /* This method is used by the Bundle Localization Service to obtain |
| * a ResourceBundle that resides in a bundle. This is not an OSGi |
| * defined method for org.osgi.framework.Bundle |
| * |
| */ |
| public ResourceBundle getResourceBundle(String localeString) { |
| try { |
| initializeManifestLocalization(); |
| } catch (BundleException ex) { |
| return (null); |
| } |
| if (localeString == null) { |
| localeString = Locale.getDefault().toString(); |
| } |
| return manifestLocalization.getResourceBundle(localeString); |
| } |
| private void initializeManifestLocalization() throws BundleException { |
| if (manifestLocalization == null) { |
| Dictionary rawHeaders; |
| rawHeaders = bundledata.getManifest(); |
| manifestLocalization = new ManifestLocalization(this, rawHeaders); |
| } |
| } |
| public boolean testStateChanging(Object thread) { |
| return stateChanging == thread; |
| } |
| public Object getStateChangeLock() { |
| return statechangeLock; |
| } |
| public Thread getStateChanging() { |
| return stateChanging; |
| } |
| } |