blob: 60a629f7bff623094c8a8100da0671d0f283eb16 [file] [log] [blame]
/*
* 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();
}
}