diff options
author | Thomas Watson | 2015-10-20 14:45:42 +0000 |
---|---|---|
committer | Thomas Watson | 2015-10-20 14:45:42 +0000 |
commit | dd65305bf1442ab1ff941bb2896ee62935fc85c3 (patch) | |
tree | 98ec1fc6a9e1117f1b6563f71811bdb707594e40 | |
parent | a83aaac9100f28c7a2cf1dc8894a5d177d2916d9 (diff) | |
download | rt.equinox.framework-dd65305bf1442ab1ff941bb2896ee62935fc85c3.tar.gz rt.equinox.framework-dd65305bf1442ab1ff941bb2896ee62935fc85c3.tar.xz rt.equinox.framework-dd65305bf1442ab1ff941bb2896ee62935fc85c3.zip |
Bug 479988 - BundleListener not called for System Bundle events (esp.
stopping/stopped)
5 files changed, 168 insertions, 74 deletions
diff --git a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/SystemBundleTests.java b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/SystemBundleTests.java index d624439b5..eeadf323e 100644 --- a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/SystemBundleTests.java +++ b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/SystemBundleTests.java @@ -16,6 +16,7 @@ import java.security.Permission; import java.security.PrivilegedAction; import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; import java.util.jar.*; import javax.net.SocketFactory; import junit.framework.Test; @@ -1926,7 +1927,16 @@ public class SystemBundleTests extends AbstractBundleTests { } finally { Thread.currentThread().setContextClassLoader(current); } - + try { + equinox.stop(); + } catch (BundleException e) { + fail("Unexpected error stopping framework", e); //$NON-NLS-1$ + } + try { + equinox.waitForStop(10000); + } catch (InterruptedException e) { + fail("Unexpected interrupted exception", e); //$NON-NLS-1$ + } } private void checkActive(Bundle b) { @@ -2447,6 +2457,43 @@ public class SystemBundleTests extends AbstractBundleTests { equinox.stop(); } + public void testSystemBundleListener() throws BundleException, InterruptedException { + File config = OSGiTestsActivator.getContext().getDataFile(getName()); + config.mkdirs(); + Map<String, Object> configuration = new HashMap<String, Object>(); + configuration.put(Constants.FRAMEWORK_STORAGE, config.getAbsolutePath()); + Equinox equinox = new Equinox(configuration); + equinox.start(); + BundleContext systemContext = equinox.getBundleContext(); + + final AtomicInteger stoppingEvent = new AtomicInteger(); + final AtomicInteger stoppedEvent = new AtomicInteger(); + + BundleListener systemBundleListener = new SynchronousBundleListener() { + + @Override + public void bundleChanged(BundleEvent event) { + if (event.getBundle().getBundleId() == 0) { + switch (event.getType()) { + case BundleEvent.STOPPING : + stoppingEvent.incrementAndGet(); + break; + case BundleEvent.STOPPED : + stoppedEvent.incrementAndGet(); + default : + break; + } + } + } + }; + systemContext.addBundleListener(systemBundleListener); + + equinox.stop(); + equinox.waitForStop(5000); + assertEquals("Wrong number of STOPPING events", 1, stoppingEvent.get()); + assertEquals("Wrong number of STOPPED events", 1, stoppedEvent.get()); + } + public void testContextBootDelegation() throws BundleException { File config = OSGiTestsActivator.getContext().getDataFile(getName()); config.mkdirs(); diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/BundleContextImpl.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/BundleContextImpl.java index a5797dc98..673d01883 100644 --- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/BundleContextImpl.java +++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/BundleContextImpl.java @@ -886,12 +886,10 @@ public class BundleContextImpl implements BundleContext, EventDispatcher<Object, * @param object Event object */ public void dispatchEvent(Object originalListener, Object l, int action, Object object) { - // save the bundle ref to a local variable - // to avoid interference from another thread closing this context - EquinoxBundle tmpBundle = bundle; Object previousTCCL = setContextFinder(); try { - if (isValid()) /* if context still valid */{ + // if context still valid or the system bundle + if (isValid() || bundle.getBundleId() == 0) { switch (action) { case EquinoxEventPublisher.BUNDLEEVENT : case EquinoxEventPublisher.BUNDLEEVENTSYNC : { @@ -899,7 +897,7 @@ public class BundleContextImpl implements BundleContext, EventDispatcher<Object, if (debug.DEBUG_EVENTS) { String listenerName = listener.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(listener)); //$NON-NLS-1$ - Debug.println("dispatchBundleEvent[" + tmpBundle + "](" + listenerName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + Debug.println("dispatchBundleEvent[" + bundle + "](" + listenerName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } listener.bundleChanged((BundleEvent) object); @@ -912,7 +910,7 @@ public class BundleContextImpl implements BundleContext, EventDispatcher<Object, ServiceListener listener = (ServiceListener) l; if (debug.DEBUG_EVENTS) { String listenerName = listener.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(listener)); //$NON-NLS-1$ - Debug.println("dispatchServiceEvent[" + tmpBundle + "](" + listenerName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + Debug.println("dispatchServiceEvent[" + bundle + "](" + listenerName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } listener.serviceChanged(event); break; @@ -923,7 +921,7 @@ public class BundleContextImpl implements BundleContext, EventDispatcher<Object, if (debug.DEBUG_EVENTS) { String listenerName = listener.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(listener)); //$NON-NLS-1$ - Debug.println("dispatchFrameworkEvent[" + tmpBundle + "](" + listenerName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + Debug.println("dispatchFrameworkEvent[" + bundle + "](" + listenerName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } listener.frameworkEvent((FrameworkEvent) object); @@ -949,7 +947,7 @@ public class BundleContextImpl implements BundleContext, EventDispatcher<Object, } } - container.getEventPublisher().publishFrameworkEvent(FrameworkEvent.ERROR, tmpBundle, t); + container.getEventPublisher().publishFrameworkEvent(FrameworkEvent.ERROR, bundle, t); } } finally { if (previousTCCL != Boolean.FALSE) diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/EquinoxBundle.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/EquinoxBundle.java index 8981c8697..39dd41208 100644 --- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/EquinoxBundle.java +++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/EquinoxBundle.java @@ -16,14 +16,10 @@ import java.security.*; import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.util.*; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; import org.eclipse.osgi.container.*; import org.eclipse.osgi.container.Module.*; import org.eclipse.osgi.container.ModuleContainerAdaptor.ContainerEvent; import org.eclipse.osgi.container.ModuleContainerAdaptor.ModuleEvent; -import org.eclipse.osgi.framework.eventmgr.EventDispatcher; -import org.eclipse.osgi.framework.eventmgr.ListenerQueue; import org.eclipse.osgi.framework.log.FrameworkLogEntry; import org.eclipse.osgi.internal.debug.Debug; import org.eclipse.osgi.internal.loader.BundleLoader; @@ -218,37 +214,12 @@ public class EquinoxBundle implements Bundle, BundleReference { ((SystemModule) getModule()).init(); } finally { if (!initListeners.isEmpty()) { - flushFrameworkEvents(); + getEquinoxContainer().getEventPublisher().flushFrameworkEvents(); removeInitListeners(); } } } - private void flushFrameworkEvents() { - EventDispatcher<Object, Object, CountDownLatch> dispatcher = new EventDispatcher<Object, Object, CountDownLatch>() { - @Override - public void dispatchEvent(Object eventListener, Object listenerObject, int eventAction, CountDownLatch flushedSignal) { - // Signal that we have flushed all events - flushedSignal.countDown(); - } - }; - - ListenerQueue<Object, Object, CountDownLatch> queue = getEquinoxContainer().newListenerQueue(); - queue.queueListeners(Collections.<Object, Object> singletonMap(dispatcher, dispatcher).entrySet(), dispatcher); - - // fire event with the flushedSignal latch - CountDownLatch flushedSignal = new CountDownLatch(1); - queue.dispatchEventAsynchronous(0, flushedSignal); - - try { - // Wait for the flush signal; timeout after 30 seconds - flushedSignal.await(30, TimeUnit.SECONDS); - } catch (InterruptedException e) { - // ignore but reset the interrupted flag - Thread.currentThread().interrupt(); - } - } - void addInitFrameworkListeners() { BundleContext context = createBundleContext(false); for (FrameworkListener initListener : initListeners) { diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/EquinoxContainer.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/EquinoxContainer.java index 5f01e167d..def3ffde7 100644 --- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/EquinoxContainer.java +++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/EquinoxContainer.java @@ -14,7 +14,6 @@ import java.io.IOException; import java.security.AccessController; import java.util.*; import java.util.concurrent.*; -import org.eclipse.osgi.framework.eventmgr.EventManager; import org.eclipse.osgi.framework.eventmgr.ListenerQueue; import org.eclipse.osgi.framework.log.FrameworkLogEntry; import org.eclipse.osgi.framework.util.SecureAction; @@ -47,10 +46,10 @@ public class EquinoxContainer implements ThreadFactory, Runnable { private final Set<String> bootDelegation; private final String[] bootDelegationStems; private final boolean bootDelegateAll; + private final EquinoxEventPublisher eventPublisher; private final Object monitor = new Object(); - private EventManager eventManager; - private EquinoxEventPublisher eventPublisher; + private ServiceRegistry serviceRegistry; private ContextFinder contextFinder; @@ -72,6 +71,7 @@ public class EquinoxContainer implements ThreadFactory, Runnable { } this.packageAdmin = new PackageAdminImpl(storage.getModuleContainer()); this.startLevel = new StartLevelImpl(storage.getModuleContainer()); + this.eventPublisher = new EquinoxEventPublisher(this); // set the boot delegation according to the osgi boot delegation property // TODO unfortunately this has to be done after constructing storage so the vm profile is loaded @@ -144,9 +144,8 @@ public class EquinoxContainer implements ThreadFactory, Runnable { } void init() { + eventPublisher.init(); synchronized (this.monitor) { - eventManager = new EventManager("Framework Event Dispatcher: " + toString()); //$NON-NLS-1$ - eventPublisher = new EquinoxEventPublisher(this); serviceRegistry = new ServiceRegistry(this); initializeContextFinder(); executor = Executors.newScheduledThreadPool(1, this); @@ -157,21 +156,16 @@ public class EquinoxContainer implements ThreadFactory, Runnable { } void close() { - EventManager currentEventManager; StorageSaver currentSaver; Storage currentStorage; ScheduledExecutorService currentExecutor; synchronized (this.monitor) { - currentEventManager = eventManager; - eventManager = null; - eventPublisher = null; serviceRegistry = null; currentSaver = storageSaver; currentStorage = storage; currentExecutor = executor; } // do this outside of the lock to avoid deadlock - currentEventManager.close(); currentSaver.close(); currentStorage.close(); // Must be done last since it will result in termination of the @@ -232,9 +226,7 @@ public class EquinoxContainer implements ThreadFactory, Runnable { } public <K, V, E> ListenerQueue<K, V, E> newListenerQueue() { - synchronized (this.monitor) { - return new ListenerQueue<K, V, E>(eventManager); - } + return eventPublisher.newListenerQueue(); } void checkAdminPermission(Bundle bundle, String action) { diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/EquinoxEventPublisher.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/EquinoxEventPublisher.java index 7ae31bc12..203197461 100644 --- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/EquinoxEventPublisher.java +++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/EquinoxEventPublisher.java @@ -13,10 +13,11 @@ package org.eclipse.osgi.internal.framework; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.*; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import org.eclipse.osgi.framework.eventmgr.*; import org.eclipse.osgi.internal.debug.Debug; -import org.eclipse.osgi.internal.serviceregistry.HookContext; -import org.eclipse.osgi.internal.serviceregistry.ShrinkableCollection; +import org.eclipse.osgi.internal.serviceregistry.*; import org.osgi.framework.*; import org.osgi.framework.hooks.bundle.CollisionHook; import org.osgi.framework.hooks.bundle.EventHook; @@ -24,6 +25,7 @@ import org.osgi.framework.hooks.bundle.EventHook; public class EquinoxEventPublisher { static final String eventHookName = EventHook.class.getName(); static final String collisionHookName = CollisionHook.class.getName(); + static final int FRAMEWORK_STOPPED_MASK = (FrameworkEvent.STOPPED | FrameworkEvent.STOPPED_BOOTCLASSPATH_MODIFIED | FrameworkEvent.STOPPED_UPDATE); static final int BUNDLEEVENT = 1; static final int BUNDLEEVENTSYNC = 2; @@ -31,6 +33,10 @@ public class EquinoxEventPublisher { static final int FRAMEWORKEVENT = 4; private final EquinoxContainer container; + + private Object monitor = new Object(); + private EventManager eventManager; + /* * The following maps objects keep track of event listeners * by BundleContext. Each element is a Map that is the set @@ -51,6 +57,45 @@ public class EquinoxEventPublisher { this.container = container; } + void init() { + // create our event manager on init() + resetEventManager(new EventManager("Framework Event Dispatcher: " + toString())); //$NON-NLS-1$ + } + + void close() { + // ensure we have flushed any events in the queue + flushFrameworkEvents(); + // close and clear out the event manager + resetEventManager(null); + // make sure we clear out all the remaining listeners + allBundleListeners.clear(); + allSyncBundleListeners.clear(); + allFrameworkListeners.clear(); + } + + private void resetEventManager(EventManager newEventManager) { + EventManager currentEventManager; + synchronized (this.monitor) { + currentEventManager = eventManager; + eventManager = newEventManager; + } + if (currentEventManager != null) { + currentEventManager.close(); + } + } + + public <K, V, E> ListenerQueue<K, V, E> newListenerQueue() { + synchronized (this.monitor) { + return new ListenerQueue<K, V, E>(eventManager); + } + } + + private boolean isEventManagerSet() { + synchronized (this.monitor) { + return eventManager != null; + } + } + /** * Deliver a BundleEvent to SynchronousBundleListeners (synchronous) and * BundleListeners (asynchronous). @@ -84,6 +129,9 @@ public class EquinoxEventPublisher { } void publishBundleEventPrivileged(BundleEvent event) { + if (!isEventManagerSet()) { + return; + } /* * We must collect the snapshots of the sync and async listeners * BEFORE we dispatch the event. @@ -155,7 +203,7 @@ public class EquinoxEventPublisher { /* Dispatch the event to the snapshot for sync listeners */ if (!listenersSync.isEmpty()) { - ListenerQueue<SynchronousBundleListener, SynchronousBundleListener, BundleEvent> queue = container.newListenerQueue(); + ListenerQueue<SynchronousBundleListener, SynchronousBundleListener, BundleEvent> queue = newListenerQueue(); for (Map.Entry<BundleContextImpl, Set<Map.Entry<SynchronousBundleListener, SynchronousBundleListener>>> entry : listenersSync.entrySet()) { @SuppressWarnings({"rawtypes", "unchecked"}) EventDispatcher<SynchronousBundleListener, SynchronousBundleListener, BundleEvent> dispatcher = (EventDispatcher) entry.getKey(); @@ -167,7 +215,7 @@ public class EquinoxEventPublisher { /* Dispatch the event to the snapshot for async listeners */ if ((listenersAsync != null) && !listenersAsync.isEmpty()) { - ListenerQueue<BundleListener, BundleListener, BundleEvent> queue = container.newListenerQueue(); + ListenerQueue<BundleListener, BundleListener, BundleEvent> queue = newListenerQueue(); for (Map.Entry<BundleContextImpl, Set<Map.Entry<BundleListener, BundleListener>>> entry : listenersAsync.entrySet()) { @SuppressWarnings({"rawtypes", "unchecked"}) EventDispatcher<BundleListener, BundleListener, BundleEvent> dispatcher = (EventDispatcher) entry.getKey(); @@ -183,21 +231,24 @@ public class EquinoxEventPublisher { Debug.println("notifyBundleEventHooks(" + event.getType() + ":" + event.getBundle() + ", " + result + " )"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ } - container.getServiceRegistry().notifyHooksPrivileged(new HookContext() { - public void call(Object hook, ServiceRegistration<?> hookRegistration) throws Exception { - if (hook instanceof EventHook) { - ((EventHook) hook).event(event, result); + ServiceRegistry serviceRegistry = container.getServiceRegistry(); + if (serviceRegistry != null) { + serviceRegistry.notifyHooksPrivileged(new HookContext() { + public void call(Object hook, ServiceRegistration<?> hookRegistration) throws Exception { + if (hook instanceof EventHook) { + ((EventHook) hook).event(event, result); + } } - } - public String getHookClassName() { - return eventHookName; - } + public String getHookClassName() { + return eventHookName; + } - public String getHookMethodName() { - return "event"; //$NON-NLS-1$ - } - }); + public String getHookMethodName() { + return "event"; //$NON-NLS-1$ + } + }); + } } /** @@ -231,6 +282,9 @@ public class EquinoxEventPublisher { } public void publishFrameworkEventPrivileged(FrameworkEvent event, FrameworkListener... callerListeners) { + if (!isEventManagerSet()) { + return; + } // Build the listener snapshot Map<BundleContextImpl, Set<Map.Entry<FrameworkListener, FrameworkListener>>> listenerSnapshot; synchronized (allFrameworkListeners) { @@ -245,7 +299,7 @@ public class EquinoxEventPublisher { // If framework event hook were defined they would be called here // deliver the event to the snapshot - ListenerQueue<FrameworkListener, FrameworkListener, FrameworkEvent> queue = container.newListenerQueue(); + ListenerQueue<FrameworkListener, FrameworkListener, FrameworkEvent> queue = newListenerQueue(); // add the listeners specified by the caller first if (callerListeners != null && callerListeners.length > 0) { @@ -271,6 +325,10 @@ public class EquinoxEventPublisher { } queue.dispatchEventAsynchronous(FRAMEWORKEVENT, event); + // close down the publisher if we got the stopped event + if ((event.getType() & FRAMEWORK_STOPPED_MASK) != 0) { + close(); + } } /** @@ -344,14 +402,42 @@ public class EquinoxEventPublisher { } void removeAllListeners(BundleContextImpl context) { - synchronized (allBundleListeners) { - allBundleListeners.remove(context); - } - synchronized (allSyncBundleListeners) { - allSyncBundleListeners.remove(context); + // leave any left over listeners until the framework STOPPED event + if (context.getBundleImpl().getBundleId() != 0) { + synchronized (allBundleListeners) { + allBundleListeners.remove(context); + } + synchronized (allSyncBundleListeners) { + allSyncBundleListeners.remove(context); + } } synchronized (allFrameworkListeners) { allFrameworkListeners.remove(context); } } + + void flushFrameworkEvents() { + EventDispatcher<Object, Object, CountDownLatch> dispatcher = new EventDispatcher<Object, Object, CountDownLatch>() { + @Override + public void dispatchEvent(Object eventListener, Object listenerObject, int eventAction, CountDownLatch flushedSignal) { + // Signal that we have flushed all events + flushedSignal.countDown(); + } + }; + + ListenerQueue<Object, Object, CountDownLatch> queue = newListenerQueue(); + queue.queueListeners(Collections.<Object, Object> singletonMap(dispatcher, dispatcher).entrySet(), dispatcher); + + // fire event with the flushedSignal latch + CountDownLatch flushedSignal = new CountDownLatch(1); + queue.dispatchEventAsynchronous(0, flushedSignal); + + try { + // Wait for the flush signal; timeout after 30 seconds + flushedSignal.await(30, TimeUnit.SECONDS); + } catch (InterruptedException e) { + // ignore but reset the interrupted flag + Thread.currentThread().interrupt(); + } + } } |