package org.apache.aries.subsystem.itests; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.aries.itest.RichBundleContext; import org.apache.aries.subsystem.itests.hello.api.Hello; import org.apache.aries.unittest.fixture.ArchiveFixture; import org.apache.aries.unittest.fixture.ArchiveFixture.JarFixture; import org.apache.aries.unittest.fixture.ArchiveFixture.ManifestFixture; import org.junit.Test; import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy; import org.ops4j.pax.exam.spi.reactors.PerMethod; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.BundleException; import org.osgi.framework.Constants; import org.osgi.framework.ServiceRegistration; import org.osgi.framework.hooks.weaving.WeavingHook; import org.osgi.framework.hooks.weaving.WovenClass; import org.osgi.service.subsystem.Subsystem; import org.osgi.service.subsystem.SubsystemConstants; import org.osgi.service.subsystem.SubsystemException; /* * Simple iTest for dynamic imports. In the first instance we'll use a * DynamicImport-Package header because it's the simplest to set up. * _Hopefully_ if this works, then packages added by WeavingProxy services * will also work. If not, we'll need extra tests :-/ */ @ExamReactorStrategy(PerMethod.class) public class DynamicImportTest extends SubsystemTest { @Override protected void createApplications() throws Exception { createApplication("dynamicImport", "dynamicImport.jar"); createEmptyClass(); createBundleA(); createApplicationA(); } /* * Install an .esa containing a bundle with a BundleActivator, and a * DynamicImport-Package on org.apache.aries.subsystem.itests.hello.api. * This app should fail to start because we've not yet intervened to permit * this dynamic package wiring requirement from being met. */ @Test public void verifyThatDynamicImportNeedsHandling() throws Exception { Subsystem subsystem = installSubsystemFromFile ("dynamicImport.esa"); try { startSubsystem(subsystem); Bundle[] bundles = subsystem.getBundleContext().getBundles(); for (Bundle b : bundles) { System.out.println (b.getSymbolicName() + " -> " + b.getState()); } fail ("dynamicImport.esa started when we didn't expect it to"); } catch (SubsystemException sx) { Throwable cause = sx.getCause(); assertTrue("BundleException expected", cause instanceof BundleException); } } class TokenWeaver implements WeavingHook { @Override public void weave(WovenClass arg0) { if ("org.apache.aries.subsystem.itests.dynamicImport".equals(arg0.getBundleWiring().getBundle().getSymbolicName())) { arg0.getDynamicImports().add("org.apache.aries.subsystem.itests.hello.api"); } } } @SuppressWarnings("rawtypes") @Test public void testFirstPassWeavingApproach() throws Exception { ServiceRegistration<?> sr = bundleContext.registerService(WeavingHook.class, new TokenWeaver(), null); try { Subsystem subsystem = installSubsystemFromFile ("dynamicImport.esa"); startSubsystem(subsystem); BundleContext bc = subsystem.getBundleContext(); Hello h = new RichBundleContext(bc).getService(Hello.class); String message = h.saySomething(); assertEquals ("Wrong message back", "Hello, this is something", message); // DynamicImportHelloImpl.java stopSubsystem(subsystem); uninstallSubsystem(subsystem); } finally { sr.unregister(); } } /* * Subsystem-SymbolicName: application.a.esa * Subsystem-Content: bundle.a.jar */ private static final String APPLICATION_A = "application.a.esa"; /* * Bundle-SymbolicName: bundle.a.jar */ private static final String BUNDLE_A = "bundle.a.jar"; private static void createApplicationA() throws IOException { createApplicationAManifest(); createSubsystem(APPLICATION_A, BUNDLE_A); } private static 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 static void createBundleA() throws IOException { JarFixture bundle = ArchiveFixture.newJar(); bundle.binary("Empty.class", new FileInputStream("Empty.class")); ManifestFixture manifest = bundle.manifest(); manifest.attribute(Constants.BUNDLE_SYMBOLICNAME, BUNDLE_A); write(BUNDLE_A, bundle); } /* * Dynamic package imports added by a weaver to a woven class should be * added to the region's sharing policy even if the subsystem has no * Import-Package header. */ @SuppressWarnings("rawtypes") @Test public void testDynamicPackageImportsAddedToSharingPolicyWhenNoImportPackageHeader() throws Exception { final AtomicBoolean weavingHookCalled = new AtomicBoolean(false); ServiceRegistration reg = bundleContext.registerService( WeavingHook.class, new WeavingHook() { @Override public void weave(WovenClass wovenClass) { if (BUNDLE_A.equals(wovenClass.getBundleWiring().getBundle().getSymbolicName())) { wovenClass.getDynamicImports().add("org.osgi.framework"); weavingHookCalled.set(true); } } }, null); try { Subsystem s = installSubsystemFromFile(APPLICATION_A); try { assertNull("Import-Package header should not exist", s.getSubsystemHeaders(null).get(Constants.IMPORT_PACKAGE)); Bundle a = getConstituentAsBundle(s, BUNDLE_A, null, null); // Force the class load so the weaving hook gets called. a.loadClass("Empty"); assertTrue("Weaving hook not called", weavingHookCalled.get()); try { // Try to load a class from the dynamically imported package. a.loadClass("org.osgi.framework.Bundle"); } catch (Exception e) { fail("Woven dynamic package import not added to the region's sharing policy"); } } finally { try { s.uninstall(); } catch (Exception e) {} } } finally { try { reg.unregister(); } catch (Exception e) {} } } protected static final byte[] EMPTY_CLASS = new byte[] { (byte)0xca, (byte)0xfe, (byte)0xba, (byte)0xbe, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x00, (byte)0x12, (byte)0x07, (byte)0x00, (byte)0x02, (byte)0x01, (byte)0x00, (byte)0x05, (byte)0x45, (byte)0x6d, (byte)0x70, (byte)0x74, (byte)0x79, (byte)0x07, (byte)0x00, (byte)0x04, (byte)0x01, (byte)0x00, (byte)0x10, (byte)0x6a, (byte)0x61, (byte)0x76, (byte)0x61, (byte)0x2f, (byte)0x6c, (byte)0x61, (byte)0x6e, (byte)0x67, (byte)0x2f, (byte)0x4f, (byte)0x62, (byte)0x6a, (byte)0x65, (byte)0x63, (byte)0x74, (byte)0x07, (byte)0x00, (byte)0x06, (byte)0x01, (byte)0x00, (byte)0x14, (byte)0x6a, (byte)0x61, (byte)0x76, (byte)0x61, (byte)0x2f, (byte)0x69, (byte)0x6f, (byte)0x2f, (byte)0x53, (byte)0x65, (byte)0x72, (byte)0x69, (byte)0x61, (byte)0x6c, (byte)0x69, (byte)0x7a, (byte)0x61, (byte)0x62, (byte)0x6c, (byte)0x65, (byte)0x01, (byte)0x00, (byte)0x06, (byte)0x3c, (byte)0x69, (byte)0x6e, (byte)0x69, (byte)0x74, (byte)0x3e, (byte)0x01, (byte)0x00, (byte)0x03, (byte)0x28, (byte)0x29, (byte)0x56, (byte)0x01, (byte)0x00, (byte)0x04, (byte)0x43, (byte)0x6f, (byte)0x64, (byte)0x65, (byte)0x0a, (byte)0x00, (byte)0x03, (byte)0x00, (byte)0x0b, (byte)0x0c, (byte)0x00, (byte)0x07, (byte)0x00, (byte)0x08, (byte)0x01, (byte)0x00, (byte)0x0f, (byte)0x4c, (byte)0x69, (byte)0x6e, (byte)0x65, (byte)0x4e, (byte)0x75, (byte)0x6d, (byte)0x62, (byte)0x65, (byte)0x72, (byte)0x54, (byte)0x61, (byte)0x62, (byte)0x6c, (byte)0x65, (byte)0x01, (byte)0x00, (byte)0x12, (byte)0x4c, (byte)0x6f, (byte)0x63, (byte)0x61, (byte)0x6c, (byte)0x56, (byte)0x61, (byte)0x72, (byte)0x69, (byte)0x61, (byte)0x62, (byte)0x6c, (byte)0x65, (byte)0x54, (byte)0x61, (byte)0x62, (byte)0x6c, (byte)0x65, (byte)0x01, (byte)0x00, (byte)0x04, (byte)0x74, (byte)0x68, (byte)0x69, (byte)0x73, (byte)0x01, (byte)0x00, (byte)0x07, (byte)0x4c, (byte)0x45, (byte)0x6d, (byte)0x70, (byte)0x74, (byte)0x79, (byte)0x3b, (byte)0x01, (byte)0x00, (byte)0x0a, (byte)0x53, (byte)0x6f, (byte)0x75, (byte)0x72, (byte)0x63, (byte)0x65, (byte)0x46, (byte)0x69, (byte)0x6c, (byte)0x65, (byte)0x01, (byte)0x00, (byte)0x0a, (byte)0x45, (byte)0x6d, (byte)0x70, (byte)0x74, (byte)0x79, (byte)0x2e, (byte)0x6a, (byte)0x61, (byte)0x76, (byte)0x61, (byte)0x00, (byte)0x21, (byte)0x00, (byte)0x01, (byte)0x00, (byte)0x03, (byte)0x00, (byte)0x01, (byte)0x00, (byte)0x05, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01, (byte)0x00, (byte)0x01, (byte)0x00, (byte)0x07, (byte)0x00, (byte)0x08, (byte)0x00, (byte)0x01, (byte)0x00, (byte)0x09, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x2f, (byte)0x00, (byte)0x01, (byte)0x00, (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x05, (byte)0x2a, (byte)0xb7, (byte)0x00, (byte)0x0a, (byte)0xb1, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x02, (byte)0x00, (byte)0x0c, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x00, (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x04, (byte)0x00, (byte)0x0d, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x0c, (byte)0x00, (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x05, (byte)0x00, (byte)0x0e, (byte)0x00, (byte)0x0f, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01, (byte)0x00, (byte)0x10, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x02, (byte)0x00, (byte)0x11 }; protected static void createEmptyClass() throws IOException { FileOutputStream fos = new FileOutputStream("Empty.class"); fos.write(EMPTY_CLASS); fos.close(); } }