diff options
7 files changed, 308 insertions, 1 deletions
diff --git a/bundles/org.eclipse.osgi.tests/.classpath b/bundles/org.eclipse.osgi.tests/.classpath index 1035af696..af3abe16d 100644 --- a/bundles/org.eclipse.osgi.tests/.classpath +++ b/bundles/org.eclipse.osgi.tests/.classpath @@ -109,5 +109,6 @@ <classpathentry kind="src" output="bundle_tests/test.logging.a" path="bundles_src/test.logging.a"/> <classpathentry kind="src" output="bundle_tests/geturl" path="bundles_src/geturl"/> <classpathentry kind="src" output="bundle_tests/test.bug375784" path="bundles_src/test.bug375784"/> + <classpathentry kind="src" output="bundle_tests/storage.hooks.a" path="bundles_src/storage.hooks.a"/> <classpathentry kind="output" path="bin"/> </classpath> diff --git a/bundles/org.eclipse.osgi.tests/build.properties b/bundles/org.eclipse.osgi.tests/build.properties index 538bdd014..1a0453f5a 100644 --- a/bundles/org.eclipse.osgi.tests/build.properties +++ b/bundles/org.eclipse.osgi.tests/build.properties @@ -229,6 +229,8 @@ source.bundle_tests/geturl.jar = bundles_src/geturl/ manifest.bundle_tests/geturl.jar = META-INF/MANIFEST.MF source.bundle_tests/test.bug375784.jar = bundles_src/test.bug306181b/ manifest.bundle_tests/test.bug375784.jar = META-INF/MANIFEST.MF +source.bundle_tests/storage.hooks.a.jar = bundles_src/storage.hooks.a/ +manifest.bundle_tests/storage.hooks.a.jar = META-INF/MANIFEST.MF jars.compile.order = bundle_tests/ext.framework.b.jar,\ osgitests.jar,\ @@ -336,4 +338,5 @@ jars.compile.order = bundle_tests/ext.framework.b.jar,\ bundle_tests/test.bug306181b.jar,\ bundle_tests/test.logging.a.jar,\ bundle_tests/geturl.jar,\ - bundle_tests/test.bug375784.jar + bundle_tests/test.bug375784.jar,\ + bundle_tests/storage.hooks.a.jar diff --git a/bundles/org.eclipse.osgi.tests/bundles_src/storage.hooks.a/META-INF/MANIFEST.MF b/bundles/org.eclipse.osgi.tests/bundles_src/storage.hooks.a/META-INF/MANIFEST.MF new file mode 100644 index 000000000..adedac0c8 --- /dev/null +++ b/bundles/org.eclipse.osgi.tests/bundles_src/storage.hooks.a/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-SymbolicName: storage.hooks.a diff --git a/bundles/org.eclipse.osgi.tests/bundles_src/storage.hooks.a/org/eclipse/osgi/tests/hooks/framework/storage/a/TestHookConfigurator.java b/bundles/org.eclipse.osgi.tests/bundles_src/storage.hooks.a/org/eclipse/osgi/tests/hooks/framework/storage/a/TestHookConfigurator.java new file mode 100644 index 000000000..d2423eef5 --- /dev/null +++ b/bundles/org.eclipse.osgi.tests/bundles_src/storage.hooks.a/org/eclipse/osgi/tests/hooks/framework/storage/a/TestHookConfigurator.java @@ -0,0 +1,59 @@ +package org.eclipse.osgi.tests.hooks.framework.storage.a; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.util.Dictionary; +import org.eclipse.osgi.internal.hookregistry.*; +import org.eclipse.osgi.storage.BundleInfo.Generation; + +public class TestHookConfigurator implements HookConfigurator { + private static class TestStorageHookFactory extends StorageHookFactory<Object, Object, TestStorageHookFactory.TestStorageHook> { + private static class TestStorageHook extends StorageHookFactory.StorageHook<Object, Object> { + public TestStorageHook(Generation generation, Class clazz) { + super(generation, clazz); + } + + @Override + public void initialize(Dictionary manifest) { + // Nothing. + } + + @Override + public void load(Object loadContext, DataInputStream is) { + // Nothing. + } + + @Override + public void save(Object saveContext, DataOutputStream os) { + // Nothing. + } + + @Override + public void validate() throws IllegalStateException { + TestHookConfigurator.validateCalled = true; + if (TestHookConfigurator.invalid) + throw new IllegalStateException(); + } + } + + public TestStorageHookFactory() { + } + + @Override + public int getStorageVersion() { + return 0; + } + + @Override + public TestStorageHook createStorageHook(Generation generation) { + return new TestStorageHook(generation, TestStorageHookFactory.class); + } + } + + public static volatile boolean invalid; + public static volatile boolean validateCalled; + + public void addHooks(HookRegistry hookRegistry) { + hookRegistry.addStorageHookFactory(new TestStorageHookFactory()); + } +} diff --git a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/hooks/framework/AbstractFrameworkHookTests.java b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/hooks/framework/AbstractFrameworkHookTests.java new file mode 100644 index 000000000..31c4f9a48 --- /dev/null +++ b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/hooks/framework/AbstractFrameworkHookTests.java @@ -0,0 +1,135 @@ +package org.eclipse.osgi.tests.hooks.framework; + +import java.io.IOException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Enumeration; +import java.util.Map; +import org.eclipse.core.runtime.adaptor.EclipseStarter; +import org.eclipse.core.tests.harness.CoreTest; +import org.eclipse.osgi.internal.hookregistry.HookRegistry; +import org.eclipse.osgi.launch.EquinoxFactory; +import org.eclipse.osgi.tests.OSGiTestsActivator; +import org.eclipse.osgi.tests.bundles.BundleInstaller; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkEvent; +import org.osgi.framework.launch.Framework; +import org.osgi.framework.launch.FrameworkFactory; + +public abstract class AbstractFrameworkHookTests extends CoreTest { + protected static class BasicURLClassLoader extends URLClassLoader { + public BasicURLClassLoader(URL[] urls, ClassLoader parent) { + super(urls, parent); + } + + @Override + public URL getResource(String name) { + if (isLocalResource(name)) + return findResource(name); + return super.getResource(name); + } + + @Override + public void addURL(URL url) { + super.addURL(url); + } + + @Override + public Enumeration<URL> getResources(String name) throws IOException { + if (isLocalResource(name)) + return findResources(name); + return super.getResources(name); + } + + @Override + protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { + if (name.startsWith("org.eclipse")) { + Class<?> result = findLoadedClass(name); + if (result == null) + result = findClass(name); + return result; + } + return super.loadClass(name, resolve); + } + + private boolean isLocalResource(String name) { + return name.startsWith("org/eclipse") || name.equals(HookRegistry.HOOK_CONFIGURATORS_FILE); + } + } + + protected static final String BUNDLES_ROOT = "bundle_tests"; + + protected BasicURLClassLoader classLoader; + protected BundleInstaller bundleInstaller; + + public BundleContext getContext() { + return OSGiTestsActivator.getContext(); + } + + protected void assertBundleDiscarded(String location, Framework framework) { + assertNull("Bundle " + location + " was not discarded", framework.getBundleContext().getBundle(location)); + } + + protected void assertBundleNotDiscarded(String location, Framework framework) { + assertNotNull("Bundle " + location + " was discarded", framework.getBundleContext().getBundle(location)); + } + + protected Framework createFramework(Map<String, String> configuration) throws Exception { + FrameworkFactory factory = (FrameworkFactory) classLoader.loadClass(EquinoxFactory.class.getName()).newInstance(); + Framework framework = factory.newFramework(configuration); + return framework; + } + + protected void initAndStart(Framework framework) throws Exception { + framework.init(); + framework.start(); + } + + protected Framework restart(Framework framework, Map<String, String> configuration) throws Exception { + stop(framework); + framework = createFramework(configuration); + initAndStart(framework); + return framework; + } + + protected void setUp() throws Exception { + setUpBundleInstaller(); + setUpClassLoader(); + } + + protected void stop(Framework framework) throws Exception { + framework.stop(); + FrameworkEvent event = framework.waitForStop(5000); + assertEquals("The framework was not stopped", FrameworkEvent.STOPPED, event.getType()); + } + + protected void stopQuietly(Framework framework) { + if (framework == null) + return; + try { + stop(framework); + } catch (Exception e) { + // Ignore. + } + } + + protected void tearDown() throws Exception { + bundleInstaller.shutdown(); + } + + private void setUpBundleInstaller() throws Exception { + bundleInstaller = new BundleInstaller(BUNDLES_ROOT, getContext()); + bundleInstaller.refreshPackages(null); + } + + private void setUpClassLoader() throws Exception { + BundleContext context = getContext(); + String osgiFramework = context.getProperty(EclipseStarter.PROP_FRAMEWORK); + URL[] urls; + if ("folder".equals(context.getProperty(EclipseStarter.PROP_FRAMEWORK_SHAPE))) + urls = new URL[] {new URL(osgiFramework), new URL(osgiFramework + "bin/")}; + else + urls = new URL[] {new URL(osgiFramework)}; + classLoader = new BasicURLClassLoader(urls, getClass().getClassLoader()); + } +} diff --git a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/hooks/framework/StorageHookTests.java b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/hooks/framework/StorageHookTests.java new file mode 100644 index 000000000..a5c00e26a --- /dev/null +++ b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/hooks/framework/StorageHookTests.java @@ -0,0 +1,104 @@ +package org.eclipse.osgi.tests.hooks.framework; + +import java.io.File; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import org.eclipse.osgi.internal.hookregistry.HookRegistry; +import org.eclipse.osgi.tests.OSGiTestsActivator; +import org.osgi.framework.Constants; +import org.osgi.framework.launch.Framework; + +/* + * The framework must discard bundles that are not valid according to storage + * hooks. See bug 407416. + */ +public class StorageHookTests extends AbstractFrameworkHookTests { + private static final String HOOK_CONFIGURATOR_BUNDLE = "storage.hooks.a"; + private static final String HOOK_CONFIGURATOR_CLASS = "org.eclipse.osgi.tests.hooks.framework.storage.a.TestHookConfigurator"; + private static final String HOOK_CONFIGURATOR_FIELD_INVALID = "invalid"; + private static final String HOOK_CONFIGURATOR_FIELD_VALIDATE_CALLED = "validateCalled"; + + private Map<String, String> configuration; + private Framework framework; + private String location; + + /* + * Bundles must be discarded if a storage hook throws an + * IllegalStateException during validation. + */ + public void testBundleDiscardedWhenClasspathStorageHookInvalidates() throws Exception { + initAndStartFramework(); + installBundle(); + setStorageHookInvalid(true); + restartFramework(); + assertStorageHookValidateCalled(); + assertBundleDiscarded(); + } + + /* + * Bundles must not be discarded when a storage hook says they are valid. + */ + public void testBundleNotDiscardedWhenClasspathStorageHookValidates() throws Exception { + initAndStartFramework(); + installBundle(); + setStorageHookInvalid(false); + restartFramework(); + assertStorageHookValidateCalled(); + assertBundleNotDiscarded(); + } + + protected void setUp() throws Exception { + super.setUp(); + String loc = bundleInstaller.getBundleLocation(HOOK_CONFIGURATOR_BUNDLE); + location = loc.substring(loc.indexOf("file:")); + classLoader.addURL(new URL(location)); + File file = OSGiTestsActivator.getContext().getDataFile(getName()); + configuration = new HashMap<String, String>(); + configuration.put(Constants.FRAMEWORK_STORAGE, file.getAbsolutePath()); + configuration.put(HookRegistry.PROP_HOOK_CONFIGURATORS_INCLUDE, HOOK_CONFIGURATOR_CLASS); + framework = createFramework(configuration); + resetStorageHook(); + } + + protected void tearDown() throws Exception { + stopQuietly(framework); + super.tearDown(); + } + + private void assertBundleDiscarded() { + assertBundleDiscarded(location, framework); + } + + private void assertBundleNotDiscarded() { + assertBundleNotDiscarded(location, framework); + } + + private void assertStorageHookValidateCalled() throws Exception { + Class<?> clazz = classLoader.loadClass(HOOK_CONFIGURATOR_CLASS); + assertTrue("Storage hook not called by framework", clazz.getField(HOOK_CONFIGURATOR_FIELD_VALIDATE_CALLED).getBoolean(null)); + } + + private void initAndStartFramework() throws Exception { + initAndStart(framework); + } + + private void installBundle() throws Exception { + framework.getBundleContext().installBundle(location); + } + + private void resetStorageHook() throws Exception { + Class<?> clazz = classLoader.loadClass(HOOK_CONFIGURATOR_CLASS); + clazz.getField(HOOK_CONFIGURATOR_FIELD_INVALID).set(null, false); + clazz.getField(HOOK_CONFIGURATOR_FIELD_VALIDATE_CALLED).set(null, false); + } + + private void restartFramework() throws Exception { + framework = restart(framework, configuration); + } + + private void setStorageHookInvalid(boolean value) throws Exception { + Class<?> clazz = classLoader.loadClass(HOOK_CONFIGURATOR_CLASS); + clazz.getField(HOOK_CONFIGURATOR_FIELD_INVALID).set(null, value); + } +} diff --git a/bundles/org.eclipse.osgi/META-INF/MANIFEST.MF b/bundles/org.eclipse.osgi/META-INF/MANIFEST.MF index 798730f5c..db400838d 100644 --- a/bundles/org.eclipse.osgi/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.osgi/META-INF/MANIFEST.MF @@ -14,6 +14,7 @@ Export-Package: org.eclipse.core.runtime.adaptor;x-friends:="org.eclipse.core.ru org.eclipse.osgi.framework.util;x-internal:=true, org.eclipse.osgi.internal.debug;x-internal:=true, org.eclipse.osgi.internal.framework;x-internal:=true, + org.eclipse.osgi.internal.hookregistry;x-friends:="org.eclipse.osgi.tests", org.eclipse.osgi.internal.loader;x-internal:=true, org.eclipse.osgi.internal.loader.buddy;x-internal:=true, org.eclipse.osgi.internal.loader.classpath;x-internal:=true, @@ -37,6 +38,7 @@ Export-Package: org.eclipse.core.runtime.adaptor;x-friends:="org.eclipse.core.ru org.eclipse.osgi.service.security;version="1.0", org.eclipse.osgi.service.urlconversion;version="1.0", org.eclipse.osgi.signedcontent;version="1.0", + org.eclipse.osgi.storage;x-friends:="org.eclipse.osgi.tests", org.eclipse.osgi.storage.bundlefile;x-internal:=true, org.eclipse.osgi.storage.url.reference;x-internal:=true, org.eclipse.osgi.storagemanager;version="1.0", |