Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Watson2015-10-20 14:45:42 +0000
committerThomas Watson2015-10-20 14:45:42 +0000
commitdd65305bf1442ab1ff941bb2896ee62935fc85c3 (patch)
tree98ec1fc6a9e1117f1b6563f71811bdb707594e40
parenta83aaac9100f28c7a2cf1dc8894a5d177d2916d9 (diff)
downloadrt.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)
-rw-r--r--bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/SystemBundleTests.java49
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/BundleContextImpl.java14
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/EquinoxBundle.java31
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/EquinoxContainer.java18
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/framework/EquinoxEventPublisher.java130
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();
+ }
+ }
}

Back to the top