You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by pa...@apache.org on 2017/02/14 16:17:27 UTC

svn commit: r1782982 - in /felix/trunk/framework/src: main/java/org/apache/felix/framework/BundleWiringImpl.java test/java/org/apache/felix/framework/ImplicitBootDelegationTest.java

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