| /******************************************************************************* |
| * Copyright (c) 2003, 2010 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.osgi.framework.internal.core; |
| |
| import java.io.*; |
| import java.net.URL; |
| import java.net.URLConnection; |
| import java.security.*; |
| import java.security.cert.Certificate; |
| import java.util.*; |
| import org.eclipse.osgi.framework.adaptor.*; |
| import org.eclipse.osgi.framework.debug.Debug; |
| import org.eclipse.osgi.framework.util.KeyedElement; |
| import org.eclipse.osgi.internal.composite.CompositeImpl; |
| import org.eclipse.osgi.internal.composite.SurrogateImpl; |
| import org.eclipse.osgi.internal.loader.BundleLoader; |
| import org.eclipse.osgi.internal.permadmin.EquinoxSecurityManager; |
| import org.eclipse.osgi.service.resolver.BundleDescription; |
| import org.eclipse.osgi.service.resolver.ResolverError; |
| import org.eclipse.osgi.signedcontent.*; |
| import org.eclipse.osgi.util.NLS; |
| import org.osgi.framework.*; |
| import org.osgi.framework.startlevel.BundleStartLevel; |
| |
| /** |
| * This object is given out to bundles and wraps the internal Bundle object. It |
| * is destroyed when a bundle is uninstalled and reused if a bundle is updated. |
| * This class is abstract and is extended by BundleHost and BundleFragment. |
| */ |
| public abstract class AbstractBundle implements Bundle, Comparable<Bundle>, KeyedElement, BundleStartLevel { |
| /** The Framework this bundle is part of */ |
| protected final Framework framework; |
| /** The state of the bundle. */ |
| protected volatile int state; |
| /** A flag to denote whether a bundle state change is in progress */ |
| protected volatile Thread stateChanging; |
| /** Bundle's BundleData object */ |
| protected BundleData bundledata; |
| /** Internal object used for state change synchronization */ |
| protected final Object statechangeLock = new Object(); |
| /** ProtectionDomain for the bundle */ |
| protected BundleProtectionDomain domain; |
| |
| volatile protected ManifestLocalization manifestLocalization = null; |
| |
| /** |
| * Bundle object constructor. This constructor should not perform any real |
| * work. |
| * |
| * @param bundledata |
| * BundleData for this bundle |
| * @param framework |
| * Framework this bundle is running in |
| */ |
| protected static AbstractBundle createBundle(BundleData bundledata, Framework framework, boolean setBundle) throws BundleException { |
| AbstractBundle result; |
| if ((bundledata.getType() & BundleData.TYPE_FRAGMENT) > 0) |
| result = new BundleFragment(bundledata, framework); |
| else if ((bundledata.getType() & BundleData.TYPE_COMPOSITEBUNDLE) > 0) |
| result = new CompositeImpl(bundledata, framework); |
| else if ((bundledata.getType() & BundleData.TYPE_SURROGATEBUNDLE) > 0) |
| result = new SurrogateImpl(bundledata, framework); |
| else |
| result = new BundleHost(bundledata, framework); |
| if (setBundle) |
| bundledata.setBundle(result); |
| return result; |
| } |
| |
| /** |
| * Bundle object constructor. This constructor should not perform any real |
| * work. |
| * |
| * @param bundledata |
| * BundleData for this bundle |
| * @param framework |
| * Framework this bundle is running in |
| */ |
| protected AbstractBundle(BundleData bundledata, Framework framework) { |
| state = INSTALLED; |
| stateChanging = null; |
| this.bundledata = bundledata; |
| this.framework = framework; |
| } |
| |
| /** |
| * Load the bundle. |
| */ |
| protected abstract void load(); |
| |
| /** |
| * Reload from a new bundle. This method must be called while holding the |
| * bundles lock. |
| * |
| * @param newBundle |
| * Dummy Bundle which contains new data. |
| * @return true if an exported package is "in use". i.e. it has been |
| * imported by a bundle |
| */ |
| protected abstract boolean reload(AbstractBundle newBundle); |
| |
| /** |
| * Refresh the bundle. This is called by Framework.refreshPackages. This |
| * method must be called while holding the bundles lock. |
| * this.loader.unimportPackages must have already been called before |
| * calling this method! |
| */ |
| protected abstract void refresh(); |
| |
| /** |
| * Unload the bundle. This method must be called while holding the bundles |
| * lock. |
| * |
| * @return true if an exported package is "in use". i.e. it has been |
| * imported by a bundle |
| */ |
| protected abstract boolean unload(); |
| |
| /** |
| * Close the the Bundle's file. |
| * |
| */ |
| protected void close() { |
| if (Debug.DEBUG_GENERAL) { |
| if ((state & (INSTALLED)) == 0) { |
| Debug.println("Bundle.close called when state != INSTALLED: " + this); //$NON-NLS-1$ |
| Debug.printStackTrace(new Exception("Stack trace")); //$NON-NLS-1$ |
| } |
| } |
| state = UNINSTALLED; |
| } |
| |
| /** |
| * Load and instantiate bundle's BundleActivator class |
| */ |
| protected BundleActivator loadBundleActivator() throws BundleException { |
| /* load Bundle's BundleActivator if it has one */ |
| String activatorClassName = bundledata.getActivator(); |
| if (activatorClassName != null) { |
| try { |
| Class activatorClass = loadClass(activatorClassName, false); |
| /* Create the activator for the bundle */ |
| return (BundleActivator) (activatorClass.newInstance()); |
| } catch (Throwable t) { |
| if (Debug.DEBUG_GENERAL) { |
| Debug.printStackTrace(t); |
| } |
| throw new BundleException(NLS.bind(Msg.BUNDLE_INVALID_ACTIVATOR_EXCEPTION, activatorClassName, bundledata.getSymbolicName()), BundleException.ACTIVATOR_ERROR, t); |
| } |
| } |
| return (null); |
| } |
| |
| /** |
| * This method loads a class from the bundle. |
| * |
| * @param name |
| * the name of the desired Class. |
| * @param checkPermission |
| * indicates whether a permission check should be done. |
| * @return the resulting Class |
| * @exception java.lang.ClassNotFoundException |
| * if the class definition was not found. |
| */ |
| protected abstract Class loadClass(String name, boolean checkPermission) throws ClassNotFoundException; |
| |
| /** |
| * Returns the current state of the bundle. |
| * |
| * A bundle can only be in one state at any time. |
| * |
| * @return bundle's state. |
| */ |
| public int getState() { |
| return (state); |
| } |
| |
| public Framework getFramework() { |
| return framework; |
| } |
| |
| /** |
| * Return true if the bundle is starting or active. |
| * |
| */ |
| protected boolean isActive() { |
| return ((state & (ACTIVE | STARTING)) != 0); |
| } |
| |
| boolean isLazyStart() { |
| int status = bundledata.getStatus(); |
| return (status & Constants.BUNDLE_ACTIVATION_POLICY) != 0 && (status & Constants.BUNDLE_LAZY_START) != 0; |
| } |
| |
| /** |
| * Return true if the bundle is resolved. |
| * |
| */ |
| public boolean isResolved() { |
| return (state & (INSTALLED | UNINSTALLED)) == 0; |
| } |
| |
| /** |
| * Start this bundle. |
| * |
| * If the current start level is less than this bundle's start level, then |
| * the Framework must persistently mark this bundle as started and delay |
| * the starting of this bundle until the Framework's current start level |
| * becomes equal or more than the bundle's start level. |
| * <p> |
| * Otherwise, the following steps are required to start a bundle: |
| * <ol> |
| * <li>If the bundle is {@link #UNINSTALLED}then an <code>IllegalStateException</code> |
| * is thrown. |
| * <li>If the bundle is {@link #ACTIVE}or {@link #STARTING}then this |
| * method returns immediately. |
| * <li>If the bundle is {@link #STOPPING}then this method may wait for |
| * the bundle to return to the {@link #RESOLVED}state before continuing. |
| * If this does not occur in a reasonable time, a {@link BundleException} |
| * is thrown to indicate the bundle was unable to be started. |
| * <li>If the bundle is not {@link #RESOLVED}, an attempt is made to |
| * resolve the bundle. If the bundle cannot be resolved, a |
| * {@link BundleException}is thrown. |
| * <li>The state of the bundle is set to {@link #STARTING}. |
| * <li>The {@link BundleActivator#start(BundleContext) start}method of the bundle's |
| * {@link BundleActivator}, if one is specified, is called. If the |
| * {@link BundleActivator}is invalid or throws an exception, the state of |
| * the bundle is set back to {@link #RESOLVED}, the bundle's listeners, if |
| * any, are removed, service's registered by the bundle, if any, are |
| * unregistered, and service's used by the bundle, if any, are released. A |
| * {@link BundleException}is then thrown. |
| * <li>It is recorded that this bundle has been started, so that when the |
| * framework is restarted, this bundle will be automatically started. |
| * <li>The state of the bundle is set to {@link #ACTIVE}. |
| * <li>A {@link BundleEvent}of type {@link BundleEvent#STARTED}is |
| * broadcast. |
| * </ol> |
| * |
| * <h5>Preconditons</h5> |
| * <ul> |
| * <li>getState() in {{@link #INSTALLED},{@link #RESOLVED}}. |
| * </ul> |
| * <h5>Postconditons, no exceptions thrown</h5> |
| * <ul> |
| * <li>getState() in {{@link #ACTIVE}}. |
| * <li>{@link BundleActivator#start(BundleContext) BundleActivator.start}has been called |
| * and did not throw an exception. |
| * </ul> |
| * <h5>Postconditions, when an exception is thrown</h5> |
| * <ul> |
| * <li>getState() not in {{@link #STARTING},{@link #ACTIVE}}. |
| * </ul> |
| * |
| * @exception BundleException |
| * If the bundle couldn't be started. This could be because |
| * a code dependency could not be resolved or the specified |
| * BundleActivator could not be loaded or threw an |
| * exception. |
| * @exception java.lang.IllegalStateException |
| * If the bundle has been uninstalled or the bundle tries to |
| * change its own state. |
| * @exception java.lang.SecurityException |
| * If the caller does not have {@link AdminPermission} |
| * permission and the Java runtime environment supports |
| * permissions. |
| */ |
| public void start() throws BundleException { |
| start(0); |
| } |
| |
| public void start(int options) throws BundleException { |
| framework.checkAdminPermission(this, AdminPermission.EXECUTE); |
| checkValid(); |
| beginStateChange(); |
| try { |
| startWorker(options); |
| } finally { |
| completeStateChange(); |
| } |
| } |
| |
| /** |
| * Internal worker to start a bundle. |
| * |
| * @param options |
| */ |
| protected abstract void startWorker(int options) throws BundleException; |
| |
| /** |
| * This method does the following |
| * <ol> |
| * <li> Return false if the bundle is a fragment |
| * <li> Return false if the bundle is not at the correct start-level |
| * <li> Return false if the bundle is not persistently marked for start |
| * <li> Return true if the bundle's activation policy is persistently ignored |
| * <li> Return true if the bundle does not define an activation policy |
| * <li> Transition to STARTING state and Fire LAZY_ACTIVATION event |
| * <li> Return false |
| * </ol> |
| * @return true if the bundle should be resumed |
| */ |
| protected boolean readyToResume() { |
| return false; |
| } |
| |
| /** |
| * Start this bundle w/o marking is persistently started. |
| * |
| * <p> |
| * The following steps are followed to start a bundle: |
| * <ol> |
| * <li>If the bundle is {@link #UNINSTALLED}then an <code>IllegalStateException</code> |
| * is thrown. |
| * <li>If the bundle is {@link #ACTIVE}or {@link #STARTING}then this |
| * method returns immediately. |
| * <li>If the bundle is {@link #STOPPING}then this method may wait for |
| * the bundle to return to the {@link #RESOLVED}state before continuing. |
| * If this does not occur in a reasonable time, a {@link BundleException} |
| * is thrown to indicate the bundle was unable to be started. |
| * <li>If the bundle is not {@link #RESOLVED}, an attempt is made to |
| * resolve the bundle. If the bundle cannot be resolved, a |
| * {@link BundleException}is thrown. |
| * <li>The state of the bundle is set to {@link #STARTING}. |
| * <li>The {@link BundleActivator#start(BundleContext) start}method of the bundle's |
| * {@link BundleActivator}, if one is specified, is called. If the |
| * {@link BundleActivator}is invalid or throws an exception, the state of |
| * the bundle is set back to {@link #RESOLVED}, the bundle's listeners, if |
| * any, are removed, service's registered by the bundle, if any, are |
| * unregistered, and service's used by the bundle, if any, are released. A |
| * {@link BundleException}is then thrown. |
| * <li>The state of the bundle is set to {@link #ACTIVE}. |
| * <li>A {@link BundleEvent}of type {@link BundleEvent#STARTED}is |
| * broadcast. |
| * </ol> |
| * |
| * <h5>Preconditons</h5> |
| * <ul> |
| * <li>getState() in {{@link #INSTALLED},{@link #RESOLVED}}. |
| * </ul> |
| * <h5>Postconditons, no exceptions thrown</h5> |
| * <ul> |
| * <li>getState() in {{@link #ACTIVE}}. |
| * <li>{@link BundleActivator#start(BundleContext) BundleActivator.start}has been called |
| * and did not throw an exception. |
| * </ul> |
| * <h5>Postconditions, when an exception is thrown</h5> |
| * <ul> |
| * <li>getState() not in {{@link #STARTING},{@link #ACTIVE}}. |
| * </ul> |
| * |
| * @exception BundleException |
| * If the bundle couldn't be started. This could be because |
| * a code dependency could not be resolved or the specified |
| * BundleActivator could not be loaded or threw an |
| * exception. |
| * @exception java.lang.IllegalStateException |
| * If the bundle tries to change its own state. |
| */ |
| protected void resume() throws BundleException { |
| if (state == UNINSTALLED) { |
| return; |
| } |
| beginStateChange(); |
| try { |
| if (readyToResume()) |
| startWorker(START_TRANSIENT); |
| } finally { |
| completeStateChange(); |
| } |
| } |
| |
| /** |
| * Stop this bundle. |
| * |
| * Any services registered by this bundle will be unregistered. Any |
| * services used by this bundle will be released. Any listeners registered |
| * by this bundle will be removed. |
| * |
| * <p> |
| * The following steps are followed to stop a bundle: |
| * <ol> |
| * <li>If the bundle is {@link #UNINSTALLED}then an <code>IllegalStateException</code> |
| * is thrown. |
| * <li>If the bundle is {@link #STOPPING},{@link #RESOLVED}, or |
| * {@link #INSTALLED}then this method returns immediately. |
| * <li>If the bundle is {@link #STARTING}then this method may wait for |
| * the bundle to reach the {@link #ACTIVE}state before continuing. If this |
| * does not occur in a reasonable time, a {@link BundleException}is thrown |
| * to indicate the bundle was unable to be stopped. |
| * <li>The state of the bundle is set to {@link #STOPPING}. |
| * <li>It is recorded that this bundle has been stopped, so that when the |
| * framework is restarted, this bundle will not be automatically started. |
| * <li>The {@link BundleActivator#stop(BundleContext) stop}method of the bundle's |
| * {@link BundleActivator}, if one is specified, is called. If the |
| * {@link BundleActivator}throws an exception, this method will continue |
| * to stop the bundle. A {@link BundleException}will be thrown after |
| * completion of the remaining steps. |
| * <li>The bundle's listeners, if any, are removed, service's registered |
| * by the bundle, if any, are unregistered, and service's used by the |
| * bundle, if any, are released. |
| * <li>The state of the bundle is set to {@link #RESOLVED}. |
| * <li>A {@link BundleEvent}of type {@link BundleEvent#STOPPED}is |
| * broadcast. |
| * </ol> |
| * |
| * <h5>Preconditons</h5> |
| * <ul> |
| * <li>getState() in {{@link #ACTIVE}}. |
| * </ul> |
| * <h5>Postconditons, no exceptions thrown</h5> |
| * <ul> |
| * <li>getState() not in {{@link #ACTIVE},{@link #STOPPING}}. |
| * <li>{@link BundleActivator#stop(BundleContext) BundleActivator.stop}has been called |
| * and did not throw an exception. |
| * </ul> |
| * <h5>Postconditions, when an exception is thrown</h5> |
| * <ul> |
| * <li>None. |
| * </ul> |
| * |
| * @exception BundleException |
| * If the bundle's BundleActivator could not be loaded or |
| * threw an exception. |
| * @exception java.lang.IllegalStateException |
| * If the bundle has been uninstalled or the bundle tries to |
| * change its own state. |
| * @exception java.lang.SecurityException |
| * If the caller does not have {@link AdminPermission} |
| * permission and the Java runtime environment supports |
| * permissions. |
| */ |
| public void stop() throws BundleException { |
| stop(0); |
| } |
| |
| public void stop(int options) throws BundleException { |
| framework.checkAdminPermission(this, AdminPermission.EXECUTE); |
| checkValid(); |
| beginStateChange(); |
| try { |
| stopWorker(options); |
| } finally { |
| completeStateChange(); |
| } |
| } |
| |
| /** |
| * Internal worker to stop a bundle. |
| * |
| * @param options |
| */ |
| protected abstract void stopWorker(int options) throws BundleException; |
| |
| /** |
| * Set the persistent status bit for the bundle. |
| * |
| * @param mask |
| * Mask for bit to set/clear |
| * @param state |
| * true to set bit, false to clear bit |
| */ |
| protected void setStatus(final int mask, final boolean state) { |
| try { |
| AccessController.doPrivileged(new PrivilegedExceptionAction() { |
| public Object run() throws IOException { |
| int status = bundledata.getStatus(); |
| boolean test = ((status & mask) != 0); |
| if (test != state) { |
| bundledata.setStatus(state ? (status | mask) : (status & ~mask)); |
| bundledata.save(); |
| } |
| return null; |
| } |
| }); |
| } catch (PrivilegedActionException pae) { |
| framework.publishFrameworkEvent(FrameworkEvent.ERROR, this, pae.getException()); |
| } |
| } |
| |
| /** |
| * Stop this bundle w/o marking is persistently stopped. |
| * |
| * Any services registered by this bundle will be unregistered. Any |
| * services used by this bundle will be released. Any listeners registered |
| * by this bundle will be removed. |
| * |
| * <p> |
| * The following steps are followed to stop a bundle: |
| * <ol> |
| * <li>If the bundle is {@link #UNINSTALLED}then an <code>IllegalStateException</code> |
| * is thrown. |
| * <li>If the bundle is {@link #STOPPING},{@link #RESOLVED}, or |
| * {@link #INSTALLED}then this method returns immediately. |
| * <li>If the bundle is {@link #STARTING}then this method may wait for |
| * the bundle to reach the {@link #ACTIVE}state before continuing. If this |
| * does not occur in a reasonable time, a {@link BundleException}is thrown |
| * to indicate the bundle was unable to be stopped. |
| * <li>The state of the bundle is set to {@link #STOPPING}. |
| * <li>The {@link BundleActivator#stop(BundleContext) stop}method of the bundle's |
| * {@link BundleActivator}, if one is specified, is called. If the |
| * {@link BundleActivator}throws an exception, this method will continue |
| * to stop the bundle. A {@link BundleException}will be thrown after |
| * completion of the remaining steps. |
| * <li>The bundle's listeners, if any, are removed, service's registered |
| * by the bundle, if any, are unregistered, and service's used by the |
| * bundle, if any, are released. |
| * <li>The state of the bundle is set to {@link #RESOLVED}. |
| * <li>A {@link BundleEvent}of type {@link BundleEvent#STOPPED}is |
| * broadcast. |
| * </ol> |
| * |
| * <h5>Preconditons</h5> |
| * <ul> |
| * <li>getState() in {{@link #ACTIVE}}. |
| * </ul> |
| * <h5>Postconditons, no exceptions thrown</h5> |
| * <ul> |
| * <li>getState() not in {{@link #ACTIVE},{@link #STOPPING}}. |
| * <li>{@link BundleActivator#stop(BundleContext) BundleActivator.stop}has been called |
| * and did not throw an exception. |
| * </ul> |
| * <h5>Postconditions, when an exception is thrown</h5> |
| * <ul> |
| * <li>None. |
| * </ul> |
| * |
| * @param lock |
| * true if state change lock should be held when returning from |
| * this method. |
| * @exception BundleException |
| * If the bundle's BundleActivator could not be loaded or |
| * threw an exception. |
| * @exception java.lang.IllegalStateException |
| * If the bundle tries to change its own state. |
| */ |
| protected void suspend(boolean lock) throws BundleException { |
| if (state == UNINSTALLED) { |
| return; |
| } |
| beginStateChange(); |
| try { |
| stopWorker(STOP_TRANSIENT); |
| } finally { |
| if (!lock) { |
| completeStateChange(); |
| } |
| } |
| } |
| |
| public void update() throws BundleException { |
| update(null); |
| } |
| |
| public void update(final InputStream in) throws BundleException { |
| if (Debug.DEBUG_GENERAL) { |
| Debug.println("update location " + bundledata.getLocation()); //$NON-NLS-1$ |
| Debug.println(" from: " + in); //$NON-NLS-1$ |
| } |
| framework.checkAdminPermission(this, AdminPermission.LIFECYCLE); |
| if ((bundledata.getType() & (BundleData.TYPE_BOOTCLASSPATH_EXTENSION | BundleData.TYPE_FRAMEWORK_EXTENSION | BundleData.TYPE_EXTCLASSPATH_EXTENSION)) != 0) |
| // need special permission to update extensions |
| framework.checkAdminPermission(this, AdminPermission.EXTENSIONLIFECYCLE); |
| checkValid(); |
| beginStateChange(); |
| try { |
| final AccessControlContext callerContext = AccessController.getContext(); |
| //note AdminPermission is checked again after updated bundle is loaded |
| updateWorker(new PrivilegedExceptionAction() { |
| public Object run() throws BundleException { |
| /* compute the update location */ |
| URLConnection source = null; |
| if (in == null) { |
| String updateLocation = (String) bundledata.getManifest().get(Constants.BUNDLE_UPDATELOCATION); |
| if (updateLocation == null) |
| updateLocation = bundledata.getLocation(); |
| if (Debug.DEBUG_GENERAL) |
| Debug.println(" from location: " + updateLocation); //$NON-NLS-1$ |
| /* Map the update location to a URLConnection */ |
| source = framework.adaptor.mapLocationToURLConnection(updateLocation); |
| } else { |
| /* Map the InputStream to a URLConnection */ |
| source = new BundleSource(in); |
| } |
| /* call the worker */ |
| updateWorkerPrivileged(source, callerContext); |
| return null; |
| } |
| }); |
| } finally { |
| completeStateChange(); |
| } |
| } |
| |
| /** |
| * Update worker. Assumes the caller has the state change lock. |
| */ |
| protected void updateWorker(PrivilegedExceptionAction action) throws BundleException { |
| int previousState = 0; |
| if (!isFragment()) |
| previousState = state; |
| if ((previousState & (ACTIVE | STARTING)) != 0) { |
| try { |
| stopWorker(STOP_TRANSIENT); |
| } catch (BundleException e) { |
| framework.publishFrameworkEvent(FrameworkEvent.ERROR, this, e); |
| if ((state & (ACTIVE | STARTING)) != 0) /* if the bundle is still active */{ |
| throw e; |
| } |
| } |
| } |
| try { |
| AccessController.doPrivileged(action); |
| framework.publishBundleEvent(BundleEvent.UPDATED, this); |
| } catch (PrivilegedActionException pae) { |
| if (pae.getException() instanceof RuntimeException) |
| throw (RuntimeException) pae.getException(); |
| throw (BundleException) pae.getException(); |
| } finally { |
| if ((previousState & (ACTIVE | STARTING)) != 0) { |
| try { |
| startWorker(START_TRANSIENT | ((previousState & STARTING) != 0 ? START_ACTIVATION_POLICY : 0)); |
| } catch (BundleException e) { |
| framework.publishFrameworkEvent(FrameworkEvent.ERROR, this, e); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Update worker. Assumes the caller has the state change lock. |
| */ |
| protected void updateWorkerPrivileged(URLConnection source, AccessControlContext callerContext) throws BundleException { |
| AbstractBundle oldBundle = AbstractBundle.createBundle(bundledata, framework, false); |
| boolean reloaded = false; |
| BundleOperation storage = framework.adaptor.updateBundle(this.bundledata, source); |
| BundleRepository bundles = framework.getBundles(); |
| try { |
| BundleData newBundleData = storage.begin(); |
| // Must call framework createBundle to check execution environment. |
| final AbstractBundle newBundle = framework.createAndVerifyBundle(newBundleData, false); |
| boolean exporting; |
| int st = getState(); |
| synchronized (bundles) { |
| String oldBSN = this.getSymbolicName(); |
| exporting = reload(newBundle); |
| // update this to flush the old BSN/version etc. |
| bundles.update(oldBSN, this); |
| manifestLocalization = null; |
| } |
| // indicate we have loaded from the new version of the bundle |
| reloaded = true; |
| if (System.getSecurityManager() != null) { |
| final boolean extension = (bundledata.getType() & (BundleData.TYPE_BOOTCLASSPATH_EXTENSION | BundleData.TYPE_FRAMEWORK_EXTENSION | BundleData.TYPE_EXTCLASSPATH_EXTENSION)) != 0; |
| // must check for AllPermission before allow a bundle extension to be updated |
| if (extension && !hasPermission(new AllPermission())) |
| throw new BundleException(Msg.BUNDLE_EXTENSION_PERMISSION, BundleException.SECURITY_ERROR, new SecurityException(Msg.BUNDLE_EXTENSION_PERMISSION)); |
| try { |
| AccessController.doPrivileged(new PrivilegedExceptionAction() { |
| public Object run() throws Exception { |
| framework.checkAdminPermission(newBundle, AdminPermission.LIFECYCLE); |
| if (extension) // need special permission to update extension bundles |
| framework.checkAdminPermission(newBundle, AdminPermission.EXTENSIONLIFECYCLE); |
| return null; |
| } |
| }, callerContext); |
| } catch (PrivilegedActionException e) { |
| throw e.getException(); |
| } |
| } |
| // send out unresolved events outside synch block (defect #80610) |
| if (st == RESOLVED) |
| framework.publishBundleEvent(BundleEvent.UNRESOLVED, this); |
| storage.commit(exporting); |
| } catch (Throwable t) { |
| try { |
| storage.undo(); |
| if (reloaded) |
| /* |
| * if we loaded from the new version of the |
| * bundle |
| */{ |
| synchronized (bundles) { |
| String oldBSN = this.getSymbolicName(); |
| reload(oldBundle); |
| // update this to flush the new BSN/version back to the old one etc. |
| bundles.update(oldBSN, this); |
| } |
| } |
| } catch (BundleException ee) { |
| /* if we fail to revert then we are in big trouble */ |
| framework.publishFrameworkEvent(FrameworkEvent.ERROR, this, ee); |
| } |
| if (t instanceof SecurityException) |
| throw (SecurityException) t; |
| if (t instanceof BundleException) |
| throw (BundleException) t; |
| throw new BundleException(t.getMessage(), t); |
| } |
| } |
| |
| /** |
| * Uninstall this bundle. |
| * <p> |
| * This method removes all traces of the bundle, including any data in the |
| * persistent storage area provided for the bundle by the framework. |
| * |
| * <p> |
| * The following steps are followed to uninstall a bundle: |
| * <ol> |
| * <li>If the bundle is {@link #UNINSTALLED}then an <code>IllegalStateException</code> |
| * is thrown. |
| * <li>If the bundle is {@link #ACTIVE}or {@link #STARTING}, the bundle |
| * is stopped as described in the {@link #stop()}method. If {@link #stop()} |
| * throws an exception, a {@link FrameworkEvent}of type |
| * {@link FrameworkEvent#ERROR}is broadcast containing the exception. |
| * <li>A {@link BundleEvent}of type {@link BundleEvent#UNINSTALLED}is |
| * broadcast. |
| * <li>The state of the bundle is set to {@link #UNINSTALLED}. |
| * <li>The bundle and the persistent storage area provided for the bundle |
| * by the framework, if any, is removed. |
| * </ol> |
| * |
| * <h5>Preconditions</h5> |
| * <ul> |
| * <li>getState() not in {{@link #UNINSTALLED}}. |
| * </ul> |
| * <h5>Postconditons, no exceptions thrown</h5> |
| * <ul> |
| * <li>getState() in {{@link #UNINSTALLED}}. |
| * <li>The bundle has been uninstalled. |
| * </ul> |
| * <h5>Postconditions, when an exception is thrown</h5> |
| * <ul> |
| * <li>getState() not in {{@link #UNINSTALLED}}. |
| * <li>The Bundle has not been uninstalled. |
| * </ul> |
| * |
| * @exception BundleException |
| * If the uninstall failed. |
| * @exception java.lang.IllegalStateException |
| * If the bundle has been uninstalled or the bundle tries to |
| * change its own state. |
| * @exception java.lang.SecurityException |
| * If the caller does not have {@link AdminPermission} |
| * permission and the Java runtime environment supports |
| * permissions. |
| * @see #stop() |
| */ |
| public void uninstall() throws BundleException { |
| if (Debug.DEBUG_GENERAL) { |
| Debug.println("uninstall location: " + bundledata.getLocation()); //$NON-NLS-1$ |
| } |
| framework.checkAdminPermission(this, AdminPermission.LIFECYCLE); |
| if ((bundledata.getType() & (BundleData.TYPE_BOOTCLASSPATH_EXTENSION | BundleData.TYPE_FRAMEWORK_EXTENSION | BundleData.TYPE_EXTCLASSPATH_EXTENSION)) != 0) |
| // need special permission to uninstall extensions |
| framework.checkAdminPermission(this, AdminPermission.EXTENSIONLIFECYCLE); |
| checkValid(); |
| beginStateChange(); |
| try { |
| uninstallWorker(new PrivilegedExceptionAction() { |
| 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; |
| if (!isFragment()) |
| bundleActive = (state & (ACTIVE | STARTING)) != 0; |
| if (bundleActive) { |
| try { |
| stopWorker(STOP_TRANSIENT); |
| } catch (BundleException e) { |
| framework.publishFrameworkEvent(FrameworkEvent.ERROR, this, e); |
| } |
| } |
| try { |
| AccessController.doPrivileged(action); |
| } catch (PrivilegedActionException pae) { |
| if (bundleActive) /* if we stopped the bundle */{ |
| try { |
| startWorker(START_TRANSIENT); |
| } catch (BundleException e) { |
| /* |
| * if we fail to start the original bundle then we are in |
| * big trouble |
| */ |
| framework.publishFrameworkEvent(FrameworkEvent.ERROR, this, e); |
| } |
| } |
| throw (BundleException) pae.getException(); |
| } |
| framework.publishBundleEvent(BundleEvent.UNINSTALLED, this); |
| } |
| |
| /** |
| * Uninstall worker. Assumes the caller has the state change lock. |
| */ |
| protected void uninstallWorkerPrivileged() throws BundleException { |
| BundleWatcher bundleStats = framework.adaptor.getBundleWatcher(); |
| if (bundleStats != null) |
| bundleStats.watchBundle(this, BundleWatcher.START_UNINSTALLING); |
| boolean unloaded = false; |
| //cache the bundle's headers |
| getHeaders(); |
| BundleOperation storage = framework.adaptor.uninstallBundle(this.bundledata); |
| BundleRepository bundles = framework.getBundles(); |
| try { |
| storage.begin(); |
| boolean exporting; |
| int st = getState(); |
| synchronized (bundles) { |
| bundles.remove(this); /* remove before calling unload */ |
| exporting = unload(); |
| } |
| // send out unresolved events outside synch block (defect #80610) |
| if (st == RESOLVED) |
| framework.publishBundleEvent(BundleEvent.UNRESOLVED, this); |
| unloaded = true; |
| storage.commit(exporting); |
| close(); |
| } catch (BundleException e) { |
| try { |
| storage.undo(); |
| if (unloaded) /* if we unloaded the bundle */{ |
| synchronized (bundles) { |
| load(); /* reload the bundle */ |
| bundles.add(this); |
| } |
| } |
| } catch (BundleException ee) { |
| /* |
| * if we fail to load the original bundle then we are in big |
| * trouble |
| */ |
| framework.publishFrameworkEvent(FrameworkEvent.ERROR, this, ee); |
| } |
| throw e; |
| } finally { |
| if (bundleStats != null) |
| bundleStats.watchBundle(this, BundleWatcher.END_UNINSTALLING); |
| } |
| } |
| |
| /** |
| * Return the bundle's manifest headers and values from the manifest's |
| * preliminary section. That is all the manifest's headers and values prior |
| * to the first blank line. |
| * |
| * <p> |
| * Manifest header names are case-insensitive. The methods of the returned |
| * <code>Dictionary</code> object will operate on header names in a |
| * case-insensitive manner. |
| * |
| * <p> |
| * For example, the following manifest headers and values are included if |
| * they are present in the manifest: |
| * |
| * <pre> |
| * Bundle-Name |
| * Bundle-Vendor |
| * Bundle-Version |
| * Bundle-Description |
| * Bundle-DocURL |
| * Bundle-ContactAddress |
| * </pre> |
| * |
| * <p> |
| * This method will continue to return this information when the bundle is |
| * in the {@link #UNINSTALLED}state. |
| * |
| * @return A <code>Dictionary</code> object containing the bundle's |
| * manifest headers and values. |
| * @exception java.lang.SecurityException |
| * If the caller does not have {@link AdminPermission} |
| * permission and the Java runtime environment supports |
| * permissions. |
| */ |
| public Dictionary getHeaders() { |
| return getHeaders(null); |
| } |
| |
| /** |
| * Returns this bundle's Manifest headers and values. This method returns |
| * all the Manifest headers and values from the main section of the |
| * bundle's Manifest file; that is, all lines prior to the first blank |
| * line. |
| * |
| * <p> |
| * Manifest header names are case-insensitive. The methods of the returned |
| * <tt>Dictionary</tt> object will operate on header names in a |
| * case-insensitive manner. |
| * |
| * If a Manifest header begins with a '%', it will be evaluated with the |
| * specified properties file for the specied Locale. |
| * |
| * <p> |
| * For example, the following Manifest headers and values are included if |
| * they are present in the Manifest file: |
| * |
| * <pre> |
| * Bundle-Name |
| * Bundle-Vendor |
| * Bundle-Version |
| * Bundle-Description |
| * Bundle-DocURL |
| * Bundle-ContactAddress |
| * </pre> |
| * |
| * <p> |
| * This method will continue to return Manifest header information while |
| * this bundle is in the <tt>UNINSTALLED</tt> state. |
| * |
| * @return A <tt>Dictionary</tt> object containing this bundle's Manifest |
| * headers and values. |
| * |
| * @exception java.lang.SecurityException |
| * If the caller does not have the <tt>AdminPermission</tt>, |
| * and the Java Runtime Environment supports permissions. |
| */ |
| public Dictionary getHeaders(String localeString) { |
| framework.checkAdminPermission(this, AdminPermission.METADATA); |
| ManifestLocalization localization; |
| try { |
| localization = getManifestLocalization(); |
| } catch (BundleException e) { |
| framework.publishFrameworkEvent(FrameworkEvent.ERROR, this, e); |
| // return an empty dictinary. |
| return new Hashtable(); |
| } |
| if (localeString == null) |
| localeString = Locale.getDefault().toString(); |
| return localization.getHeaders(localeString); |
| } |
| |
| /** |
| * Retrieve the bundle's unique identifier, which the framework assigned to |
| * this bundle when it was installed. |
| * |
| * <p> |
| * The unique identifier has the following attributes: |
| * <ul> |
| * <li>It is unique and persistent. |
| * <li>The identifier is a long. |
| * <li>Once its value is assigned to a bundle, that value is not reused |
| * for another bundle, even after the bundle is uninstalled. |
| * <li>Its value does not change as long as the bundle remains installed. |
| * <li>Its value does not change when the bundle is updated |
| * </ul> |
| * |
| * <p> |
| * This method will continue to return the bundle's unique identifier when |
| * the bundle is in the {@link #UNINSTALLED}state. |
| * |
| * @return This bundle's unique identifier. |
| */ |
| public long getBundleId() { |
| return (bundledata.getBundleID()); |
| } |
| |
| /** |
| * Retrieve the location identifier of the bundle. This is typically the |
| * location passed to |
| * {@link BundleContextImpl#installBundle(String) BundleContext.installBundle}when the |
| * bundle was installed. The location identifier of the bundle may change |
| * during bundle update. Calling this method while framework is updating |
| * the bundle results in undefined behavior. |
| * |
| * <p> |
| * This method will continue to return the bundle's location identifier |
| * when the bundle is in the {@link #UNINSTALLED}state. |
| * |
| * @return A string that is the location identifier of the bundle. |
| * @exception java.lang.SecurityException |
| * If the caller does not have {@link AdminPermission} |
| * permission and the Java runtime environment supports |
| * permissions. |
| */ |
| public String getLocation() { |
| framework.checkAdminPermission(this, AdminPermission.METADATA); |
| return (bundledata.getLocation()); |
| } |
| |
| /** |
| * Determine whether the bundle has the requested permission. |
| * |
| * <p> |
| * If the Java runtime environment does not supports permissions this |
| * method always returns <code>true</code>. The permission parameter is |
| * of type <code>Object</code> to avoid referencing the <code>java.security.Permission</code> |
| * class directly. This is to allow the framework to be implemented in Java |
| * environments which do not support permissions. |
| * |
| * @param permission |
| * The requested permission. |
| * @return <code>true</code> if the bundle has the requested permission |
| * or <code>false</code> if the bundle does not have the |
| * permission or the permission parameter is not an <code>instanceof java.security.Permission</code>. |
| * @exception java.lang.IllegalStateException |
| * If the bundle has been uninstalled. |
| */ |
| public boolean hasPermission(Object permission) { |
| checkValid(); |
| if (domain != null) { |
| if (permission instanceof Permission) { |
| SecurityManager sm = System.getSecurityManager(); |
| if (sm instanceof EquinoxSecurityManager) { |
| /* |
| * If the FrameworkSecurityManager is active, we need to do checks the "right" way. |
| * We can exploit our knowledge that the security context of FrameworkSecurityManager |
| * is an AccessControlContext to invoke it properly with the ProtectionDomain. |
| */ |
| AccessControlContext acc = new AccessControlContext(new ProtectionDomain[] {domain}); |
| try { |
| sm.checkPermission((Permission) permission, acc); |
| return true; |
| } catch (Exception e) { |
| return false; |
| } |
| } |
| return domain.implies((Permission) permission); |
| } |
| return false; |
| } |
| return true; |
| } |
| |
| /** |
| * This method marks the bundle's state as changing so that other calls to |
| * start/stop/suspend/update/uninstall can wait until the state change is |
| * complete. If stateChanging is non-null when this method is called, we |
| * will wait for the state change to complete. If the timeout expires |
| * without changing state (this may happen if the state change is back up |
| * our call stack), a BundleException is thrown so that we don't wait |
| * forever. |
| * |
| * A call to this method should be immediately followed by a try block |
| * whose finally block calls completeStateChange(). |
| * |
| * beginStateChange(); try { // change the bundle's state here... } finally { |
| * completeStateChange(); } |
| * |
| * @exception org.osgi.framework.BundleException |
| * if the bundles state is still changing after waiting for |
| * the timeout. |
| */ |
| protected void beginStateChange() throws BundleException { |
| synchronized (statechangeLock) { |
| boolean doubleFault = false; |
| while (true) { |
| if (stateChanging == null) { |
| stateChanging = Thread.currentThread(); |
| return; |
| } |
| if (doubleFault || (stateChanging == Thread.currentThread())) { |
| throw new BundleException(NLS.bind(Msg.BUNDLE_STATE_CHANGE_EXCEPTION, getBundleData().getLocation(), stateChanging.getName()), BundleException.STATECHANGE_ERROR, new BundleStatusException(null, StatusException.CODE_WARNING, stateChanging)); |
| } |
| try { |
| long start = 0; |
| if (Debug.DEBUG_GENERAL) { |
| Debug.println(" Waiting for state to change in bundle " + this); //$NON-NLS-1$ |
| start = System.currentTimeMillis(); |
| } |
| statechangeLock.wait(5000); |
| /* |
| * wait for other thread to |
| * finish changing state |
| */ |
| if (Debug.DEBUG_GENERAL) { |
| long end = System.currentTimeMillis(); |
| if (end - start > 0) |
| System.out.println("Waiting... : " + getSymbolicName() + ' ' + (end - start)); //$NON-NLS-1$ |
| } |
| } catch (InterruptedException e) { |
| //Nothing to do |
| } |
| doubleFault = true; |
| } |
| } |
| } |
| |
| /** |
| * This method completes the bundle state change by setting stateChanging |
| * to null and notifying one waiter that the state change has completed. |
| */ |
| protected void completeStateChange() { |
| synchronized (statechangeLock) { |
| if (stateChanging == Thread.currentThread()) { |
| stateChanging = null; |
| statechangeLock.notify(); |
| /* |
| * notify one waiting thread that the |
| * state change is complete |
| */ |
| } |
| } |
| } |
| |
| /** |
| * Return a string representation of this bundle. |
| * |
| * @return String |
| */ |
| public String toString() { |
| String name = bundledata.getSymbolicName(); |
| if (name == null) |
| name = "unknown"; //$NON-NLS-1$ |
| return (name + '_' + bundledata.getVersion() + " [" + getBundleId() + "]"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| |
| /** |
| * Answers an integer indicating the relative positions of the receiver and |
| * the argument in the natural order of elements of the receiver's class. |
| * |
| * @return int which should be <0 if the receiver should sort before the |
| * argument, 0 if the receiver should sort in the same position as |
| * the argument, and >0 if the receiver should sort after the |
| * argument. |
| * @param obj |
| * another Bundle an object to compare the receiver to |
| * @exception ClassCastException |
| * if the argument can not be converted into something |
| * comparable with the receiver. |
| */ |
| public int compareTo(Bundle obj) { |
| int slcomp = getInternalStartLevel() - ((AbstractBundle) obj).getInternalStartLevel(); |
| if (slcomp != 0) { |
| return slcomp; |
| } |
| long idcomp = getBundleId() - ((AbstractBundle) obj).getBundleId(); |
| return (idcomp < 0L) ? -1 : ((idcomp > 0L) ? 1 : 0); |
| } |
| |
| /** |
| * This method checks that the bundle is not uninstalled. If the bundle is |
| * uninstalled, an IllegalStateException is thrown. |
| * |
| * @exception java.lang.IllegalStateException |
| * If the bundle is uninstalled. |
| */ |
| protected void checkValid() { |
| if (state == UNINSTALLED) { |
| throw new IllegalStateException(NLS.bind(Msg.BUNDLE_UNINSTALLED_EXCEPTION, getBundleData().getLocation())); |
| } |
| } |
| |
| /** |
| * Get the bundle's ProtectionDomain. |
| * |
| * @return bundle's ProtectionDomain. |
| */ |
| public BundleProtectionDomain getProtectionDomain() { |
| return domain; |
| } |
| |
| protected BundleFragment[] getFragments() { |
| checkValid(); |
| return null; |
| } |
| |
| protected boolean isFragment() { |
| return false; |
| } |
| |
| BundleHost[] getHosts() { |
| checkValid(); |
| return null; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.osgi.framework.Bundle#findClass(java.lang.String) |
| */ |
| public Class loadClass(String classname) throws ClassNotFoundException { |
| return loadClass(classname, true); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.osgi.framework.Bundle#getResourcePaths(java.lang.String) |
| */ |
| public Enumeration getEntryPaths(final String path) { |
| try { |
| framework.checkAdminPermission(this, AdminPermission.RESOURCE); |
| } catch (SecurityException e) { |
| return null; |
| } |
| checkValid(); |
| // TODO this doPrivileged is probably not needed. The adaptor isolates callers from disk access |
| return (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(this, AdminPermission.RESOURCE); |
| } catch (SecurityException e) { |
| return null; |
| } |
| return getEntry0(fileName); |
| } |
| |
| URL getEntry0(String fileName) { |
| checkValid(); |
| return bundledata.getEntry(fileName); |
| } |
| |
| public String getSymbolicName() { |
| return bundledata.getSymbolicName(); |
| } |
| |
| public long getLastModified() { |
| return bundledata.getLastModified(); |
| } |
| |
| public BundleData getBundleData() { |
| return bundledata; |
| } |
| |
| public Version getVersion() { |
| return bundledata.getVersion(); |
| } |
| |
| public BundleDescription getBundleDescription() { |
| return framework.adaptor.getState().getBundle(getBundleId()); |
| } |
| |
| int getInternalStartLevel() { |
| return bundledata.getStartLevel(); |
| } |
| |
| protected abstract BundleLoader getBundleLoader(); |
| |
| /** |
| * Mark this bundle as resolved. |
| */ |
| protected void resolve() { |
| if (Debug.DEBUG_GENERAL) { |
| if ((state & (INSTALLED)) == 0) { |
| Debug.println("Bundle.resolve called when state != INSTALLED: " + this); //$NON-NLS-1$ |
| Debug.printStackTrace(new Exception("Stack trace")); //$NON-NLS-1$ |
| } |
| } |
| if (state == INSTALLED) { |
| state = RESOLVED; |
| // Do not publish RESOLVED event here. This is done by caller |
| // to resolve if appropriate. |
| } |
| } |
| |
| public BundleContext getBundleContext() { |
| framework.checkAdminPermission(this, AdminPermission.CONTEXT); |
| return getContext(); |
| } |
| |
| /** |
| * Return the current context for this bundle. |
| * |
| * @return BundleContext for this bundle. |
| */ |
| abstract protected BundleContextImpl getContext(); |
| |
| public BundleException getResolutionFailureException() { |
| BundleDescription bundleDescription = getBundleDescription(); |
| if (bundleDescription == null) |
| return new BundleException(NLS.bind(Msg.BUNDLE_UNRESOLVED_EXCEPTION, this.toString()), BundleException.RESOLVE_ERROR); |
| // just a sanity check - this would be an inconsistency between the framework and the state |
| if (bundleDescription.isResolved()) |
| return new BundleException(Msg.BUNDLE_UNRESOLVED_STATE_CONFLICT, BundleException.RESOLVE_ERROR); |
| return getResolverError(bundleDescription); |
| } |
| |
| private BundleException getResolverError(BundleDescription bundleDesc) { |
| ResolverError[] errors = framework.adaptor.getState().getResolverErrors(bundleDesc); |
| if (errors == null || errors.length == 0) |
| return new BundleException(NLS.bind(Msg.BUNDLE_UNRESOLVED_EXCEPTION, this.toString()), BundleException.RESOLVE_ERROR); |
| StringBuffer message = new StringBuffer(); |
| int errorType = BundleException.RESOLVE_ERROR; |
| for (int i = 0; i < errors.length; i++) { |
| if ((errors[i].getType() & ResolverError.INVALID_NATIVECODE_PATHS) != 0) |
| errorType = BundleException.NATIVECODE_ERROR; |
| message.append(errors[i].toString()); |
| if (i < errors.length - 1) |
| message.append(", "); //$NON-NLS-1$ |
| } |
| return new BundleException(NLS.bind(Msg.BUNDLE_UNRESOLVED_UNSATISFIED_CONSTRAINT_EXCEPTION, this.toString(), message.toString()), errorType); |
| } |
| |
| public int getKeyHashCode() { |
| long id = getBundleId(); |
| return (int) (id ^ (id >>> 32)); |
| } |
| |
| public boolean compare(KeyedElement other) { |
| return getBundleId() == ((AbstractBundle) other).getBundleId(); |
| } |
| |
| public Object getKey() { |
| return new Long(getBundleId()); |
| } |
| |
| /* This method is used by the Bundle Localization Service to obtain |
| * a ResourceBundle that resides in a bundle. This is not an OSGi |
| * defined method for org.osgi.framework.Bundle |
| * |
| */ |
| public ResourceBundle getResourceBundle(String localeString) { |
| ManifestLocalization localization; |
| try { |
| localization = getManifestLocalization(); |
| } catch (BundleException ex) { |
| return (null); |
| } |
| if (localeString == null) { |
| localeString = Locale.getDefault().toString(); |
| } |
| return localization.getResourceBundle(localeString); |
| } |
| |
| private synchronized ManifestLocalization getManifestLocalization() throws BundleException { |
| ManifestLocalization currentLocalization = manifestLocalization; |
| if (currentLocalization == null) { |
| Dictionary rawHeaders = bundledata.getManifest(); |
| manifestLocalization = currentLocalization = new ManifestLocalization(this, rawHeaders); |
| } |
| return currentLocalization; |
| } |
| |
| public boolean testStateChanging(Object thread) { |
| return stateChanging == thread; |
| } |
| |
| public Thread getStateChanging() { |
| return stateChanging; |
| } |
| |
| public Enumeration findEntries(String path, String filePattern, boolean recurse) { |
| try { |
| framework.checkAdminPermission(this, AdminPermission.RESOURCE); |
| } catch (SecurityException e) { |
| return null; |
| } |
| checkValid(); |
| // check to see if the bundle is resolved |
| if (!isResolved()) |
| framework.packageAdmin.resolveBundles(new Bundle[] {this}); |
| |
| // a list used to store the results of the search |
| List pathList = new ArrayList(); |
| Filter patternFilter = null; |
| Hashtable patternProps = null; |
| if (filePattern != null) |
| try { |
| // create a file pattern filter with 'filename' as the key |
| patternFilter = FilterImpl.newInstance("(filename=" + filePattern + ")"); //$NON-NLS-1$ //$NON-NLS-2$ |
| // create a single hashtable to be shared during the recursive search |
| patternProps = new Hashtable(2); |
| } catch (InvalidSyntaxException e) { |
| // cannot happen |
| } |
| // find the local entries of this bundle |
| findLocalEntryPaths(path, patternFilter, patternProps, recurse, pathList); |
| // if this bundle is a host to fragments then search the fragments |
| final BundleFragment[] fragments = getFragments(); |
| final int numFragments = fragments == null ? -1 : fragments.length; |
| for (int i = 0; i < numFragments; i++) |
| ((AbstractBundle) fragments[i]).findLocalEntryPaths(path, patternFilter, patternProps, recurse, pathList); |
| // return null if no entries found |
| if (pathList.size() == 0) |
| return null; |
| // create an enumeration to enumerate the pathList |
| final String[] pathArray = (String[]) pathList.toArray(new String[pathList.size()]); |
| return new Enumeration() { |
| int curIndex = 0; |
| int curFragment = -1; |
| URL nextElement = null; |
| |
| public boolean hasMoreElements() { |
| if (nextElement != null) |
| return true; |
| getNextElement(); |
| return nextElement != null; |
| } |
| |
| public Object nextElement() { |
| if (!hasMoreElements()) |
| throw new NoSuchElementException(); |
| URL result; |
| result = nextElement; |
| // force the next element search |
| getNextElement(); |
| return result; |
| } |
| |
| private void getNextElement() { |
| nextElement = null; |
| if (curIndex >= pathArray.length) |
| // reached the end of the pathArray; no more elements |
| return; |
| String curPath = pathArray[curIndex]; |
| if (curFragment == -1) { |
| // need to search ourselves first |
| nextElement = getEntry0(curPath); |
| curFragment++; |
| } |
| // if the element is not in the host look in the fragments until we have searched them all |
| while (nextElement == null && curFragment < numFragments) |
| nextElement = fragments[curFragment++].getEntry0(curPath); |
| // if we have no fragments or we have searched all fragments then advance to the next path |
| if (numFragments == -1 || curFragment >= numFragments) { |
| curIndex++; |
| curFragment = -1; |
| } |
| // searched all fragments for the current path, move to the next one |
| if (nextElement == null) |
| getNextElement(); |
| } |
| |
| }; |
| } |
| |
| protected void findLocalEntryPaths(String path, Filter patternFilter, Hashtable patternProps, boolean recurse, List pathList) { |
| Enumeration entryPaths = bundledata.getEntryPaths(path); |
| if (entryPaths == null) |
| return; |
| while (entryPaths.hasMoreElements()) { |
| String entry = (String) entryPaths.nextElement(); |
| int lastSlash = entry.lastIndexOf('/'); |
| if (patternProps != null) { |
| int secondToLastSlash = entry.lastIndexOf('/', lastSlash - 1); |
| int fileStart; |
| int fileEnd = entry.length(); |
| if (lastSlash < 0) |
| fileStart = 0; |
| else if (lastSlash != entry.length() - 1) |
| fileStart = lastSlash + 1; |
| else { |
| fileEnd = lastSlash; // leave the lastSlash out |
| if (secondToLastSlash < 0) |
| fileStart = 0; |
| else |
| fileStart = secondToLastSlash + 1; |
| } |
| String fileName = entry.substring(fileStart, fileEnd); |
| // set the filename to the current entry |
| patternProps.put("filename", fileName); //$NON-NLS-1$ |
| } |
| // prevent duplicates and match on the patterFilter |
| if (!pathList.contains(entry) && (patternFilter == null || patternFilter.matchCase(patternProps))) |
| pathList.add(entry); |
| // rescurse only into entries that are directories |
| if (recurse && !entry.equals(path) && entry.length() > 0 && lastSlash == (entry.length() - 1)) |
| findLocalEntryPaths(entry, patternFilter, patternProps, recurse, pathList); |
| } |
| return; |
| } |
| |
| class BundleStatusException extends Throwable implements StatusException { |
| private static final long serialVersionUID = 7201911791818929100L; |
| private int code; |
| private Object status; |
| |
| BundleStatusException(String message, int code, Object status) { |
| super(message); |
| this.code = code; |
| this.status = status; |
| } |
| |
| public Object getStatus() { |
| return status; |
| } |
| |
| public int getStatusCode() { |
| return code; |
| } |
| |
| } |
| |
| public Map/* <X509Certificate, List<X509Certificate>> */getSignerCertificates(int signersType) { |
| if (signersType != SIGNERS_ALL && signersType != SIGNERS_TRUSTED) |
| throw new IllegalArgumentException("Invalid signers type: " + signersType); //$NON-NLS-1$ |
| if (framework == null) |
| return Collections.EMPTY_MAP; |
| SignedContentFactory factory = framework.getSignedContentFactory(); |
| if (factory == null) |
| return Collections.EMPTY_MAP; |
| try { |
| SignedContent signedContent = factory.getSignedContent(this); |
| SignerInfo[] infos = signedContent.getSignerInfos(); |
| if (infos.length == 0) |
| return Collections.EMPTY_MAP; |
| Map/* <X509Certificate, List<X509Certificate>> */results = new HashMap(infos.length); |
| for (int i = 0; i < infos.length; i++) { |
| if (signersType == SIGNERS_TRUSTED && !infos[i].isTrusted()) |
| continue; |
| Certificate[] certs = infos[i].getCertificateChain(); |
| if (certs == null || certs.length == 0) |
| continue; |
| List/* <X509Certificate> */certChain = new ArrayList(); |
| for (int j = 0; j < certs.length; j++) |
| certChain.add(certs[j]); |
| results.put(certs[0], certChain); |
| } |
| return results; |
| } catch (Exception e) { |
| return Collections.EMPTY_MAP; |
| } |
| } |
| |
| public <A> A adapt(Class<A> adapterType) { |
| if (adapterType.isInstance(this)) |
| return (A) this; |
| if (BundleContext.class.equals(adapterType)) { |
| try { |
| return (A) getBundleContext(); |
| } catch (SecurityException e) { |
| return null; |
| } |
| } |
| if (BundleStartLevel.class.equals(adapterType)) |
| return (A) this; |
| |
| // TODO need to handle BundleWiring, BundlePackageAdmin |
| return null; |
| } |
| |
| public File getDataFile(String filename) { |
| return framework.getDataFile(this, filename); |
| } |
| |
| public Bundle getBundle() { |
| return this; |
| } |
| |
| public int getStartLevel() { |
| if (getState() == Bundle.UNINSTALLED) |
| throw new IllegalArgumentException(NLS.bind(Msg.BUNDLE_UNINSTALLED_EXCEPTION, getBundleData().getLocation())); |
| return getInternalStartLevel(); |
| } |
| |
| public void setStartLevel(int startlevel) { |
| framework.startLevelManager.setBundleStartLevel(this, startlevel); |
| } |
| |
| public boolean isPersistentlyStarted() { |
| if (getState() == Bundle.UNINSTALLED) |
| throw new IllegalArgumentException(NLS.bind(Msg.BUNDLE_UNINSTALLED_EXCEPTION, getBundleData().getLocation())); |
| return (getBundleData().getStatus() & Constants.BUNDLE_STARTED) != 0; |
| } |
| |
| public boolean isActivationPolicyUsed() { |
| if (getState() == Bundle.UNINSTALLED) |
| throw new IllegalArgumentException(NLS.bind(Msg.BUNDLE_UNINSTALLED_EXCEPTION, getBundleData().getLocation())); |
| return (getBundleData().getStatus() & Constants.BUNDLE_ACTIVATION_POLICY) != 0; |
| } |
| |
| } |