package org.apache.aries.subsystem.itests; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.fail; import java.io.File; import java.io.IOException; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.apache.aries.subsystem.AriesSubsystem; import org.apache.aries.subsystem.core.internal.BasicRequirement; import org.apache.aries.util.filesystem.FileSystem; import org.apache.aries.util.filesystem.IDirectory; import org.easymock.EasyMock; import org.eclipse.equinox.region.Region; import org.eclipse.equinox.region.RegionFilter; import org.junit.Test; import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy; import org.ops4j.pax.exam.spi.reactors.PerMethod; import org.osgi.framework.Constants; import org.osgi.framework.Version; import org.osgi.framework.namespace.IdentityNamespace; import org.osgi.framework.namespace.PackageNamespace; import org.osgi.resource.Namespace; import org.osgi.resource.Requirement; import org.osgi.resource.Resource; import org.osgi.service.subsystem.Subsystem; import org.osgi.service.subsystem.SubsystemConstants; import org.osgi.service.subsystem.SubsystemException; @ExamReactorStrategy(PerMethod.class) public class AriesSubsystemTest extends SubsystemTest { /* * Subsystem-SymbolicName: application.a.esa * Subsystem-Content: bundle.a.jar */ private static final String APPLICATION_A = "application.a.esa"; /* * Subsystem-SymbolicName: application.b.esa * Subsystem-Content: bundle.b.jar */ private static final String APPLICATION_B = "application.b.esa"; /* * Bundle-SymbolicName: bundle.a.jar * Import-Package: org.osgi.framework,org.osgi.resource */ private static final String BUNDLE_A = "bundle.a.jar"; /* * Bundle-SymbolicName: bundle.b.jar * Import-Package: org.osgi.resource */ private static final String BUNDLE_B = "bundle.b.jar"; /* * Subsystem-SymbolicName: composite.a.esa * Subsystem-Type: osgi.subsystem.composite */ private static final String COMPOSITE_A = "composite.a.esa"; private void createApplicationA() throws IOException { createApplicationAManifest(); createSubsystem(APPLICATION_A, BUNDLE_A); } private void createApplicationB() throws IOException { createApplicationBManifest(); createSubsystem(APPLICATION_B, BUNDLE_B); } private void createApplicationAManifest() throws IOException { Map<String, String> attributes = new HashMap<String, String>(); attributes.put(SubsystemConstants.SUBSYSTEM_SYMBOLICNAME, APPLICATION_A); createManifest(APPLICATION_A + ".mf", attributes); } private void createApplicationBManifest() throws IOException { Map<String, String> attributes = new HashMap<String, String>(); attributes.put(SubsystemConstants.SUBSYSTEM_SYMBOLICNAME, APPLICATION_B); createManifest(APPLICATION_B + ".mf", attributes); } private void createBundleA() throws IOException { createBundle(name(BUNDLE_A), importPackage("org.osgi.framework,org.osgi.resource")); } private void createBundleB() throws IOException { createBundle(name(BUNDLE_B), importPackage("org.osgi.resource")); } private void createCompositeA() throws IOException { createCompositeAManifest(); createSubsystem(COMPOSITE_A, BUNDLE_B, APPLICATION_B); } private void createCompositeAManifest() throws IOException { Map<String, String> attributes = new HashMap<String, String>(); attributes.put(SubsystemConstants.SUBSYSTEM_SYMBOLICNAME, COMPOSITE_A); attributes.put(SubsystemConstants.SUBSYSTEM_TYPE, SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE); attributes.put(SubsystemConstants.SUBSYSTEM_CONTENT, BUNDLE_B + ';' + IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE + "=\"[0,0]\"," + APPLICATION_B + ';' + IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE + "=\"[0,0]\";" + IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE + '=' + SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION); attributes.put(Constants.IMPORT_PACKAGE, "org.osgi.resource"); createManifest(COMPOSITE_A + ".mf", attributes); } @Override public void createApplications() throws Exception { createBundleA(); createBundleB(); createApplicationA(); createApplicationB(); createCompositeA(); } /* * The region copy process when adding additional requirements should * keep all edges, not just the ones running between parent and child. This * is of particular concern with regard to the connections all subsystem * regions have with the root region to allow the subsystem services * through. However, it may also be of concern if the region digraph is * modified outside of the subsystems API. */ @Test public void testAddRequirementsKeepsEdgesOtherThanParentChild() throws Exception { AriesSubsystem compositeA = (AriesSubsystem)installSubsystemFromFile(COMPOSITE_A); try { AriesSubsystem applicationB = (AriesSubsystem)getConstituentAsSubsystem(compositeA, APPLICATION_B, null, SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION); Region bRegion = getRegion(applicationB); // One edge to parent for import package. One edge to root for subsystem // service. assertEquals("Wrong number of edges", 2, bRegion.getEdges().size()); Requirement requirement = new BasicRequirement.Builder() .namespace(PackageNamespace.PACKAGE_NAMESPACE) .directive( PackageNamespace.REQUIREMENT_FILTER_DIRECTIVE, "(osgi.wiring.package=org.osgi.framework)") .resource(EasyMock.createMock(Resource.class)) .build(); applicationB.addRequirements(Collections.singleton(requirement)); bRegion = getRegion(applicationB); // Still one edge to parent for import package. One edge to root for // subsystem service. assertEquals("Wrong number of edges", 2, bRegion.getEdges().size()); Region rootRegion = getRegion(getRootSubsystem()); // The root region won't be the tail region for any connection unless // manually added. assertEquals("Wrong number of edges", 0, rootRegion.getEdges().size()); // Manually add a connection from root to application B. rootRegion.connectRegion( bRegion, rootRegion.getRegionDigraph().createRegionFilterBuilder().allow( "com.foo", "(bar=b)").build()); // The root region should now have an edge. assertEquals("Wrong number of edges", 1, rootRegion.getEdges().size()); // Add another requirement to force a copy. requirement = new BasicRequirement.Builder() .namespace(PackageNamespace.PACKAGE_NAMESPACE) .directive( PackageNamespace.REQUIREMENT_FILTER_DIRECTIVE, "(osgi.wiring.package=org.osgi.framework.wiring)") .resource(EasyMock.createMock(Resource.class)) .build(); applicationB.addRequirements(Collections.singleton(requirement)); rootRegion = getRegion(getRootSubsystem()); // The root region should still have its edge. assertEquals("Wrong number of edges", 1, rootRegion.getEdges().size()); bRegion = getRegion(applicationB); // Still one edge to parent for import package. One edge to root for // subsystem service. assertEquals("Wrong number of edges", 2, bRegion.getEdges().size()); } finally { uninstallSubsystemSilently(compositeA); } } /* * Test the AriesSubsystem.addRequirements(Collection<Requirement>) method. * * There are several things to consider for this test. * * (1) Installing a child subsystem before the requirement has been added * should fail. * (2) Installing a child subsystem after the requirement has been added * should succeed. * (3) The newly created region should contain all of the bundles from the * old one. * (4) The connections between the subsystem with the added requirement and * its parents should be reestablished. * (5) The connections between the subsystem with the added requirement and * its children should be reestablished. */ @Test public void testAddRequirements() throws Exception { AriesSubsystem compositeA = (AriesSubsystem)installSubsystemFromFile(COMPOSITE_A); try { startSubsystem(compositeA); assertCompositeABefore(compositeA); // Test that the installation of applicationA fails. try { installSubsystemFromFile(compositeA, APPLICATION_A); fail("Subsystem should not have installed due to unresolved org.osgi.framework package requirement"); } catch (SubsystemException e) { // Okay. } // Add the org.osgi.framework package requirement. Requirement requirement = new BasicRequirement.Builder() .namespace(PackageNamespace.PACKAGE_NAMESPACE) .directive( PackageNamespace.REQUIREMENT_FILTER_DIRECTIVE, "(osgi.wiring.package=org.osgi.framework)") .resource(EasyMock.createMock(Resource.class)) .build(); compositeA.addRequirements(Collections.singleton(requirement)); // Test that the bundles were copied over to the newly created region. assertCompositeABefore(compositeA); // Test that the parent connections were reestablished. assertRefreshAndResolve(Collections.singletonList(getConstituentAsBundle(compositeA, BUNDLE_B, null, null))); // Test that the child connections were reestablished. assertRefreshAndResolve(Collections.singletonList(getConstituentAsBundle(getConstituentAsSubsystem(compositeA, APPLICATION_B, null, SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION), BUNDLE_B, null, null))); // Test that the installation of applicationA succeeds. AriesSubsystem applicationA; try { applicationA = (AriesSubsystem)installSubsystemFromFile(compositeA, APPLICATION_A); startSubsystem(applicationA); } catch (SubsystemException e) { fail("Subsystem should have installed and started"); } assertCompositeAAfter(compositeA); } finally { stopAndUninstallSubsystemSilently(compositeA); } } /* * Aries Subsystems uses Equinox Region Digraph as its isolation engine. * Digraph has a "special" namespace value that tells the region to allow * everything a bundle offers. This test ensures that a correctly formatted * requirement in that namespace works as expected. */ @Test public void testAddRequirementWithVisibleBundleNamespace() throws Exception { Requirement requirement = new BasicRequirement.Builder() .namespace(RegionFilter.VISIBLE_BUNDLE_NAMESPACE) .directive(Namespace.REQUIREMENT_FILTER_DIRECTIVE, "(id=0)") .resource(EasyMock.createMock(Resource.class)).build(); AriesSubsystem compositeA = (AriesSubsystem) installSubsystemFromFile(COMPOSITE_A); try { startSubsystem(compositeA); // Test that the installation of applicationA fails. try { installSubsystemFromFile(compositeA, APPLICATION_A); fail("Subsystem should not have installed due to unresolved org.osgi.framework package requirement"); } catch (SubsystemException e) { // Okay. } // Add the requirement with the region digraph specific namespace. compositeA.addRequirements(Collections.singleton(requirement)); // Test that the installation and startup of applicationA succeeds. AriesSubsystem applicationA; try { applicationA = (AriesSubsystem) installSubsystemFromFile( compositeA, APPLICATION_A); startSubsystem(applicationA); } catch (SubsystemException e) { fail("Subsystem should have installed and started"); } assertCompositeAAfter(compositeA); } finally { stopAndUninstallSubsystemSilently(compositeA); } } @Test public void testInstallIDirectory() { File file = new File(COMPOSITE_A); IDirectory directory = FileSystem.getFSRoot(file); try { AriesSubsystem compositeA = getRootAriesSubsystem().install(COMPOSITE_A, directory); uninstallSubsystemSilently(compositeA); } catch (Exception e) { fail("Installation from IDirectory should have succeeded"); } } @Test public void testServiceRegistrations() { Subsystem root1 = null; try { root1 = getRootSubsystem(); } catch (Exception e) { fail(Subsystem.class.getName() + " service not registered"); } AriesSubsystem root2 = null; try { root2 = getRootAriesSubsystem(); } catch (Exception e) { fail(AriesSubsystem.class.getName() + " service not registered"); } assertSame("Services should be the same instance", root1, root2); } private void assertCompositeAAfter(Subsystem compositeA) { // applicationA, applicationB, bundleB, region context bundle assertConstituents(4, compositeA); assertConstituent(compositeA, APPLICATION_A, null, SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION); assertConstituent(compositeA, APPLICATION_B, null, SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION); assertConstituent(compositeA, BUNDLE_B); assertNotNull("Bundle not in region", getRegion(compositeA).getBundle(BUNDLE_B, Version.emptyVersion)); assertConstituent(compositeA, "org.osgi.service.subsystem.region.context.1", Version.parseVersion("1")); // applicationA, applicationB assertChildren(2, compositeA); assertApplicationA(assertChild(compositeA, APPLICATION_A)); assertApplicationB(assertChild(compositeA, APPLICATION_B)); } private void assertCompositeABefore(Subsystem compositeA) { // applicationB, bundleB, region context bundle assertConstituents(3, compositeA); assertConstituent(compositeA, APPLICATION_B, null, SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION); assertConstituent(compositeA, BUNDLE_B); assertNotNull("Bundle not in region", getRegion(compositeA).getBundle(BUNDLE_B, Version.emptyVersion)); assertConstituent(compositeA, "org.osgi.service.subsystem.region.context.1", Version.parseVersion("1")); // applicationB assertChildren(1, compositeA); assertApplicationB(assertChild(compositeA, APPLICATION_B)); } private void assertApplicationA(Subsystem applicationA) { // bundleA, region context bundle assertConstituents(2, applicationA); assertConstituent(applicationA, BUNDLE_A); // The subsystem id is 4 instead of 3 due to the first installation that failed. assertConstituent(applicationA, "org.osgi.service.subsystem.region.context.4", Version.parseVersion("1")); assertChildren(0, applicationA); } private void assertApplicationB(Subsystem applicationB) { // bundleB, region context bundle assertConstituents(2, applicationB); assertConstituent(applicationB, BUNDLE_B); assertConstituent(applicationB, "org.osgi.service.subsystem.region.context.2", Version.parseVersion("1")); assertChildren(0, applicationB); } }