package org.opendaylight.controller.config.manager.impl.osgi;
import java.util.AbstractMap;
+import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
-import java.util.Map.Entry;
import org.opendaylight.controller.config.manager.impl.factoriesresolver.ModuleFactoriesResolver;
import org.opendaylight.controller.config.spi.ModuleFactory;
import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
ModuleFactoriesResolver {
private static final Logger LOG = LoggerFactory
.getLogger(BundleContextBackedModuleFactoriesResolver.class);
- private ModuleFactoryBundleTracker moduleFactoryBundleTracker;
+ private final BundleContext bundleContext;
- public BundleContextBackedModuleFactoriesResolver() {
- }
-
- public void setModuleFactoryBundleTracker(ModuleFactoryBundleTracker moduleFactoryBundleTracker) {
- this.moduleFactoryBundleTracker = moduleFactoryBundleTracker;
+ public BundleContextBackedModuleFactoriesResolver(
+ BundleContext bundleContext) {
+ this.bundleContext = bundleContext;
}
@Override
public Map<String, Map.Entry<ModuleFactory, BundleContext>> getAllFactories() {
- Map<String, Map.Entry<ModuleFactory, BundleContext>> result = new HashMap<>();
- for(Entry<ModuleFactory, BundleContext> entry: moduleFactoryBundleTracker.getModuleFactoryEntries()) {
- ModuleFactory factory = entry.getKey();
- BundleContext bundleContext = entry.getValue();
- String moduleName = factory .getImplementationName();
- if (moduleName == null || moduleName.isEmpty()) {
- throw new IllegalStateException("Invalid implementation name for " + factory);
+ Collection<ServiceReference<ModuleFactory>> serviceReferences;
+ try {
+ serviceReferences = bundleContext.getServiceReferences(
+ ModuleFactory.class, null);
+ } catch (InvalidSyntaxException e) {
+ throw new IllegalStateException(e);
+ }
+ Map<String, Map.Entry<ModuleFactory, BundleContext>> result = new HashMap<>(serviceReferences.size());
+ for (ServiceReference<ModuleFactory> serviceReference : serviceReferences) {
+ ModuleFactory factory = bundleContext.getService(serviceReference);
+ // null if the service is not registered, the service object
+ // returned by a ServiceFactory does not
+ // implement the classes under which it was registered or the
+ // ServiceFactory threw an exception.
+ if(factory == null) {
+ throw new NullPointerException("ServiceReference of class" + serviceReference.getClass() + "not found.");
}
- LOG.debug("Processing factory {} {}", moduleName, factory);
+ String moduleName = factory.getImplementationName();
+ if (moduleName == null || moduleName.isEmpty()) {
+ throw new IllegalStateException(
+ "Invalid implementation name for " + factory);
+ }
+ if (serviceReference.getBundle() == null || serviceReference.getBundle().getBundleContext() == null) {
+ throw new NullPointerException("Bundle context of " + factory + " ModuleFactory not found.");
+ }
+ LOG.debug("Reading factory {} {}", moduleName, factory);
Map.Entry<ModuleFactory, BundleContext> conflicting = result.get(moduleName);
if (conflicting != null) {
LOG.error(error);
throw new IllegalArgumentException(error);
} else {
- result.put(moduleName, new AbstractMap.SimpleImmutableEntry<>(factory, bundleContext));
+ result.put(moduleName, new AbstractMap.SimpleImmutableEntry<>(factory,
+ serviceReference.getBundle().getBundleContext()));
}
}
-
return result;
}
}
import org.opendaylight.controller.config.spi.ModuleFactory;
import org.opendaylight.yangtools.sal.binding.generator.impl.GeneratedClassLoadingStrategy;
import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext;
-import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.util.tracker.ServiceTracker;
ModuleInfoBundleTracker moduleInfoBundleTracker = new ModuleInfoBundleTracker(moduleInfoRegistryWrapper);
// start config registry
- BundleContextBackedModuleFactoriesResolver bundleContextBackedModuleFactoriesResolver = new BundleContextBackedModuleFactoriesResolver();
+ BundleContextBackedModuleFactoriesResolver bundleContextBackedModuleFactoriesResolver = new BundleContextBackedModuleFactoriesResolver(
+ context);
ConfigRegistryImpl configRegistry = new ConfigRegistryImpl(bundleContextBackedModuleFactoriesResolver, configMBeanServer,
bindingContextProvider);
configRegistry);
ModuleFactoryBundleTracker primaryModuleFactoryBundleTracker = new ModuleFactoryBundleTracker(
blankTransactionServiceTracker);
- bundleContextBackedModuleFactoriesResolver.setModuleFactoryBundleTracker(primaryModuleFactoryBundleTracker);
// start extensible tracker
ExtensibleBundleTracker<?> bundleTracker = new ExtensibleBundleTracker<>(context,
- Bundle.RESOLVED | Bundle.STARTING | Bundle.ACTIVE,
- primaryModuleFactoryBundleTracker, moduleInfoBundleTracker);
+ primaryModuleFactoryBundleTracker, moduleInfoBundleTracker);
bundleTracker.open();
// Wrap config registry with JMX notification publishing adapter
*/
package org.opendaylight.controller.config.manager.impl.osgi;
-import com.google.common.util.concurrent.MoreExecutors;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
import java.util.concurrent.Future;
+import java.util.concurrent.ThreadFactory;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
* @param <T>
*/
public final class ExtensibleBundleTracker<T> extends BundleTracker<Future<T>> {
-
+ private static final ThreadFactory THREAD_FACTORY = new ThreadFactoryBuilder()
+ .setNameFormat("config-bundle-tracker-%d").build();
private final ExecutorService eventExecutor;
private final BundleTrackerCustomizer<T> primaryTracker;
private final BundleTrackerCustomizer<?>[] additionalTrackers;
private static final Logger LOG = LoggerFactory.getLogger(ExtensibleBundleTracker.class);
public ExtensibleBundleTracker(final BundleContext context, final BundleTrackerCustomizer<T> primaryBundleTrackerCustomizer,
- final BundleTrackerCustomizer<?>... additionalBundleTrackerCustomizers) {
+ final BundleTrackerCustomizer<?>... additionalBundleTrackerCustomizers) {
this(context, Bundle.ACTIVE, primaryBundleTrackerCustomizer, additionalBundleTrackerCustomizers);
}
public ExtensibleBundleTracker(final BundleContext context, final int bundleState,
- final BundleTrackerCustomizer<T> primaryBundleTrackerCustomizer,
- final BundleTrackerCustomizer<?>... additionalBundleTrackerCustomizers) {
+ final BundleTrackerCustomizer<T> primaryBundleTrackerCustomizer,
+ final BundleTrackerCustomizer<?>... additionalBundleTrackerCustomizers) {
super(context, bundleState, null);
this.primaryTracker = primaryBundleTrackerCustomizer;
this.additionalTrackers = additionalBundleTrackerCustomizers;
- eventExecutor = MoreExecutors.newDirectExecutorService();
+ eventExecutor = Executors.newSingleThreadExecutor(THREAD_FACTORY);
LOG.trace("Registered as extender with context {} and bundle state {}", context, bundleState);
}
import static java.lang.String.format;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Charsets;
-import com.google.common.base.Stopwatch;
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.Multimap;
import com.google.common.io.Resources;
-import com.google.common.util.concurrent.Uninterruptibles;
import java.io.IOException;
import java.net.URL;
-import java.util.AbstractMap;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.concurrent.TimeUnit;
-import javax.annotation.concurrent.GuardedBy;
import org.opendaylight.controller.config.spi.ModuleFactory;
import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
+import org.osgi.framework.ServiceRegistration;
import org.osgi.util.tracker.BundleTrackerCustomizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
* Code based on http://www.toedter.com/blog/?p=236
*/
public class ModuleFactoryBundleTracker implements BundleTrackerCustomizer<Object> {
- private static final Logger LOG = LoggerFactory.getLogger(ModuleFactoryBundleTracker.class);
- private static final long BUNDLE_CONTEXT_TIMEOUT = TimeUnit.MILLISECONDS.convert(60, TimeUnit.SECONDS);
-
private final BlankTransactionServiceTracker blankTransactionServiceTracker;
-
- @GuardedBy(value = "bundleModuleFactoryMap")
- private final Multimap<BundleKey, ModuleFactory> bundleModuleFactoryMap = HashMultimap.create();
+ private static final Logger LOG = LoggerFactory.getLogger(ModuleFactoryBundleTracker.class);
public ModuleFactoryBundleTracker(BlankTransactionServiceTracker blankTransactionServiceTracker) {
this.blankTransactionServiceTracker = blankTransactionServiceTracker;
@Override
public Object addingBundle(Bundle bundle, BundleEvent event) {
- if(event != null && (event.getType() == BundleEvent.STOPPED || event.getType() == BundleEvent.STOPPING)) {
- // We're tracking RESOLVED, STARTING, and ACTIVE states but not STOPPING. So when a bundle transitions
- // to STOPPING, removedBundle gets called and we remove the ModuleFactory entries. However the
- // bundle will then transition to RESOLVED which will cause the tracker to notify addingBundle.
- // In this case we don't want to re-add the ModuleFactory entries so we check if the BundleEvent
- // is STOPPED.
- return null;
- }
-
URL resource = bundle.getEntry("META-INF/services/" + ModuleFactory.class.getName());
LOG.trace("Got addingBundle event of bundle {}, resource {}, event {}",
bundle, resource, event);
if (resource != null) {
try {
for (String factoryClassName : Resources.readLines(resource, Charsets.UTF_8)) {
- Entry<ModuleFactory, Bundle> moduleFactoryEntry = registerFactory(factoryClassName, bundle);
- synchronized (bundleModuleFactoryMap) {
- bundleModuleFactoryMap.put(new BundleKey(moduleFactoryEntry.getValue()),
- moduleFactoryEntry.getKey());
- }
+ registerFactory(factoryClassName, bundle);
}
} catch (IOException e) {
LOG.error("Error while reading {}", resource, e);
@Override
public void removedBundle(Bundle bundle, BundleEvent event, Object object) {
- synchronized (bundleModuleFactoryMap) {
- bundleModuleFactoryMap.removeAll(new BundleKey(bundle));
- }
-
// workaround for service tracker not getting removed service event
blankTransactionServiceTracker.blankTransaction();
}
- public Collection<Map.Entry<ModuleFactory, BundleContext>> getModuleFactoryEntries() {
- Collection<Entry<BundleKey, ModuleFactory>> entries;
- synchronized (bundleModuleFactoryMap) {
- entries = new ArrayList<>(bundleModuleFactoryMap.entries());
- }
-
- Collection<Map.Entry<ModuleFactory, BundleContext>> result = new ArrayList<>(entries.size());
- for(Entry<BundleKey, ModuleFactory> entry: entries) {
- BundleContext context = entry.getKey().getBundleContext();
- if(context == null) {
- LOG.warn("Bundle context for {} ModuleFactory not found", entry.getValue());
- } else {
- result.add(new AbstractMap.SimpleImmutableEntry<>(entry.getValue(), context));
- }
- }
-
- return result;
- }
-
@VisibleForTesting
- protected static Map.Entry<ModuleFactory, Bundle> registerFactory(String factoryClassName, Bundle bundle) {
+ protected static ServiceRegistration<?> registerFactory(String factoryClassName, Bundle bundle) {
String errorMessage;
Exception ex = null;
try {
Class<?> clazz = bundle.loadClass(factoryClassName);
if (ModuleFactory.class.isAssignableFrom(clazz)) {
try {
- LOG.debug("Registering {} in bundle {}", clazz.getName(), bundle);
-
- return new AbstractMap.SimpleImmutableEntry<>((ModuleFactory)clazz.newInstance(), bundle);
+ LOG.debug("Registering {} in bundle {}",
+ clazz.getName(), bundle);
+ return bundle.getBundleContext().registerService(
+ ModuleFactory.class.getName(), clazz.newInstance(),
+ null);
} catch (InstantiationException e) {
errorMessage = logMessage(
"Could not instantiate {} in bundle {}, reason {}",
"Illegal access during instantiation of class {} in bundle {}, reason {}",
factoryClassName, bundle, e);
ex = e;
- } catch (RuntimeException e) {
- errorMessage = logMessage(
- "Unexpected exception during instantiation of class {} in bundle {}, reason {}",
- clazz, bundle.getBundleContext(), e);
- ex = e;
}
} else {
errorMessage = logMessage(
String formatMessage = slfMessage.replaceAll("\\{\\}", "%s");
return format(formatMessage, params);
}
-
- private static class BundleKey {
- Bundle bundle;
- BundleContext bundleContext;
-
- public BundleKey(Bundle bundle) {
- this.bundle = bundle;
- }
-
- BundleContext getBundleContext() {
- if(bundleContext != null) {
- return bundleContext;
- }
-
- // If the bundle isn't activated yet, it may not have a BundleContext yet so busy wait for it.
- Stopwatch timer = Stopwatch.createStarted();
- while(timer.elapsed(TimeUnit.MILLISECONDS) <= BUNDLE_CONTEXT_TIMEOUT) {
- bundleContext = bundle.getBundleContext();
- if(bundleContext != null) {
- return bundleContext;
- }
-
- Uninterruptibles.sleepUninterruptibly(10, TimeUnit.MILLISECONDS);
- }
-
- return null;
- }
-
- @Override
- public int hashCode() {
- return (int) bundle.getBundleId();
- }
-
- @Override
- public boolean equals(Object obj) {
- if (getClass() != obj.getClass()) {
- return false;
- }
- BundleKey other = (BundleKey) obj;
- return bundle.getBundleId() == other.bundle.getBundleId();
- }
- }
}
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
-import com.google.common.util.concurrent.Uninterruptibles;
-import java.util.AbstractMap;
-import java.util.Arrays;
+import com.google.common.collect.Lists;
import java.util.Map;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
import org.opendaylight.controller.config.spi.ModuleFactory;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
public class BundleContextBackedModuleFactoriesResolverTest {
@Mock
- private ModuleFactoryBundleTracker mockBundleTracker;
-
- @Mock
- private BundleContext mockBundleContext1;
-
- @Mock
- private BundleContext mockBundleContext2;
-
- @Mock
- private Bundle mockBundle1;
-
- @Mock
- private Bundle mockBundle2;
-
+ private BundleContext bundleContext;
private BundleContextBackedModuleFactoriesResolver resolver;
+ private ServiceReference<?> s1;
+ private ServiceReference<?> s2;
private ModuleFactory f1;
private ModuleFactory f2;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- doReturn(mockBundleContext1).when(mockBundle1).getBundleContext();
- doReturn(mockBundleContext2).when(mockBundle2).getBundleContext();
-
+ s1 = getServiceRef();
+ s2 = getServiceRef();
+ doReturn(Lists.newArrayList(s1, s2)).when(bundleContext).getServiceReferences(ModuleFactory.class, null);
f1 = getMockFactory("f1");
+ doReturn(f1).when(bundleContext).getService(s1);
f2 = getMockFactory("f2");
-
- resolver = new BundleContextBackedModuleFactoriesResolver();
- resolver.setModuleFactoryBundleTracker(mockBundleTracker);
+ doReturn(f2).when(bundleContext).getService(s2);
+ resolver = new BundleContextBackedModuleFactoriesResolver(bundleContext);
}
private ModuleFactory getMockFactory(final String name) {
return mock;
}
+ private ServiceReference<?> getServiceRef() {
+ ServiceReference<?> mock = mock(ServiceReference.class);
+ doReturn("serviceRef").when(mock).toString();
+ final Bundle bundle = mock(Bundle.class);
+ doReturn(bundleContext).when(bundle).getBundleContext();
+ doReturn(bundle).when(mock).getBundle();
+ return mock;
+ }
+
@Test
public void testGetAllFactories() throws Exception {
- doReturn(Arrays.asList(new AbstractMap.SimpleImmutableEntry<>(f1, mockBundleContext1),
- new AbstractMap.SimpleImmutableEntry<>(f2, mockBundleContext2))).
- when(mockBundleTracker).getModuleFactoryEntries();
-
Map<String, Map.Entry<ModuleFactory, BundleContext>> allFactories = resolver.getAllFactories();
assertEquals(2, allFactories.size());
assertTrue(allFactories.containsKey(f1.getImplementationName()));
assertEquals(f1, allFactories.get(f1.getImplementationName()).getKey());
- assertEquals(mockBundleContext1, allFactories.get(f1.getImplementationName()).getValue());
+ assertEquals(bundleContext, allFactories.get(f1.getImplementationName()).getValue());
assertTrue(allFactories.containsKey(f2.getImplementationName()));
assertEquals(f2, allFactories.get(f2.getImplementationName()).getKey());
- assertEquals(mockBundleContext2, allFactories.get(f2.getImplementationName()).getValue());
+ assertEquals(bundleContext, allFactories.get(f2.getImplementationName()).getValue());
}
@Test
public void testDuplicateFactories() throws Exception {
- doReturn(Arrays.asList(new AbstractMap.SimpleImmutableEntry<>(f1, mockBundleContext1),
- new AbstractMap.SimpleImmutableEntry<>(f1, mockBundleContext2))).
- when(mockBundleTracker).getModuleFactoryEntries();
-
+ doReturn(f1).when(bundleContext).getService(s2);
try {
resolver.getAllFactories();
} catch (Exception e) {
fail("Should fail with duplicate factory name");
}
+ @Test(expected = NullPointerException.class)
+ public void testNullFactory() throws Exception {
+ doReturn(null).when(bundleContext).getService(s2);
+ resolver.getAllFactories();
+ }
+
@Test(expected = IllegalStateException.class)
public void testNullFactoryName() throws Exception {
- doReturn(Arrays.asList(new AbstractMap.SimpleImmutableEntry<>(f1, mockBundleContext1))).
- when(mockBundleTracker).getModuleFactoryEntries();
-
doReturn(null).when(f1).getImplementationName();
resolver.getAllFactories();
}
- @Test
- public void testBundleContextInitiallyNull() throws Exception {
- final AtomicReference<BundleContext> bundleContext = new AtomicReference<>();
- Answer<BundleContext> answer = new Answer<BundleContext>() {
- @Override
- public BundleContext answer(InvocationOnMock invocation) throws Throwable {
- return bundleContext.get();
- }
- };
-
- doAnswer(answer).when(mockBundle1).getBundleContext();
- doReturn(Arrays.asList(new AbstractMap.SimpleImmutableEntry<>(f1, mockBundleContext1))).
- when(mockBundleTracker).getModuleFactoryEntries();
-
- final AtomicReference<Map<String, Map.Entry<ModuleFactory, BundleContext>>> allFactories = new AtomicReference<>();
- final AtomicReference<Exception> caughtEx = new AtomicReference<>();
- final CountDownLatch doneLatch = new CountDownLatch(1);
- new Thread() {
- @Override
- public void run() {
- try {
- allFactories.set(resolver.getAllFactories());
- } catch (Exception e) {
- caughtEx.set(e);
- } finally {
- doneLatch.countDown();
- }
- }
- }.start();
-
- Uninterruptibles.sleepUninterruptibly(500, TimeUnit.MILLISECONDS);
- bundleContext.set(mockBundleContext1);
-
- assertEquals(true, doneLatch.await(5, TimeUnit.SECONDS));
- if(caughtEx.get() != null) {
- throw caughtEx.get();
- }
-
- assertEquals(1, allFactories.get().size());
- assertTrue(allFactories.get().containsKey(f1.getImplementationName()));
+ @Test(expected = NullPointerException.class)
+ public void testNullBundleName() throws Exception {
+ doReturn(null).when(s1).getBundle();
+ resolver.getAllFactories();
}
}
import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
-import java.util.Collection;
import java.util.Dictionary;
-import java.util.Map.Entry;
import java.util.Set;
import org.junit.Before;
import org.junit.Test;
private BundleContext context;
@Mock
private ServiceRegistration<?> reg;
- @Mock
- private BlankTransactionServiceTracker blankTxTracker;
@Before
public void setUp() throws Exception {
}
}).when(bundle).loadClass(anyString());
doReturn("mockBundle").when(bundle).toString();
- doReturn("mockBundleContext").when(context).toString();
doReturn(context).when(bundle).getBundleContext();
- doReturn(100L).when(bundle).getBundleId();
doReturn(reg).when(context).registerService(anyString(), anyObject(), any(Dictionary.class));
}
@Test
public void testRegisterFactory() throws Exception {
- Entry<ModuleFactory, Bundle> entry = ModuleFactoryBundleTracker.registerFactory(
- TestingFactory.class.getName(), bundle);
- assertEquals(TestingFactory.currentInstance, entry.getKey());
- assertEquals(bundle, entry.getValue());
+ ModuleFactoryBundleTracker.registerFactory(TestingFactory.class.getName(), bundle);
+ verify(context).registerService(ModuleFactory.class.getName(), TestingFactory.currentInstance, null);
}
@Test
fail("Cannot register without extend");
}
+ @Mock
+ private BlankTransactionServiceTracker blankTxTracker;
+
@Test
- public void testBundleAddAndRemove() throws Exception {
+ public void testAddingBundle() throws Exception {
final ModuleFactoryBundleTracker tracker = new ModuleFactoryBundleTracker(blankTxTracker);
doReturn(getClass().getResource("/module-factories/module-factory-ok")).when(bundle).getEntry(anyString());
- tracker.addingBundle(bundle, null);
-
- Collection<Entry<ModuleFactory, BundleContext>> entries = tracker.getModuleFactoryEntries();
- assertNotNull(entries);
- assertEquals(1, entries.size());
- Entry<ModuleFactory, BundleContext> entry = entries.iterator().next();
- assertEquals(TestingFactory.currentInstance, entry.getKey());
- assertEquals(context, entry.getValue());
-
- doNothing().when(blankTxTracker).blankTransaction();;
-
- BundleEvent mockEvent = mock(BundleEvent.class);
- doReturn(BundleEvent.STOPPING).when(mockEvent).getType();
-
- tracker.removedBundle(bundle, mockEvent, bundle);
-
- entries = tracker.getModuleFactoryEntries();
- assertNotNull(entries);
- assertEquals(0, entries.size());
-
- verify(blankTxTracker).blankTransaction();
-
- reset(mockEvent);
- doReturn(BundleEvent.STOPPED).when(mockEvent).getType();
-
- tracker.addingBundle(bundle, mockEvent);
-
- assertEquals(0, tracker.getModuleFactoryEntries().size());
+ tracker.addingBundle(bundle, mock(BundleEvent.class));
+ verify(context).registerService(ModuleFactory.class.getName(), TestingFactory.currentInstance, null);
}
@Test