/******************************************************************************* * Copyright (c) 2011, 2015 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.equinox.region.tests.system; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.IOException; import java.lang.management.ManagementFactory; import java.net.MalformedURLException; import java.net.URL; import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; import javax.management.*; import org.eclipse.equinox.region.*; import org.eclipse.equinox.region.RegionDigraph.FilteredRegion; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestName; import org.osgi.framework.*; import org.osgi.framework.hooks.bundle.CollisionHook; import org.osgi.framework.hooks.bundle.EventHook; import org.osgi.framework.hooks.resolver.ResolverHook; import org.osgi.framework.hooks.resolver.ResolverHookFactory; import org.osgi.framework.wiring.*; import org.osgi.util.tracker.ServiceTracker; public class RegionSystemTests extends AbstractRegionSystemTest { @Rule public TestName testName = new TestName(); private static final long TEST_BUNDLE_ID = 452345245L; @Test public void testBasic() throws BundleException, InvalidSyntaxException, InterruptedException { // get the system region Region systemRegion = digraph.getRegion(0); // create a disconnected test region Region testRegion = digraph.createRegion(testName.getMethodName()); List<Bundle> bundles = new ArrayList<Bundle>(); // Install all test bundles Bundle pp1, cp2, sc1; bundles.add(pp1 = bundleInstaller.installBundle(PP1, testRegion)); // should be able to start pp1 because it depends on nothing pp1.start(); // do a sanity check that we have no services available in the isolated region assertNull("Found some services.", pp1.getBundleContext().getAllServiceReferences(null, null)); assertEquals("Found extra bundles in region", 1, pp1.getBundleContext().getBundles().length); pp1.stop(); bundles.add(bundleInstaller.installBundle(SP1, testRegion)); bundles.add(bundleInstaller.installBundle(CP1, testRegion)); bundles.add(bundleInstaller.installBundle(PP2, testRegion)); bundles.add(bundleInstaller.installBundle(SP2, testRegion)); bundles.add(cp2 = bundleInstaller.installBundle(CP2, testRegion)); bundles.add(bundleInstaller.installBundle(PC1, testRegion)); bundles.add(bundleInstaller.installBundle(BC1, testRegion)); bundles.add(sc1 = bundleInstaller.installBundle(SC1, testRegion)); bundles.add(bundleInstaller.installBundle(CC1, testRegion)); // Import the system bundle from the systemRegion digraph.connect(testRegion, digraph.createRegionFilterBuilder().allow(RegionFilter.VISIBLE_BUNDLE_NAMESPACE, "(id=0)").build(), systemRegion); // must import Boolean services into systemRegion to test digraph.connect(systemRegion, digraph.createRegionFilterBuilder().allow(RegionFilter.VISIBLE_SERVICE_NAMESPACE, "(objectClass=java.lang.Boolean)").build(), testRegion); bundleInstaller.resolveBundles(bundles.toArray(new Bundle[bundles.size()])); for (Bundle bundle : bundles) { assertEquals("Bundle did not resolve: " + bundle.getSymbolicName(), Bundle.RESOLVED, bundle.getState()); bundle.start(); } BundleContext context = getContext(); ServiceTracker<Boolean, Boolean> cp2Tracker = new ServiceTracker<Boolean, Boolean>(context, context.createFilter("(&(objectClass=java.lang.Boolean)(bundle.id=" + cp2.getBundleId() + "))"), null); ServiceTracker<Boolean, Boolean> sc1Tracker = new ServiceTracker<Boolean, Boolean>(context, context.createFilter("(&(objectClass=java.lang.Boolean)(bundle.id=" + sc1.getBundleId() + "))"), null); cp2Tracker.open(); sc1Tracker.open(); assertNotNull("The cp2 bundle never found the service.", cp2Tracker.waitForService(2000)); assertNotNull("The sc1 bundle never found the service.", sc1Tracker.waitForService(2000)); cp2Tracker.close(); sc1Tracker.close(); } @Test public void testSingleBundleRegions() throws BundleException, InvalidSyntaxException, InterruptedException { // get the system region Region systemRegion = digraph.getRegion(0); Map<String, Bundle> bundles = new HashMap<String, Bundle>(); // create a disconnected test region for each test bundle for (String location : ALL) { Region testRegion = digraph.createRegion(location); bundles.put(location, bundleInstaller.installBundle(location, testRegion)); // Import the system bundle from the systemRegion digraph.connect(testRegion, digraph.createRegionFilterBuilder().allow(RegionFilter.VISIBLE_BUNDLE_NAMESPACE, "(id=0)").build(), systemRegion); // must import Boolean services into systemRegion to test digraph.connect(systemRegion, digraph.createRegionFilterBuilder().allow(RegionFilter.VISIBLE_SERVICE_NAMESPACE, "(objectClass=java.lang.Boolean)").build(), testRegion); } bundleInstaller.resolveBundles(bundles.values().toArray(new Bundle[bundles.size()])); assertEquals(PP1, Bundle.RESOLVED, bundles.get(PP1).getState()); assertEquals(SP1, Bundle.INSTALLED, bundles.get(SP1).getState()); assertEquals(CP1, Bundle.RESOLVED, bundles.get(CP1).getState()); assertEquals(PP2, Bundle.INSTALLED, bundles.get(PP2).getState()); assertEquals(SP2, Bundle.INSTALLED, bundles.get(SP2).getState()); assertEquals(CP2, Bundle.INSTALLED, bundles.get(CP2).getState()); assertEquals(BC1, Bundle.INSTALLED, bundles.get(BC1).getState()); assertEquals(SC1, Bundle.INSTALLED, bundles.get(SC1).getState()); assertEquals(CC1, Bundle.INSTALLED, bundles.get(CC1).getState()); // now make the necessary connections // SP1 digraph.connect(digraph.getRegion(SP1), digraph.createRegionFilterBuilder().allow(RegionFilter.VISIBLE_PACKAGE_NAMESPACE, "(" + RegionFilter.VISIBLE_PACKAGE_NAMESPACE + "=pkg1.*)").build(), digraph.getRegion(PP1)); // PP2 digraph.connect(digraph.getRegion(PP2), digraph.createRegionFilterBuilder().allow(CP1, "(name=" + CP1 + ")").build(), digraph.getRegion(CP1)); // SP2 digraph.connect(digraph.getRegion(SP2), digraph.createRegionFilterBuilder().allow(RegionFilter.VISIBLE_PACKAGE_NAMESPACE, "(" + RegionFilter.VISIBLE_PACKAGE_NAMESPACE + "=pkg2.*)").build(), digraph.getRegion(PP2)); // CP2 digraph.connect(digraph.getRegion(CP2), digraph.createRegionFilterBuilder().allow(RegionFilter.VISIBLE_PACKAGE_NAMESPACE, "(" + RegionFilter.VISIBLE_PACKAGE_NAMESPACE + "=pkg1.*)").build(), digraph.getRegion(PP1)); digraph.connect(digraph.getRegion(CP2), digraph.createRegionFilterBuilder().allow(RegionFilter.VISIBLE_SERVICE_NAMESPACE, "(" + Constants.OBJECTCLASS + "=pkg1.*)").build(), digraph.getRegion(SP1)); // PC1 digraph.connect(digraph.getRegion(PC1), digraph.createRegionFilterBuilder().allow(RegionFilter.VISIBLE_PACKAGE_NAMESPACE, "(" + RegionFilter.VISIBLE_PACKAGE_NAMESPACE + "=pkg2.*)").build(), digraph.getRegion(PP2)); // BC1 digraph.connect(digraph.getRegion(BC1), digraph.createRegionFilterBuilder().allow(RegionFilter.VISIBLE_REQUIRE_NAMESPACE, "(" + RegionFilter.VISIBLE_REQUIRE_NAMESPACE + "=" + PP2 + ")").build(), digraph.getRegion(PP2)); // SC1 digraph.connect(digraph.getRegion(SC1), digraph.createRegionFilterBuilder().allow(RegionFilter.VISIBLE_PACKAGE_NAMESPACE, "(" + RegionFilter.VISIBLE_PACKAGE_NAMESPACE + "=pkg2.*)").build(), digraph.getRegion(PP2)); digraph.connect(digraph.getRegion(SC1), digraph.createRegionFilterBuilder().allow(RegionFilter.VISIBLE_SERVICE_NAMESPACE, "(" + Constants.OBJECTCLASS + "=pkg2.*)").build(), digraph.getRegion(SP2)); // CC1 digraph.connect(digraph.getRegion(CC1), digraph.createRegionFilterBuilder().allow(CP2, "(name=" + CP2 + ")").build(), digraph.getRegion(CP2)); bundleInstaller.resolveBundles(bundles.values().toArray(new Bundle[bundles.size()])); for (Bundle bundle : bundles.values()) { assertEquals("Bundle did not resolve: " + bundle.getSymbolicName(), Bundle.RESOLVED, bundle.getState()); bundle.start(); } BundleContext context = getContext(); ServiceTracker<Boolean, Boolean> cp2Tracker = new ServiceTracker<Boolean, Boolean>(context, context.createFilter("(&(objectClass=java.lang.Boolean)(bundle.id=" + bundles.get(CP2).getBundleId() + "))"), null); ServiceTracker<Boolean, Boolean> sc1Tracker = new ServiceTracker<Boolean, Boolean>(context, context.createFilter("(&(objectClass=java.lang.Boolean)(bundle.id=" + bundles.get(SC1).getBundleId() + "))"), null); cp2Tracker.open(); sc1Tracker.open(); assertNotNull("The cp2 bundle never found the service.", cp2Tracker.waitForService(2000)); assertNotNull("The sc1 bundle never found the service.", sc1Tracker.waitForService(2000)); cp2Tracker.close(); sc1Tracker.close(); } // public void testPersistence() throws BundleException, InvalidSyntaxException { // // get the system region // Region systemRegion = digraph.getRegion(0); // Map<String, Bundle> bundles = new HashMap<String, Bundle>(); // // create a disconnected test region for each test bundle // for (String location : ALL) { // Region testRegion = digraph.createRegion(location); // bundles.put(location, bundleInstaller.installBundle(location, testRegion)); // // Import the system bundle from the systemRegion // digraph.connect(testRegion, digraph.createRegionFilterBuilder().allow(RegionFilter.VISIBLE_BUNDLE_NAMESPACE, "(id=0)").build(), systemRegion); // // must import Boolean services into systemRegion to test // digraph.connect(systemRegion, digraph.createRegionFilterBuilder().allow(RegionFilter.VISIBLE_SERVICE_NAMESPACE, "(objectClass=java.lang.Boolean)").build(), testRegion); // } // // bundleInstaller.resolveBundles(bundles.values().toArray(new Bundle[bundles.size()])); // assertEquals(PP1, Bundle.RESOLVED, bundles.get(PP1).getState()); // assertEquals(SP1, Bundle.INSTALLED, bundles.get(SP1).getState()); // assertEquals(CP1, Bundle.RESOLVED, bundles.get(CP1).getState()); // assertEquals(PP2, Bundle.INSTALLED, bundles.get(PP2).getState()); // assertEquals(SP2, Bundle.INSTALLED, bundles.get(SP2).getState()); // assertEquals(CP2, Bundle.INSTALLED, bundles.get(CP2).getState()); // assertEquals(BC1, Bundle.INSTALLED, bundles.get(BC1).getState()); // assertEquals(SC1, Bundle.INSTALLED, bundles.get(SC1).getState()); // assertEquals(CC1, Bundle.INSTALLED, bundles.get(CC1).getState()); // // regionBundle.stop(); // bundleInstaller.resolveBundles(bundles.values().toArray(new Bundle[bundles.size()])); // assertEquals(PP1, Bundle.RESOLVED, bundles.get(PP1).getState()); // assertEquals(SP1, Bundle.RESOLVED, bundles.get(SP1).getState()); // assertEquals(CP1, Bundle.RESOLVED, bundles.get(CP1).getState()); // assertEquals(PP2, Bundle.RESOLVED, bundles.get(PP2).getState()); // assertEquals(SP2, Bundle.RESOLVED, bundles.get(SP2).getState()); // assertEquals(CP2, Bundle.RESOLVED, bundles.get(CP2).getState()); // assertEquals(BC1, Bundle.RESOLVED, bundles.get(BC1).getState()); // assertEquals(SC1, Bundle.RESOLVED, bundles.get(SC1).getState()); // assertEquals(CC1, Bundle.RESOLVED, bundles.get(CC1).getState()); // // startRegionBundle(); // // bundleInstaller.refreshPackages(bundles.values().toArray(new Bundle[bundles.size()])); // assertEquals(PP1, Bundle.RESOLVED, bundles.get(PP1).getState()); // assertEquals(SP1, Bundle.INSTALLED, bundles.get(SP1).getState()); // assertEquals(CP1, Bundle.RESOLVED, bundles.get(CP1).getState()); // assertEquals(PP2, Bundle.INSTALLED, bundles.get(PP2).getState()); // assertEquals(SP2, Bundle.INSTALLED, bundles.get(SP2).getState()); // assertEquals(CP2, Bundle.INSTALLED, bundles.get(CP2).getState()); // assertEquals(BC1, Bundle.INSTALLED, bundles.get(BC1).getState()); // assertEquals(SC1, Bundle.INSTALLED, bundles.get(SC1).getState()); // assertEquals(CC1, Bundle.INSTALLED, bundles.get(CC1).getState()); // } // // public void testPersistenceBug343020() throws BundleException, InvalidSyntaxException { // // get the system region // Region systemRegion = digraph.getRegion(0); // // create a test region // Region testRegion = digraph.createRegion(getName()); // // RegionFilterBuilder builder = digraph.createRegionFilterBuilder(); // // Import the system bundle from the systemRegion // builder.allow(RegionFilter.VISIBLE_BUNDLE_NAMESPACE, "(id=0)"); // // import PP1 // builder.allow(RegionFilter.VISIBLE_PACKAGE_NAMESPACE, "(" + RegionFilter.VISIBLE_PACKAGE_NAMESPACE + "=pkg1.*)"); // digraph.connect(testRegion, builder.build(), systemRegion); // // install CP2 // Bundle cp2 = bundleInstaller.installBundle(CP2, testRegion); // // bundleInstaller.resolveBundles(new Bundle[] {cp2}); // assertEquals("Wrong state for pc1.", Bundle.INSTALLED, cp2.getState()); // // regionBundle.stop(); // // // install PP1 there is no region alive // bundleInstaller.installBundle(PP1); // // // start region bundle and confirm we can resolve cp2 now // startRegionBundle(); // // bundleInstaller.refreshPackages(new Bundle[] {cp2}); // assertEquals("Wrong state for pc1.", Bundle.RESOLVED, cp2.getState()); // // // stop region bundle to test uninstalling bundles while stopped // regionBundle.stop(); // cp2.uninstall(); // // startRegionBundle(); // testRegion = digraph.getRegion(getName()); // assertNotNull("No test region found.", testRegion); // Set<Long> testIds = testRegion.getBundleIds(); // assertEquals("Wrong number of test ids.", 0, testIds.size()); // } @Test public void testCyclicRegions0() throws BundleException, InvalidSyntaxException, InterruptedException { doCyclicRegions(0); } @Test public void testCyclicRegions10() throws BundleException, InvalidSyntaxException, InterruptedException { doCyclicRegions(10); } @Test public void testCyclicRegions100() throws BundleException, InvalidSyntaxException, InterruptedException { doCyclicRegions(100); } private void doCyclicRegions(int numLevels) throws BundleException, InvalidSyntaxException, InterruptedException { String regionName1 = testName.getMethodName() + "_1"; String regionName2 = testName.getMethodName() + "_2"; // get the system region Region systemRegion = digraph.getRegion(0); // create two regions to hold the bundles Region testRegion1 = digraph.createRegion(regionName1); Region testRegion2 = digraph.createRegion(regionName2); // connect to the system bundle testRegion1.connectRegion(systemRegion, digraph.createRegionFilterBuilder().allow(RegionFilter.VISIBLE_BUNDLE_NAMESPACE, "(id=0)").build()); testRegion2.connectRegion(systemRegion, digraph.createRegionFilterBuilder().allow(RegionFilter.VISIBLE_BUNDLE_NAMESPACE, "(id=0)").build()); // must import Boolean services into systemRegion to test systemRegion.connectRegion(testRegion1, digraph.createRegionFilterBuilder().allow(RegionFilter.VISIBLE_SERVICE_NAMESPACE, "(objectClass=java.lang.Boolean)").build()); systemRegion.connectRegion(testRegion2, digraph.createRegionFilterBuilder().allow(RegionFilter.VISIBLE_SERVICE_NAMESPACE, "(objectClass=java.lang.Boolean)").build()); Map<String, Bundle> bundles = new HashMap<String, Bundle>(); // add bundles to region1 bundles.put(PP1, bundleInstaller.installBundle(PP1, testRegion1)); bundles.put(SP2, bundleInstaller.installBundle(SP2, testRegion1)); bundles.put(CP2, bundleInstaller.installBundle(CP2, testRegion1)); bundles.put(PC1, bundleInstaller.installBundle(PC1, testRegion1)); bundles.put(BC1, bundleInstaller.installBundle(BC1, testRegion1)); // add bundles to region2 bundles.put(SP1, bundleInstaller.installBundle(SP1, testRegion2)); bundles.put(CP1, bundleInstaller.installBundle(CP1, testRegion2)); bundles.put(PP2, bundleInstaller.installBundle(PP2, testRegion2)); bundles.put(SC1, bundleInstaller.installBundle(SC1, testRegion2)); bundles.put(CC1, bundleInstaller.installBundle(CC1, testRegion2)); RegionFilterBuilder testRegionFilter1 = digraph.createRegionFilterBuilder(); // SP2 -> PP2 testRegionFilter1.allow(RegionFilter.VISIBLE_PACKAGE_NAMESPACE, "(" + RegionFilter.VISIBLE_PACKAGE_NAMESPACE + "=pkg2.*)"); // CP2 -> SP1 testRegionFilter1.allow(RegionFilter.VISIBLE_SERVICE_NAMESPACE, "(" + Constants.OBJECTCLASS + "=pkg1.*)"); // PC1 -> PP2 // this is not needed because we already import pkg2.* above //testRegionFilter1.allow(RegionFilter.VISIBLE_PACKAGE_NAMESPACE, "(" + RegionFilter.VISIBLE_PACKAGE_NAMESPACE + "=pkg2.*)"); // BC1 -> PP2 testRegionFilter1.allow(RegionFilter.VISIBLE_REQUIRE_NAMESPACE, "(" + RegionFilter.VISIBLE_REQUIRE_NAMESPACE + "=" + PP2 + ")"); RegionFilterBuilder testRegionFilter2 = digraph.createRegionFilterBuilder(); //SP1 -> PP1 testRegionFilter2.allow(RegionFilter.VISIBLE_PACKAGE_NAMESPACE, "(" + RegionFilter.VISIBLE_PACKAGE_NAMESPACE + "=pkg1.*)"); //SC1 -> SP2 testRegionFilter2.allow(RegionFilter.VISIBLE_SERVICE_NAMESPACE, "(" + Constants.OBJECTCLASS + "=pkg2.*)"); //CC1 -> CP2 testRegionFilter2.allow(CP2, "(name=" + CP2 + ")"); Region r1, r2 = null; for (int i = 0; i <= numLevels; i++) { r1 = (i > 0) ? r2 : testRegion1; r2 = (i < numLevels) ? digraph.createRegion(testName.getMethodName() + "_level_" + i) : testRegion2; r1.connectRegion(r2, testRegionFilter1.build()); r2.connectRegion(r1, testRegionFilter2.build()); } bundleInstaller.resolveBundles(bundles.values().toArray(new Bundle[bundles.size()])); for (Bundle bundle : bundles.values()) { assertEquals("Bundle did not resolve: " + bundle.getSymbolicName(), Bundle.RESOLVED, bundle.getState()); bundle.start(); } BundleContext context = getContext(); ServiceTracker<Boolean, Boolean> cp2Tracker = new ServiceTracker<Boolean, Boolean>(context, context.createFilter("(&(objectClass=java.lang.Boolean)(bundle.id=" + bundles.get(CP2).getBundleId() + "))"), null); ServiceTracker<Boolean, Boolean> sc1Tracker = new ServiceTracker<Boolean, Boolean>(context, context.createFilter("(&(objectClass=java.lang.Boolean)(bundle.id=" + bundles.get(SC1).getBundleId() + "))"), null); cp2Tracker.open(); sc1Tracker.open(); assertNotNull("The cp2 bundle never found the service.", cp2Tracker.waitForService(2000)); assertNotNull("The sc1 bundle never found the service.", sc1Tracker.waitForService(2000)); cp2Tracker.close(); sc1Tracker.close(); } private ServiceRegistration<ResolverHookFactory> disableAllResolves() { return getContext().registerService(ResolverHookFactory.class, new ResolverHookFactory() { @Override public ResolverHook begin(Collection<BundleRevision> triggers) { return new ResolverHook() { @Override public void filterSingletonCollisions(BundleCapability singleton, Collection<BundleCapability> collisionCandidates) { // nothing; } @Override public void filterResolvable(Collection<BundleRevision> candidates) { // prevent all resolves candidates.clear(); } @Override public void filterMatches(BundleRequirement requirement, Collection<BundleCapability> candidates) { // nothing; } @Override public void end() { // TODO Auto-generated method stub } }; } }, null); } @Test public void testSingletons() throws BundleException { Region region1 = digraph.createRegion(testName.getMethodName() + "_1"); Region region2 = digraph.createRegion(testName.getMethodName() + "_2"); // first install into the same region; higher version 2 should resolve Bundle singleton1 = bundleInstaller.installBundle(SINGLETON1, region1); Bundle singleton2 = bundleInstaller.installBundle(SINGLETON2, region1); assertFalse(bundleInstaller.resolveBundles(new Bundle[] {singleton1, singleton2})); assertEquals("singleton1", Bundle.INSTALLED, singleton1.getState()); assertEquals("singleton2", Bundle.RESOLVED, singleton2.getState()); // now install into different regions; both 1 and 2 should resolve singleton2.uninstall(); singleton2 = bundleInstaller.installBundle(SINGLETON2, region2); assertTrue(bundleInstaller.resolveBundles(new Bundle[] {singleton1, singleton2})); assertEquals("singleton1", Bundle.RESOLVED, singleton1.getState()); assertEquals("singleton2", Bundle.RESOLVED, singleton2.getState()); ServiceRegistration<ResolverHookFactory> disableHook = disableAllResolves(); try { // now refresh to get us to an unresolved state again bundleInstaller.refreshPackages(new Bundle[] {singleton1, singleton2}); // connect region2 -> region1 region2.connectRegion(region1, digraph.createRegionFilterBuilder().allowAll(RegionFilter.VISIBLE_BUNDLE_NAMESPACE).build()); // enable resolving again disableHook.unregister(); disableHook = null; assertFalse(bundleInstaller.resolveBundles(new Bundle[] {singleton1, singleton2})); assertTrue("One and only singleton bundle should be resolved", (singleton1.getState() == Bundle.RESOLVED) ^ (singleton2.getState() == Bundle.RESOLVED)); singleton2.uninstall(); disableHook = disableAllResolves(); // now refresh to get us to an unresolved state again bundleInstaller.refreshPackages(new Bundle[] {singleton1, singleton2}); // enable resolving again disableHook.unregister(); disableHook = null; // make sure singleton1 is resolved first assertTrue(bundleInstaller.resolveBundles(new Bundle[] {singleton1})); assertEquals("singleton1", Bundle.RESOLVED, singleton1.getState()); singleton2 = bundleInstaller.installBundle(SINGLETON2, region2); assertFalse(bundleInstaller.resolveBundles(new Bundle[] {singleton2})); assertEquals("singleton2", Bundle.INSTALLED, singleton2.getState()); singleton1.uninstall(); disableHook = disableAllResolves(); // now refresh to get us to an unresolved state again bundleInstaller.refreshPackages(new Bundle[] {singleton1, singleton2}); // enable resolving again disableHook.unregister(); disableHook = null; // make sure singleton2 is resolved first assertTrue(bundleInstaller.resolveBundles(new Bundle[] {singleton2})); assertEquals("singleton2", Bundle.RESOLVED, singleton2.getState()); singleton1 = bundleInstaller.installBundle(SINGLETON1, region1); assertFalse(bundleInstaller.resolveBundles(new Bundle[] {singleton1})); assertEquals("singleton1", Bundle.INSTALLED, singleton1.getState()); } finally { if (disableHook != null) disableHook.unregister(); } } private static final String REGION_DOMAIN_PROP = "org.eclipse.equinox.region.domain"; @Test public void testMbeans() throws MalformedObjectNameException, BundleException, InstanceNotFoundException, ReflectionException, MBeanException, AttributeNotFoundException { MBeanServer server = ManagementFactory.getPlatformMBeanServer(); ObjectName digraphName = new ObjectName(REGION_DOMAIN_PROP + ":type=RegionDigraph,*"); ObjectName regionNameAllQuery = new ObjectName(REGION_DOMAIN_PROP + ":type=Region,name=*,*"); Set<ObjectInstance> digraphs = server.queryMBeans(null, digraphName); assertEquals("Expected only one instance of digraph", 1, digraphs.size()); Set<ObjectInstance> regions = server.queryMBeans(null, regionNameAllQuery); assertEquals("Expected only one instance of region", 1, regions.size()); Region pp1Region = digraph.createRegion(PP1); Bundle pp1Bundle = bundleInstaller.installBundle(PP1, pp1Region); Region sp1Region = digraph.createRegion(SP1); Bundle sp1Bundle = bundleInstaller.installBundle(SP1, sp1Region); regions = server.queryMBeans(null, regionNameAllQuery); assertEquals("Wrong number of regions", 3, regions.size()); Set<ObjectInstance> pp1Query = server.queryMBeans(null, new ObjectName(REGION_DOMAIN_PROP + ":type=Region,name=" + PP1 + ",*")); assertEquals("Expected only one instance of: " + PP1, 1, pp1Query.size()); Set<ObjectInstance> sp1Query = server.queryMBeans(null, new ObjectName(REGION_DOMAIN_PROP + ":type=Region,name=" + SP1 + ",*")); assertEquals("Expected only one instance of: " + SP1, 1, sp1Query.size()); ObjectName pp1Name = (ObjectName) server.invoke(digraphs.iterator().next().getObjectName(), "getRegion", new Object[] {PP1}, new String[] {String.class.getName()}); assertEquals(PP1 + " regions not equal.", pp1Query.iterator().next().getObjectName(), pp1Name); ObjectName sp1Name = (ObjectName) server.invoke(digraphs.iterator().next().getObjectName(), "getRegion", new Object[] {SP1}, new String[] {String.class.getName()}); assertEquals(SP1 + " regions not equal.", sp1Query.iterator().next().getObjectName(), sp1Name); // test non existing region ObjectName shouldNotExistName = (ObjectName) server.invoke(digraphs.iterator().next().getObjectName(), "getRegion", new Object[] {"ShouldNotExist"}, new String[] {String.class.getName()}); assertNull("Should not exist", shouldNotExistName); long[] bundleIds = (long[]) server.getAttribute(pp1Name, "BundleIds"); assertEquals("Wrong number of bundles", 1, bundleIds.length); assertEquals("Wrong bundle", pp1Bundle.getBundleId(), bundleIds[0]); String name = (String) server.getAttribute(pp1Name, "Name"); assertEquals("Wrong name", PP1, name); bundleIds = (long[]) server.getAttribute(sp1Name, "BundleIds"); assertEquals("Wrong number of bundles", 1, bundleIds.length); assertEquals("Wrong bundle", sp1Bundle.getBundleId(), bundleIds[0]); name = (String) server.getAttribute(sp1Name, "Name"); assertEquals("Wrong name", SP1, name); } @Test public void testBundleCollisionDisconnectedRegions() throws BundleException, InvalidSyntaxException { // get the system region Region systemRegion = digraph.getRegion(0); Collection<Bundle> bundles = new HashSet<Bundle>(); // create 4 disconnected test regions and install each bundle into each region int numRegions = 4; String regionName = "IsolatedRegion_"; for (int i = 0; i < numRegions; i++) { Region region = digraph.createRegion(regionName + i); // Import the system bundle from the systemRegion digraph.connect(region, digraph.createRegionFilterBuilder().allow(RegionFilter.VISIBLE_BUNDLE_NAMESPACE, "(id=0)").build(), systemRegion); // must import Boolean services into systemRegion to test digraph.connect(systemRegion, digraph.createRegionFilterBuilder().allow(RegionFilter.VISIBLE_SERVICE_NAMESPACE, "(objectClass=java.lang.Boolean)").build(), region); for (String location : ALL) { Bundle b = bundleInstaller.installBundle(location, region); bundles.add(b); } } assertEquals("Wrong number of bundles installed", numRegions * ALL.size(), bundles.size()); assertTrue("Could not resolve bundles.", bundleInstaller.resolveBundles(bundles.toArray(new Bundle[bundles.size()]))); // test install of duplicates for (int i = 0; i < numRegions; i++) { Region region = digraph.getRegion(regionName + i); for (String name : ALL) { String location = bundleInstaller.getBundleLocation(name); try { Bundle b = region.installBundle(testName.getMethodName() + "_expectToFail", new URL(location).openStream()); b.uninstall(); fail("Expected a bundle exception on duplicate bundle installation: " + name); } catch (BundleException e) { // expected assertEquals("Wrong exception type.", BundleException.DUPLICATE_BUNDLE_ERROR, e.getType()); } catch (IOException e) { fail("Failed to open bunldle location: " + e.getMessage()); } } } // test update to a duplicate for (int i = 0; i < numRegions; i++) { Region region = digraph.getRegion(regionName + i); Bundle regionPP1 = region.getBundle(PP1, new Version(1, 0, 0)); String locationSP1 = bundleInstaller.getBundleLocation(SP1); try { regionPP1.update(new URL(locationSP1).openStream()); fail("Expected a bundle exception on duplicate bundle update: " + region); } catch (BundleException e) { // expected assertEquals("Wrong exception type.", BundleException.DUPLICATE_BUNDLE_ERROR, e.getType()); } catch (IOException e) { fail("Failed to open bunldle location: " + e.getMessage()); } // now uninstall SP1 and try to update PP1 to SP1 again Bundle regionSP1 = region.getBundle(SP1, new Version(1, 0, 0)); regionSP1.uninstall(); try { regionPP1.update(new URL(locationSP1).openStream()); } catch (IOException e) { fail("Failed to open bunldle location: " + e.getMessage()); } } } @Test public void testBundleCollisionUninstalledBundle() throws BundleException { Region region1 = digraph.createRegion(testName.getMethodName() + 1); Region region2 = digraph.createRegion(testName.getMethodName() + 2); Region region3 = digraph.createRegion(testName.getMethodName() + 3); // install the same bundle into the first two regions. Should be no collision bundleInstaller.installBundle(PP1, region1); bundleInstaller.installBundle(PP1, region2); ServiceRegistration<CollisionHook> collisionHookREg = getContext().registerService(CollisionHook.class, new CollisionHook() { @Override public void filterCollisions(int operationType, Bundle target, Collection<Bundle> collisionCandidates) { for (Bundle bundle : collisionCandidates) { try { bundle.uninstall(); } catch (BundleException e) { e.printStackTrace(); } } } }, new Hashtable<String, Object>(Collections.singletonMap(Constants.SERVICE_RANKING, Integer.MAX_VALUE))); try { bundleInstaller.installBundle(PP1, region3); } finally { collisionHookREg.unregister(); } } @Test public void testBundleCollisionConnectedRegions() throws BundleException, InvalidSyntaxException { // get the system region Region systemRegion = digraph.getRegion(0); // create 3 connected test regions and install each bundle into each region int numRegions = 4; String regionName = "ConnectedRegion_"; for (int i = 0; i < numRegions; i++) { Region region = digraph.createRegion(regionName + i); // Import the system bundle from the systemRegion digraph.connect(region, digraph.createRegionFilterBuilder().allow(RegionFilter.VISIBLE_BUNDLE_NAMESPACE, "(id=0)").build(), systemRegion); // must import Boolean services into systemRegion to test digraph.connect(systemRegion, digraph.createRegionFilterBuilder().allow(RegionFilter.VISIBLE_SERVICE_NAMESPACE, "(objectClass=java.lang.Boolean)").build(), region); } Region region0 = digraph.getRegion(regionName + 0); Region region1 = digraph.getRegion(regionName + 1); Region region2 = digraph.getRegion(regionName + 2); // create connections that share the bundles we want RegionFilterBuilder filterBuilder = digraph.createRegionFilterBuilder(); filterBuilder.allow(RegionFilter.VISIBLE_BUNDLE_NAMESPACE, "(bundle-symbolic-name=" + PP1 + ")"); filterBuilder.allow(RegionFilter.VISIBLE_BUNDLE_NAMESPACE, "(bundle-symbolic-name=" + SP1 + ")"); filterBuilder.allow(RegionFilter.VISIBLE_BUNDLE_NAMESPACE, "(bundle-symbolic-name=" + CP1 + ")"); region1.connectRegion(region0, filterBuilder.build()); region2.connectRegion(region1, filterBuilder.build()); // install a bundle in each region bundleInstaller.installBundle(PP1, region0); bundleInstaller.installBundle(SP1, region1); bundleInstaller.installBundle(CP1, region2); // Should not be able to install SP1 into region0 because that would collide with SP1->region1->region0 assertInstallFail(SP1, region0); // Should not be able to install PP1 into region1 because that would collide with region1->region0->PP1 assertInstallFail(PP1, region1); // Should not be able to install PP1 into region2 because that would collide with region2->region1->region0->PP1 assertInstallFail(PP1, region2); // Should not be able to install CP1 into region0 because that would collide with CP1->region2->region1->region0 assertInstallFail(CP1, region0); } private void assertInstallFail(String name, Region region) { try { Bundle b = bundleInstaller.installBundle(name, region); b.uninstall(); fail("Expected a bundle exception on duplicate bundle install: " + region); } catch (BundleException e) { // expected assertEquals("Wrong exception type.", BundleException.DUPLICATE_BUNDLE_ERROR, e.getType()); } } @Test public void testDefaultRegion() throws BundleException { digraph.setDefaultRegion(null); Region systemRegion = digraph.getRegion(0); Region pp1Region = digraph.createRegion(PP1); Bundle pp1Bundle = bundleInstaller.installBundle(PP1, null); Region result = digraph.getRegion(pp1Bundle); assertEquals("Wrong region", systemRegion, result); pp1Bundle.uninstall(); digraph.setDefaultRegion(pp1Region); pp1Bundle = bundleInstaller.installBundle(PP1, null); result = digraph.getRegion(pp1Bundle); assertEquals("Wrong region", pp1Region, result); digraph.setDefaultRegion(null); } @Test public void testRemoveDefaultRegion() throws BundleException { digraph.setDefaultRegion(null); Region pp1Region = digraph.createRegion(PP1); digraph.setDefaultRegion(pp1Region); digraph.removeRegion(pp1Region); assertEquals("DefaultRegion is not null", null, digraph.getDefaultRegion()); } @Test public void testSetNotExistingDefaultRegion() throws BundleException { Region pp1Region = digraph.createRegion(PP1); digraph.removeRegion(pp1Region); try { digraph.setDefaultRegion(pp1Region); assertFalse("IllegalArgumentException not thrown for setting non-existing region as default", true); } catch (IllegalStateException _) { assertNull("DefaultRegion is not null", digraph.getDefaultRegion()); } } @Test public void testRemoveRegion() throws BundleException { Region pp1Region = digraph.createRegion(PP1); pp1Region.addBundle(TEST_BUNDLE_ID); assertEquals("Region not associated with bundle id", pp1Region, digraph.getRegion(TEST_BUNDLE_ID)); digraph.removeRegion(pp1Region); assertNull("Region still associated with bundle id", digraph.getRegion(TEST_BUNDLE_ID)); // Adding a bundle to a removed region should not change the digraph and should error try { pp1Region.addBundle(TEST_BUNDLE_ID); fail("Added a bundle to a region which was not part of a digraph"); } catch (IllegalStateException e) { // expected } assertNull("Region now associated with bundle id", digraph.getRegion(TEST_BUNDLE_ID)); Region pp2Region = digraph.createRegion(PP2); pp2Region.addBundle(TEST_BUNDLE_ID); // removing a bundle from a removed region should not change the digraph and should error try { pp1Region.removeBundle(TEST_BUNDLE_ID); fail("Removed a bundle via a region which was not part of a digraph"); } catch (IllegalStateException e) { // Expected } assertEquals("Wrong region found for the bundle id", pp2Region, digraph.getRegion(TEST_BUNDLE_ID)); } @Test public void testInstallAtLocation() throws BundleException, MalformedURLException, IOException { // create disconnected test regions Region r1 = digraph.createRegion(testName.getMethodName() + ".1"); Region r2 = digraph.createRegion(testName.getMethodName() + ".2"); String location = bundleInstaller.getBundleLocation(PP1); Bundle b1 = null; Bundle b2 = null; String l1 = null; String l2 = null; try { URL url = new URL(location); b1 = r1.installBundle(location + ".1", url.openStream()); l1 = b1.getLocation(); b2 = r2.installBundleAtLocation(location + ".2", url.openStream()); l2 = b2.getLocation(); } finally { if (b1 != null) { try { b1.uninstall(); } catch (BundleException e) { // ignore } } if (b2 != null) { try { b2.uninstall(); } catch (BundleException e) { // ignore } } } assertEquals("Wrong location found.", location + ".1#" + r1.getName(), l1); assertEquals("Wrong location found.", location + ".2", l2); } @Test public void testInvalidRegionName() { Collection<String> invalidNames = new ArrayList<String>(); invalidNames.addAll(Arrays.asList(":", "bad:Name", ":bad::name:", ":badname", "badname:")); invalidNames.addAll(Arrays.asList("=", "bad=Name", "=bad==name=", "=badname", "badname=")); invalidNames.addAll(Arrays.asList("\n", "bad\nName", "\nbad\n\nname\n", "\nbadname", "badname\n")); invalidNames.addAll(Arrays.asList("*", "bad*Name", "*bad**name*", "*badname", "badname*")); invalidNames.addAll(Arrays.asList("?", "bad?Name", "?bad??name?", "?badname", "badname?")); invalidNames.addAll(Arrays.asList(",", "bad,Name", ",bad,,name,", ",badname", "badname,")); invalidNames.addAll(Arrays.asList("\"", "bad\"Name", "\"bad\"\"name\"", "\"badname", "badname\"")); invalidNames.addAll(Arrays.asList("\\", "bad\\Name", "\\bad\\\\name\\", "\\badname", "badname\\")); for (String invalidName : invalidNames) { try { digraph.createRegion(invalidName); fail("Expected failure to create region."); } catch (IllegalArgumentException e) { // expected } catch (BundleException e) { fail("Unexpected bundle exception: " + e.getMessage()); } } } @Test public void testHigherRankedEventHookResolve() throws BundleException { final List<BundleEvent> events = new CopyOnWriteArrayList<BundleEvent>(); SynchronousBundleListener listener = new SynchronousBundleListener() { @Override public void bundleChanged(BundleEvent event) { events.add(event); } }; getContext().addBundleListener(listener); // register a higher ranked bundle EventHook that causes a bundle to resolve while processing INSTALLED events ServiceRegistration<EventHook> bundleEventHook = getContext().registerService(EventHook.class, new EventHook() { @Override public void event(BundleEvent event, Collection<BundleContext> contexts) { // force resolution if event is INSTALLED if (event.getType() == BundleEvent.INSTALLED) { getContext().getBundle(Constants.SYSTEM_BUNDLE_LOCATION).adapt(FrameworkWiring.class).resolveBundles(Collections.singleton(event.getBundle())); } } }, new Hashtable<String, Object>(Collections.singletonMap(Constants.SERVICE_RANKING, Integer.MAX_VALUE))); Bundle b = null; try { // install a bundle with no dependencies b = bundleInstaller.installBundle(CP1); b.start(); } finally { getContext().removeBundleListener(listener); bundleEventHook.unregister(); } // Had to change the expected order to be out of order now since region hooks are always first for (int eventType : new int[] {BundleEvent.RESOLVED, BundleEvent.INSTALLED, BundleEvent.STARTING, BundleEvent.STARTED}) { if (events.isEmpty()) { fail("No events left, expecting event: " + eventType); } BundleEvent event = events.remove(0); assertEquals("Wrong event type.", eventType, event.getType()); assertEquals("Wrong bundle.", b, event.getBundle()); } } @Test public void testHigherRankedEventHookUninstall() throws BundleException { // register a higher ranked bundle EventHook that causes a bundle to resolve while processing INSTALLED events ServiceRegistration<EventHook> bundleEventHook = getContext().registerService(EventHook.class, new EventHook() { @Override public void event(BundleEvent event, Collection<BundleContext> contexts) { // force uninstall if event is INSTALLED (evil) if (event.getType() == BundleEvent.INSTALLED) { try { event.getBundle().uninstall(); } catch (BundleException e) { e.printStackTrace(); } } } }, new Hashtable<String, Object>(Collections.singletonMap(Constants.SERVICE_RANKING, Integer.MAX_VALUE))); Bundle b = null; try { // install a bundle with no dependencies b = bundleInstaller.installBundle(CP1); } finally { bundleEventHook.unregister(); } assertNull("Found region for uninstalled bundle.", digraph.getRegion(b)); } @Test public void testReplaceConnection() throws BundleException, InvalidSyntaxException { // get the system region Region systemRegion = digraph.getRegion(0); Map<String, Bundle> bundles = new HashMap<String, Bundle>(); // create a disconnected test region for each test bundle for (String location : new String[] {PP1, SP1}) { Region testRegion = digraph.createRegion(location); bundles.put(location, bundleInstaller.installBundle(location, testRegion)); // Import the system bundle from the systemRegion digraph.connect(testRegion, digraph.createRegionFilterBuilder().allow(RegionFilter.VISIBLE_BUNDLE_NAMESPACE, "(id=0)").build(), systemRegion); // must import Boolean services into systemRegion to test digraph.connect(systemRegion, digraph.createRegionFilterBuilder().allow(RegionFilter.VISIBLE_SERVICE_NAMESPACE, "(objectClass=java.lang.Boolean)").build(), testRegion); } bundleInstaller.resolveBundles(bundles.values().toArray(new Bundle[bundles.size()])); assertEquals(PP1, Bundle.RESOLVED, bundles.get(PP1).getState()); assertEquals(SP1, Bundle.INSTALLED, bundles.get(SP1).getState()); // now make a connection that does not let the necessary package through RegionFilter badRegionFilter = digraph.createRegionFilterBuilder().allow(RegionFilter.VISIBLE_PACKAGE_NAMESPACE, "(" + RegionFilter.VISIBLE_PACKAGE_NAMESPACE + "=bad)").build(); Set<FilteredRegion> edges = digraph.getRegion(SP1).getEdges(); assertEquals("Wrong number of edges.", 1, edges.size()); // use replace and verify a new edge is added if the connection did not exist already assertNull("Found existing connection.", digraph.replaceConnection(digraph.getRegion(SP1), badRegionFilter, digraph.getRegion(PP1))); edges = digraph.getRegion(SP1).getEdges(); assertEquals("Wrong number of edges.", 2, edges.size()); // still should not resolve bundleInstaller.resolveBundles(bundles.values().toArray(new Bundle[bundles.size()])); assertEquals(PP1, Bundle.RESOLVED, bundles.get(PP1).getState()); assertEquals(SP1, Bundle.INSTALLED, bundles.get(SP1).getState()); // reconnect to let the package though RegionFilter goodRegionFilter = digraph.createRegionFilterBuilder().allow(RegionFilter.VISIBLE_PACKAGE_NAMESPACE, "(" + RegionFilter.VISIBLE_PACKAGE_NAMESPACE + "=pkg1.*)").build(); RegionFilter existingFilter = digraph.replaceConnection(digraph.getRegion(SP1), goodRegionFilter, digraph.getRegion(PP1)); assertEquals("Wrong existing filter found.", badRegionFilter, existingFilter); // number of edges must remain 2 since we use reconnect edges = digraph.getRegion(SP1).getEdges(); assertEquals("Wrong number of edges.", 2, edges.size()); // should resolve now bundleInstaller.resolveBundles(bundles.values().toArray(new Bundle[bundles.size()])); for (Bundle bundle : bundles.values()) { assertEquals("Bundle did not resolve: " + bundle.getSymbolicName(), Bundle.RESOLVED, bundle.getState()); bundle.start(); } // now remove the connection existingFilter = digraph.replaceConnection(digraph.getRegion(SP1), null, digraph.getRegion(PP1)); assertEquals("Wrong existing filter found.", goodRegionFilter, existingFilter); edges = digraph.getRegion(SP1).getEdges(); assertEquals("Wrong number of edges.", 1, edges.size()); bundleInstaller.refreshPackages(bundles.values().toArray(new Bundle[bundles.size()])); // should not resolve again bundleInstaller.resolveBundles(bundles.values().toArray(new Bundle[bundles.size()])); assertEquals(PP1, Bundle.ACTIVE, bundles.get(PP1).getState()); assertEquals(SP1, Bundle.INSTALLED, bundles.get(SP1).getState()); } }