Author: pauls Date: Tue Feb 14 16:17:27 2017 New Revision: 1782982 URL: http://svn.apache.org/viewvc?rev=1782982&view=rev Log: Don't take implicit boot delegation into account on service assignability check (FELIX-5544).
Added: felix/trunk/framework/src/test/java/org/apache/felix/framework/ImplicitBootDelegationTest.java Modified: felix/trunk/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java Modified: felix/trunk/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java?rev=1782982&r1=1782981&r2=1782982&view=diff ============================================================================== --- felix/trunk/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java (original) +++ felix/trunk/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java Tue Feb 14 16:17:27 2017 @@ -1748,6 +1748,12 @@ public class BundleWiringImpl implements { break; } + // Break if this goes through ServiceRegistrationImpl.ServiceReferenceImpl + // because it must be a assignability check which should not implicitly boot delegate + else if (ServiceRegistrationImpl.ServiceReferenceImpl.class.equals(classes[i])) + { + break; + } else if (isClassExternal(classes[i])) { try Added: felix/trunk/framework/src/test/java/org/apache/felix/framework/ImplicitBootDelegationTest.java URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/test/java/org/apache/felix/framework/ImplicitBootDelegationTest.java?rev=1782982&view=auto ============================================================================== --- felix/trunk/framework/src/test/java/org/apache/felix/framework/ImplicitBootDelegationTest.java (added) +++ felix/trunk/framework/src/test/java/org/apache/felix/framework/ImplicitBootDelegationTest.java Tue Feb 14 16:17:27 2017 @@ -0,0 +1,333 @@ +/* + * 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.felix.framework; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; +import java.util.zip.ZipEntry; + +import org.junit.Assert; +import org.junit.Assume; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleReference; +import org.osgi.framework.Constants; + +import junit.framework.Test; +import junit.framework.TestCase; + +public class ImplicitBootDelegationTest extends TestCase { + + public void testDoesBootdelegateForClassloaderClassload() throws Exception{ + withFelixDo(new ThrowingConsumer<Felix>() { + @Override + public void accept(Felix felix) throws Exception { + BundleImpl bundle = (BundleImpl) felix.getBundleContext().installBundle(createBundle( + ImplicitBootDelegationTestActivator.class).toURI().toURL().toString()); + + bundle.start(); + + Runnable testClass = felix.getBundleContext().getService( + felix.getBundleContext().getServiceReference(Runnable.class)); + + Assert.assertEquals(TestClass.class, + testClass.getClass().getClassLoader().loadClass(TestClass.class.getName())); + } + }); + } + + public void testDoesNotBootdelegateForClassloadFromInsideBundle() throws Exception{ + withFelixDo(new ThrowingConsumer<Felix>() { + @Override + public void accept(Felix felix) throws Exception { + BundleImpl bundle = (BundleImpl) felix.getBundleContext().installBundle(createBundle( + ImplicitBootDelegationTestActivator.class).toURI().toURL().toString()); + + bundle.start(); + + Runnable testClass = felix.getBundleContext().getService( + felix.getBundleContext().getServiceReference(Runnable.class)); + + try + { + testClass.run(); + Assert.fail("Expected to not be able to load an implicit bootdelegated class from inside the bundle"); + } catch (NoClassDefFoundError ex) { + + } + } + }); + } + + public void testDoesNotBootdelegateForBundleClassload() throws Exception { + withFelixDo(new ThrowingConsumer<Felix>() { + @Override + public void accept(Felix felix) throws Exception { + BundleImpl bundle = (BundleImpl) felix.getBundleContext().installBundle(createBundle( + ImplicitBootDelegationTestActivator.class).toURI().toURL().toString()); + try + { + bundle.loadClass(TestClass.class.getName()); + Assert.fail("Expected to not be able to bundle.loadClass an implicit bootdelegated class"); + } + catch (ClassNotFoundException ex) { + + } + } + }); + } + + public void testDoesNotBootdelegateForServiceAssignability() throws Exception { + withFelixDo(new ThrowingConsumer<Felix>() { + @Override + public void accept(Felix felix) throws Exception { + BundleImpl provider = (BundleImpl) felix.getBundleContext().installBundle(createBundle( + ProvidesActivator.class, TestClass.class).toURI().toURL().toString()); + + provider.start(); + + Assert.assertNotNull(felix.getBundleContext().getAllServiceReferences(TestClass.class.getName(), null)); + + BundleImpl requirer = (BundleImpl) felix.getBundleContext().installBundle(createBundle( + RequireActivator.class).toURI().toURL().toString()); + + requirer.start(); + + Runnable requirerActivtor = felix.getBundleContext().getService( + felix.getBundleContext().getServiceReference(Runnable.class)); + + Assume.assumeTrue(requirerActivtor.getClass().getClassLoader().loadClass(TestClass.class.getName()) + == TestClass.class); + + requirerActivtor.run(); + + Object service = requirer.getBundleContext().getService( + requirer.getBundleContext().getServiceReference(TestClass.class.getName())); + + assertNotNull(service); + assertTrue(!(service instanceof TestClass)); + assertTrue(service.getClass().getName().equals(TestClass.class.getName())); + } + }); + } + + public static class RequireActivator implements BundleActivator, Runnable { + private volatile BundleContext context; + @Override + public void start(BundleContext context) throws Exception { + this.context = context; + context.registerService(Runnable.class, this, null); + } + + @Override + public void stop(BundleContext context) throws Exception { + // TODO Auto-generated method stub + + } + + public void run() { + Object service = context.getService(context.getServiceReference( + "org.apache.felix.framework.ImplicitBootDelegationTest$TestClass")); + + if (service == null) + { + throw new IllegalStateException("Expected service to be available from inside bundle"); + } + + ClassLoader loader = service.getClass().getClassLoader(); + if (!(service.getClass().getClassLoader() instanceof BundleReference)) + { + throw new IllegalStateException("Expected service to be loaded from bundle"); + } + try + { + getClass().getClassLoader().loadClass(service.getClass().getName()); + throw new IllegalStateException("Expected to be unable to load service class"); + } + catch (ClassNotFoundException ex) { + + } + + try { + TestClass test = new TestClass(); + throw new IllegalStateException("Expected to be unable to create object of type TestClass"); + + } catch (NoClassDefFoundError ex) { + + } + } + } + + public static class ProvidesActivator implements BundleActivator { + + @Override + public void start(BundleContext context) throws Exception { + context.registerService(TestClass.class, new TestClass(), null); + } + + @Override + public void stop(BundleContext context) throws Exception { + // TODO Auto-generated method stub + + } + + } + + public static class TestClass { + } + + private void withFelixDo(ThrowingConsumer<Felix> consumer) throws Exception { + File cacheDir = File.createTempFile("felix-cache", ".dir"); + try + { + Felix felix = getFramework(cacheDir); + try + { + felix.init(); + felix.start(); + consumer.accept(felix); + } + finally + { + felix.stop(); + felix.waitForStop(1000); + } + } + finally + { + delete(cacheDir); + } + } + + @FunctionalInterface + private static interface ThrowingConsumer<T> { + public void accept(T t) throws Exception; + } + + + public static class ImplicitBootDelegationTestActivator implements BundleActivator, Runnable { + + @Override + public void start(BundleContext context) throws Exception { + context.registerService(Runnable.class, this, null); + } + + @Override + public void stop(BundleContext context) throws Exception { + } + + public void run() + { + new TestClass(); + } + } + + private Felix getFramework(File cacheDir) { + Map params = new HashMap(); + params.put(Constants.FRAMEWORK_SYSTEMPACKAGES, + "org.osgi.framework; version=1.4.0," + + "org.osgi.service.packageadmin; version=1.2.0," + + "org.osgi.service.startlevel; version=1.1.0," + + "org.osgi.util.tracker; version=1.3.3," + + "org.osgi.service.url; version=1.0.0"); + cacheDir.delete(); + cacheDir.mkdirs(); + String cache = cacheDir.getPath(); + params.put("felix.cache.profiledir", cache); + params.put("felix.cache.dir", cache); + params.put(Constants.FRAMEWORK_STORAGE, cache); + + return new Felix(params); + } + + private static File createBundle(Class activator, Class...classes) throws IOException + { + String mf = "Bundle-SymbolicName: " + activator.getName() +"\n" + + "Bundle-Version: 1.0.0\n" + + "Bundle-ManifestVersion: 2\n" + + "Import-Package: org.osgi.framework\n" + + "Manifest-Version: 1.0\n" + + "Bundle-Activator: " + activator.getName() + "\n\n"; + + Class[] classesCombined; + + if (classes.length > 0) { + List<Class> list = new ArrayList<Class>(Arrays.asList(classes)); + list.add(activator); + classesCombined = list.toArray(new Class[0]); + } + else + { + classesCombined = new Class[]{activator}; + } + return createBundle(mf,classesCombined); + + } + + private static File createBundle(String manifest, Class... classes) throws IOException + { + File f = File.createTempFile("felix-bundle", ".jar"); + f.deleteOnExit(); + + Manifest mf = new Manifest(new ByteArrayInputStream(manifest.getBytes("utf-8"))); + JarOutputStream os = new JarOutputStream(new FileOutputStream(f), mf); + + for (Class clazz : classes) + { + String path = clazz.getName().replace('.', '/') + ".class"; + os.putNextEntry(new ZipEntry(path)); + + InputStream is = clazz.getClassLoader().getResourceAsStream(path); + byte[] buffer = new byte[8 * 1024]; + for (int i = is.read(buffer); i != -1; i = is.read(buffer)) + { + os.write(buffer, 0, i); + } + is.close(); + os.closeEntry(); + } + os.close(); + return f; + } + + private static void delete(File file) throws IOException + { + if (file.isDirectory()) + { + for (File child : file.listFiles()) + { + delete(child); + } + } + file.delete(); + } +} + + + \ No newline at end of file