aries-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From dav...@apache.org
Subject svn commit: r1215211 - in /aries/trunk/spi-fly: spi-fly-core/src/main/java/org/apache/aries/spifly/Util.java spi-fly-dynamic-bundle/src/test/java/org/apache/aries/spifly/dynamic/ClientWeavingHookOSGi43Test.java
Date Fri, 16 Dec 2011 16:29:35 GMT
Author: davidb
Date: Fri Dec 16 16:29:35 2011
New Revision: 1215211

URL: http://svn.apache.org/viewvc?rev=1215211&view=rev
Log:
Test case for the mechanism to obtain the bundle classloader through the BundleRevision.

Added:
    aries/trunk/spi-fly/spi-fly-dynamic-bundle/src/test/java/org/apache/aries/spifly/dynamic/ClientWeavingHookOSGi43Test.java
  (with props)
Modified:
    aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/Util.java

Modified: aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/Util.java
URL: http://svn.apache.org/viewvc/aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/Util.java?rev=1215211&r1=1215210&r2=1215211&view=diff
==============================================================================
--- aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/Util.java (original)
+++ aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/Util.java Fri Dec
16 16:29:35 2011
@@ -121,7 +121,7 @@ public class Util {
         // Here we're just finding any class in the bundle, load that and then use its classloader.
 
         try {
-            Method adaptMethod = b.getClass().getMethod("adapt", Class.class);
+            Method adaptMethod = Bundle.class.getMethod("adapt", Class.class);
             if (adaptMethod != null) {
                 return getBundleClassLoaderViaAdapt(b, adaptMethod);
             }

Added: aries/trunk/spi-fly/spi-fly-dynamic-bundle/src/test/java/org/apache/aries/spifly/dynamic/ClientWeavingHookOSGi43Test.java
URL: http://svn.apache.org/viewvc/aries/trunk/spi-fly/spi-fly-dynamic-bundle/src/test/java/org/apache/aries/spifly/dynamic/ClientWeavingHookOSGi43Test.java?rev=1215211&view=auto
==============================================================================
--- aries/trunk/spi-fly/spi-fly-dynamic-bundle/src/test/java/org/apache/aries/spifly/dynamic/ClientWeavingHookOSGi43Test.java
(added)
+++ aries/trunk/spi-fly/spi-fly-dynamic-bundle/src/test/java/org/apache/aries/spifly/dynamic/ClientWeavingHookOSGi43Test.java
Fri Dec 16 16:29:35 2011
@@ -0,0 +1,428 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.aries.spifly.dynamic;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.security.ProtectionDomain;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.aries.spifly.BaseActivator;
+import org.apache.aries.spifly.Streams;
+import org.apache.aries.spifly.api.SpiFlyConstants;
+import org.easymock.EasyMock;
+import org.easymock.IAnswer;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleReference;
+import org.osgi.framework.Version;
+import org.osgi.framework.hooks.weaving.WeavingHook;
+import org.osgi.framework.hooks.weaving.WovenClass;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.framework.wiring.BundleWiring;
+
+public class ClientWeavingHookOSGi43Test  {
+    DynamicWeavingActivator activator;
+
+    @Before
+    public void setUp() {
+        activator = new DynamicWeavingActivator();
+        BaseActivator.activator = activator;
+    }
+
+    @After
+    public void tearDown() {
+        BaseActivator.activator = null;
+        activator = null;
+    }
+
+    @Test
+    public void testBasicServiceLoaderUsageWithClassLoaderFromBundleRevision() throws Exception
{
+        Dictionary<String, String> consumerHeaders = new Hashtable<String, String>();
+        consumerHeaders.put(SpiFlyConstants.SPI_CONSUMER_HEADER, "*");
+
+        // Register the bundle that provides the SPI implementation.
+        Bundle providerBundle = mockProviderBundle("impl1", 1);
+        activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle,
new HashMap<String, Object>());
+
+        Bundle consumerBundle = mockConsumerBundle(consumerHeaders, providerBundle);
+        activator.addConsumerWeavingData(consumerBundle, SpiFlyConstants.SPI_CONSUMER_HEADER);
+
+        Bundle spiFlyBundle = mockSpiFlyBundle("spifly", Version.parseVersion("1.9.4"), consumerBundle,
providerBundle);
+        WeavingHook wh = new ClientWeavingHook(spiFlyBundle.getBundleContext(), activator);
+
+        // Weave the TestClient class.
+        URL clsUrl = getClass().getResource("TestClient.class");
+        Assert.assertNotNull("Precondition", clsUrl);
+
+        String clientClassName = "org.apache.aries.spifly.dynamic.TestClient";
+        WovenClass wc = new MyWovenClass(clsUrl, clientClassName, consumerBundle);
+        Assert.assertEquals("Precondition", 0, wc.getDynamicImports().size());
+        wh.weave(wc);
+        Assert.assertEquals(1, wc.getDynamicImports().size());
+        String di1 = "org.apache.aries.spifly;bundle-symbolic-name=spifly;bundle-version=1.9.4";
+        String di2 = "org.apache.aries.spifly;bundle-version=1.9.4;bundle-symbolic-name=spifly";
+        String di = wc.getDynamicImports().get(0);
+        Assert.assertTrue("Weaving should have added a dynamic import", di1.equals(di) ||
di2.equals(di));
+
+        // Invoke the woven class and check that it properly sets the TCCL so that the
+        // META-INF/services/org.apache.aries.mytest.MySPI file from impl1 is visible.
+        Class<?> cls = wc.getDefinedClass();
+        Method method = cls.getMethod("test", new Class [] {String.class});
+        Object result = method.invoke(cls.newInstance(), "hello");
+        Assert.assertEquals("olleh", result);
+    }
+
+    private Bundle mockSpiFlyBundle(String bsn, Version version, Bundle ... bundles) throws
Exception {
+        Bundle spiFlyBundle = EasyMock.createMock(Bundle.class);
+
+        BundleContext spiFlyBundleContext = EasyMock.createMock(BundleContext.class);
+        EasyMock.expect(spiFlyBundleContext.getBundle()).andReturn(spiFlyBundle).anyTimes();
+        List<Bundle> allBundles = new ArrayList<Bundle>(Arrays.asList(bundles));
+        allBundles.add(spiFlyBundle);
+        EasyMock.expect(spiFlyBundleContext.getBundles()).andReturn(allBundles.toArray(new
Bundle [] {})).anyTimes();
+        EasyMock.replay(spiFlyBundleContext);
+
+        EasyMock.expect(spiFlyBundle.getSymbolicName()).andReturn(bsn).anyTimes();
+        EasyMock.expect(spiFlyBundle.getVersion()).andReturn(version).anyTimes();
+        EasyMock.expect(spiFlyBundle.getBundleId()).andReturn(Long.MAX_VALUE).anyTimes();
+        EasyMock.expect(spiFlyBundle.getBundleContext()).andReturn(spiFlyBundleContext).anyTimes();
+        EasyMock.replay(spiFlyBundle);
+
+        // Set the bundle context for testing purposes
+        Field bcField = BaseActivator.class.getDeclaredField("bundleContext");
+        bcField.setAccessible(true);
+        bcField.set(activator, spiFlyBundle.getBundleContext());
+
+        return spiFlyBundle;
+    }
+
+    private Bundle mockProviderBundle(String subdir, long id) throws Exception {
+        return mockProviderBundle(subdir, id, Version.emptyVersion);
+    }
+
+    private Bundle mockProviderBundle(String subdir, long id, Version version) throws Exception
{
+        URL url = getClass().getResource("/" + getClass().getName().replace('.', '/') + ".class");
+        File classFile = new File(url.getFile());
+        File baseDir = new File(classFile.getParentFile(), subdir);
+        File directory = new File(baseDir, "/META-INF/services");
+        final List<String> classNames = new ArrayList<String>();
+
+        // Do a directory listing of the applicable META-INF/services directory
+        List<String> resources = new ArrayList<String>();
+        for (File f : directory.listFiles()) {
+            String fileName = f.getName();
+            if (fileName.startsWith(".") || fileName.endsWith("."))
+                continue;
+
+            classNames.addAll(getClassNames(f));
+
+            // Needs to be something like: META-INF/services/org.apache.aries.mytest.MySPI
+            String path = f.getAbsolutePath().substring(baseDir.getAbsolutePath().length());
+            path = path.replace('\\', '/');
+            if (path.startsWith("/")) {
+                path = path.substring(1);
+            }
+            resources.add(path);
+        }
+
+        // Set up the classloader that will be used by the ASM-generated code as the TCCL.
+        // It can load a META-INF/services file
+        final ClassLoader cl = new TestProviderBundleClassLoader(subdir, resources.toArray(new
String [] {}));
+
+        final List<String> classResources = new ArrayList<String>();
+        for(String className : classNames) {
+            classResources.add("/" + className.replace('.', '/') + ".class");
+        }
+
+        Bundle systemBundle = EasyMock.createMock(Bundle.class);
+        EasyMock.<Class<?>>expect(systemBundle.loadClass(EasyMock.anyObject(String.class))).andAnswer(new
IAnswer<Class<?>>() {
+            @Override
+            public Class<?> answer() throws Throwable {
+                String name = (String) EasyMock.getCurrentArguments()[0];
+                return ClientWeavingHookOSGi43Test.class.getClassLoader().loadClass(name);
+            }
+        }).anyTimes();
+        EasyMock.replay(systemBundle);
+
+        BundleContext bc = EasyMock.createNiceMock(BundleContext.class);
+        EasyMock.expect(bc.getBundle(0)).andReturn(systemBundle).anyTimes();
+        EasyMock.replay(bc);
+
+        BundleWiring bundleWiring = EasyMock.createMock(BundleWiring.class);
+        EasyMock.expect(bundleWiring.getClassLoader()).andReturn(cl).anyTimes();
+        EasyMock.replay(bundleWiring);
+
+        BundleRevision bundleRevision = EasyMock.createMock(BundleRevision.class);
+        EasyMock.expect(bundleRevision.getWiring()).andReturn(bundleWiring).anyTimes();
+        EasyMock.replay(bundleRevision);
+
+        Bundle providerBundle = EasyMock.createMock(Bundle.class);
+        String bsn = subdir;
+        int idx = bsn.indexOf('_');
+        if (idx > 0) {
+            bsn = bsn.substring(0, idx);
+        }
+        EasyMock.expect(providerBundle.getSymbolicName()).andReturn(bsn).anyTimes();
+        EasyMock.expect(providerBundle.getBundleId()).andReturn(id).anyTimes();
+        EasyMock.expect(providerBundle.getBundleContext()).andReturn(bc).anyTimes();
+        EasyMock.expect(providerBundle.getVersion()).andReturn(version).anyTimes();
+        EasyMock.expect(providerBundle.adapt(BundleRevision.class)).andReturn(bundleRevision).anyTimes();
+        EasyMock.<Class<?>>expect(providerBundle.loadClass(EasyMock.anyObject(String.class))).andAnswer(new
IAnswer<Class<?>>() {
+            @Override
+            public Class<?> answer() throws Throwable {
+                String name = (String) EasyMock.getCurrentArguments()[0];
+                if (!classNames.contains(name)) {
+                    throw new ClassCastException(name);
+                }
+                return cl.loadClass(name);
+            }
+        }).anyTimes();
+        EasyMock.replay(providerBundle);
+        return providerBundle;
+    }
+
+    private Collection<String> getClassNames(File f) throws IOException {
+        List<String> names = new ArrayList<String>();
+
+        BufferedReader br = new BufferedReader(new FileReader(f));
+        try {
+            String line = null;
+            while((line = br.readLine()) != null) {
+                names.add(line.trim());
+            }
+        } finally {
+            br.close();
+        }
+        return names;
+    }
+
+    private Bundle mockConsumerBundle(Dictionary<String, String> headers, Bundle ...
otherBundles) {
+        // Create a mock object for the client bundle which holds the code that uses ServiceLoader.load()
+        // or another SPI invocation.
+        BundleContext bc = EasyMock.createMock(BundleContext.class);
+
+        Bundle consumerBundle = EasyMock.createMock(Bundle.class);
+        EasyMock.expect(consumerBundle.getSymbolicName()).andReturn("testConsumer").anyTimes();
+        EasyMock.expect(consumerBundle.getHeaders()).andReturn(headers).anyTimes();
+        EasyMock.expect(consumerBundle.getBundleContext()).andReturn(bc).anyTimes();
+        EasyMock.expect(consumerBundle.getBundleId()).andReturn(Long.MAX_VALUE).anyTimes();
+        EasyMock.replay(consumerBundle);
+
+        List<Bundle> allBundles = new ArrayList<Bundle>(Arrays.asList(otherBundles));
+        allBundles.add(consumerBundle);
+        EasyMock.expect(bc.getBundles()).andReturn(allBundles.toArray(new Bundle [] {})).anyTimes();
+        EasyMock.replay(bc);
+
+        return consumerBundle;
+    }
+
+    // A classloader that loads anything starting with org.apache.aries.spifly.dynamic.impl1
from it
+    // and the rest from the parent. This is to mimic a bundle that holds a specific SPI
implementation.
+    public static class TestProviderBundleClassLoader extends URLClassLoader {
+        private final List<String> resources;
+        private final String prefix;
+        private final String classPrefix;
+        private final Map<String, Class<?>> loadedClasses = new ConcurrentHashMap<String,
Class<?>>();
+
+        public TestProviderBundleClassLoader(String subdir, String ... resources) {
+            super(new URL [] {}, TestProviderBundleClassLoader.class.getClassLoader());
+
+            this.prefix = TestProviderBundleClassLoader.class.getPackage().getName().replace('.',
'/') + "/" + subdir + "/";
+            this.classPrefix = prefix.replace('/', '.');
+            this.resources = Arrays.asList(resources);
+        }
+
+        @Override
+        public Class<?> loadClass(String name) throws ClassNotFoundException {
+            if (name.startsWith(classPrefix))
+                return loadClassLocal(name);
+
+            return super.loadClass(name);
+        }
+
+        @Override
+        protected synchronized Class<?> loadClass(String name, boolean resolve) throws
ClassNotFoundException {
+            if (name.startsWith(classPrefix)) {
+                Class<?> cls = loadClassLocal(name);
+                if (resolve)
+                    resolveClass(cls);
+
+                return cls;
+            }
+
+            return super.loadClass(name, resolve);
+        }
+
+        protected Class<?> loadClassLocal(String name) throws ClassNotFoundException
{
+            Class<?> prevLoaded = loadedClasses.get(name);
+            if (prevLoaded != null)
+                return prevLoaded;
+
+            URL res = TestProviderBundleClassLoader.class.getClassLoader().getResource(name.replace('.',
'/') + ".class");
+            try {
+                byte[] bytes = Streams.suck(res.openStream());
+                Class<?> cls = defineClass(name, bytes, 0, bytes.length);
+                loadedClasses.put(name, cls);
+                return cls;
+            } catch (Exception e) {
+                throw new ClassNotFoundException(name, e);
+            }
+        }
+
+        @Override
+        public URL findResource(String name) {
+            if (resources.contains(name)) {
+                return getClass().getClassLoader().getResource(prefix + name);
+            } else {
+                return super.findResource(name);
+            }
+        }
+
+        @Override
+        public Enumeration<URL> findResources(String name) throws IOException {
+            if (resources.contains(name)) {
+                return getClass().getClassLoader().getResources(prefix + name);
+            } else {
+                return super.findResources(name);
+            }
+        }
+    }
+
+    private static class MyWovenClass implements WovenClass {
+        byte [] bytes;
+        final String className;
+        final Bundle bundleContainingOriginalClass;
+        List<String> dynamicImports = new ArrayList<String>();
+        boolean weavingComplete = false;
+
+        private MyWovenClass(URL clazz, String name, Bundle bundle) throws Exception {
+            bytes = Streams.suck(clazz.openStream());
+            className = name;
+            bundleContainingOriginalClass = bundle;
+        }
+
+        @Override
+        public byte[] getBytes() {
+            return bytes;
+        }
+
+        @Override
+        public void setBytes(byte[] newBytes) {
+            bytes = newBytes;
+        }
+
+        @Override
+        public List<String> getDynamicImports() {
+            return dynamicImports;
+        }
+
+        @Override
+        public boolean isWeavingComplete() {
+            return weavingComplete;
+        }
+
+        @Override
+        public String getClassName() {
+            return className;
+        }
+
+        @Override
+        public ProtectionDomain getProtectionDomain() {
+            return null;
+        }
+
+        @Override
+        public Class<?> getDefinedClass() {
+            try {
+                weavingComplete = true;
+                return new MyWovenClassClassLoader(className, getBytes(), getClass().getClassLoader(),
bundleContainingOriginalClass).loadClass(className);
+            } catch (ClassNotFoundException e) {
+                e.printStackTrace();
+                return null;
+            }
+        }
+
+        @Override
+        public BundleWiring getBundleWiring() {
+            BundleWiring bw = EasyMock.createMock(BundleWiring.class);
+            EasyMock.expect(bw.getBundle()).andReturn(bundleContainingOriginalClass);
+            EasyMock.expect(bw.getClassLoader()).andReturn(getClass().getClassLoader());
+            EasyMock.replay(bw);
+            return bw;
+        }
+    }
+
+    private static class MyWovenClassClassLoader extends ClassLoader implements BundleReference
{
+        private final String className;
+        private final Bundle bundle;
+        private final byte [] bytes;
+        private Class<?> wovenClass;
+
+        public MyWovenClassClassLoader(String className, byte[] bytes, ClassLoader parent,
Bundle bundle) {
+            super(parent);
+
+            this.className = className;
+            this.bundle = bundle;
+            this.bytes = bytes;
+        }
+
+        @Override
+        protected synchronized Class<?> loadClass(String name, boolean resolve)
+                throws ClassNotFoundException {
+            if (name.equals(className)) {
+                if (wovenClass == null)
+                    wovenClass = defineClass(className, bytes, 0, bytes.length);
+
+                return wovenClass;
+            } else {
+                return super.loadClass(name, resolve);
+            }
+        }
+
+        @Override
+        public Class<?> loadClass(String name) throws ClassNotFoundException {
+            return loadClass(name, false);
+        }
+
+        @Override
+        public Bundle getBundle() {
+            return bundle;
+        }
+    }
+}

Propchange: aries/trunk/spi-fly/spi-fly-dynamic-bundle/src/test/java/org/apache/aries/spifly/dynamic/ClientWeavingHookOSGi43Test.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain



Mime
View raw message