diff options
author | Thomas Watson | 2019-06-28 16:24:47 +0000 |
---|---|---|
committer | Thomas Watson | 2019-07-02 13:28:11 +0000 |
commit | 441b72b01f521dd2d894e49c8d299a4b7073410b (patch) | |
tree | 99862704fb1a9f8271650f584e31355990970435 | |
parent | 6f3b7ea05d6f805a70ecd313ffc9de61912748dc (diff) | |
download | rt.equinox.framework-441b72b01f521dd2d894e49c8d299a4b7073410b.tar.gz rt.equinox.framework-441b72b01f521dd2d894e49c8d299a4b7073410b.tar.xz rt.equinox.framework-441b72b01f521dd2d894e49c8d299a4b7073410b.zip |
Bug 548780 - Add handleContentConnection method to StorageHookFactoryY20190704-0900I20190704-1800I20190704-0245I20190703-1800I20190703-0640I20190702-1800
Make StorageHook not abstract to make it easier to implement
StorageHookFactory and allow the storage hook factory to return null
from createStorageHook
Change-Id: I654f1d9f051df8e8f6c9c3912d0814588e58280d
Signed-off-by: Thomas Watson <tjwatson@us.ibm.com>
4 files changed, 149 insertions, 24 deletions
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 index db23e19b3..da835d0b1 100644 --- 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 @@ -13,13 +13,21 @@ *******************************************************************************/ package org.eclipse.osgi.tests.hooks.framework.storage.a; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URLConnection; import java.util.Collections; import java.util.Dictionary; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; +import java.util.jar.Attributes; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; import org.eclipse.osgi.container.Module; import org.eclipse.osgi.container.ModuleContainerAdaptor.ModuleEvent; import org.eclipse.osgi.container.ModuleRevisionBuilder; @@ -117,6 +125,32 @@ public class TestHookConfigurator implements HookConfigurator { factoryClass = StorageHookFactory.class; return new TestStorageHook(generation, factoryClass); } + + @Override + public URLConnection handleContentConnection(Module module, String location, InputStream in) throws IOException { + if (handleContentConnection) { + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + Manifest manifest = new Manifest(); + Attributes attrs = manifest.getMainAttributes(); + attrs.putValue("Manifest-Version", "1.0"); //$NON-NLS-1$ //$NON-NLS-2$ + attrs.putValue(Constants.BUNDLE_MANIFESTVERSION, "2"); + attrs.putValue(Constants.BUNDLE_SYMBOLICNAME, "testHandleContentConnection"); + JarOutputStream jos = new JarOutputStream(baos, manifest); + jos.close(); + return new URLConnection(null) { + @Override + public void connect() { + connected = true; + } + + @Override + public InputStream getInputStream() { + return new ByteArrayInputStream(baos.toByteArray()); + } + }; + } + return super.handleContentConnection(module, location, in); + } } public static volatile boolean createStorageHookCalled; @@ -127,6 +161,7 @@ public class TestHookConfigurator implements HookConfigurator { public static volatile boolean deletingGenerationCalled; public static volatile boolean adaptManifest; public static volatile boolean replaceModuleBuilder; + public static volatile boolean handleContentConnection; public void addHooks(HookRegistry hookRegistry) { hookRegistry.addStorageHookFactory(new TestStorageHookFactory()); 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 index 78aba2011..a3a61f45c 100644 --- 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 @@ -15,6 +15,7 @@ package org.eclipse.osgi.tests.hooks.framework; import static org.junit.Assert.assertNotEquals; +import java.io.ByteArrayInputStream; import java.io.File; import java.net.URL; import java.util.HashMap; @@ -44,6 +45,7 @@ public class StorageHookTests extends AbstractFrameworkHookTests { private static final String HOOK_CONFIGURATOR_FIELD_DELETING_CALLED = "deletingGenerationCalled"; private static final String HOOK_CONFIGURATOR_FIELD_ADAPT_MANIFEST = "adaptManifest"; private static final String HOOK_CONFIGURATOR_FIELD_REPLACE_BUILDER = "replaceModuleBuilder"; + private static final String HOOK_CONFIGURATOR_FIELD_HANDLE_CONTENT = "handleContentConnection"; private Map<String, String> configuration; private Framework framework; @@ -233,6 +235,41 @@ public class StorageHookTests extends AbstractFrameworkHookTests { assertEquals("Wrong bundle found.", framework.getBundleContext().getBundle(Constants.SYSTEM_BUNDLE_LOCATION), b); } + public void testHandleContent() throws Exception { + initAndStartFramework(); + + // install with an empty stream, the hook will replace it will content to a real bundle + setFactoryClassHandleContent(true); + Bundle b = framework.getBundleContext().installBundle("testBundle", new ByteArrayInputStream(new byte[0])); + assertEquals("Wrong symbolicName", "testHandleContentConnection", b.getSymbolicName()); + b.uninstall(); + + // install with no stream, the hook will supply the real content of the bundle + b = framework.getBundleContext().installBundle("testBundle"); + assertEquals("Wrong symbolicName", "testHandleContentConnection", b.getSymbolicName()); + b.uninstall(); + + // tell the hook to no longer handle content, the default behavior of the framework will be used + setFactoryClassHandleContent(false); + b = installBundle(); + assertEquals("Wrong symbolicName", "test1", b.getSymbolicName()); + + // tell the hook to handle content again, update will update to the content supplied from the hook + setFactoryClassHandleContent(true); + b.update(new ByteArrayInputStream(new byte[0])); + assertEquals("Wrong symbolicName", "testHandleContentConnection", b.getSymbolicName()); + + // tell the hook to no longer handle content, update will go back to using content derived from the original location + setFactoryClassHandleContent(false); + b.update(); + assertEquals("Wrong symbolicName", "test1", b.getSymbolicName()); + + // now update again with hook handling content + setFactoryClassHandleContent(true); + b.update(); + assertEquals("Wrong symbolicName", "testHandleContentConnection", b.getSymbolicName()); + } + @Override protected void setUp() throws Exception { super.setUp(); @@ -289,8 +326,8 @@ public class StorageHookTests extends AbstractFrameworkHookTests { initAndStart(framework); } - private void installBundle() throws Exception { - framework.getBundleContext().installBundle(location); + private Bundle installBundle() throws Exception { + return framework.getBundleContext().installBundle(location); } private void resetStorageHook() throws Exception { @@ -333,6 +370,11 @@ public class StorageHookTests extends AbstractFrameworkHookTests { clazz.getField(HOOK_CONFIGURATOR_FIELD_FAIL_LOAD).set(null, value); } + private void setFactoryClassHandleContent(boolean value) throws Exception { + Class<?> clazz = classLoader.loadClass(HOOK_CONFIGURATOR_CLASS); + clazz.getField(HOOK_CONFIGURATOR_FIELD_HANDLE_CONTENT).set(null, value); + } + private void updateBundle() throws Exception { framework.getBundleContext().getBundle(location).update(); } diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/hookregistry/StorageHookFactory.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/hookregistry/StorageHookFactory.java index eaf97101e..34cd3dd9b 100644 --- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/hookregistry/StorageHookFactory.java +++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/hookregistry/StorageHookFactory.java @@ -17,6 +17,8 @@ package org.eclipse.osgi.internal.hookregistry; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.net.URLConnection; import java.util.Dictionary; import org.eclipse.osgi.container.Module; import org.eclipse.osgi.container.ModuleContainer; @@ -31,9 +33,9 @@ import org.osgi.framework.BundleException; * A StorageHookFactory hooks into the persistent storage loading and saving of bundle {@link Generation generations}. * A factory creates StorageHook instances that get associated with each Generation object installed.<p> * @see Generation#getStorageHook(Class) - * @param <S> the StorageHook type + * @param <S> the save context type * @param <L> the load context type - * @param <H> the save context type + * @param <H> the StorageHook type */ public abstract class StorageHookFactory<S, L, H extends StorageHookFactory.StorageHook<S, L>> { protected final String KEY = this.getClass().getName().intern(); @@ -45,7 +47,9 @@ public abstract class StorageHookFactory<S, L, H extends StorageHookFactory.Stor * data the storage version should be incremented. * @return the storage version of this storage hook */ - public abstract int getStorageVersion(); + public int getStorageVersion() { + return 0; + } /** * Returns the implementation class name for the hook implementation @@ -73,7 +77,7 @@ public abstract class StorageHookFactory<S, L, H extends StorageHookFactory.Stor * Creates a save context object for a storage hook. The * save context is passed to the {@link StorageHook#save(Object, DataOutputStream)} * for each generation being persisted by the framework. - * @return a save context object + * @return a save context object or {@code null} if no save context is needed */ public S createSaveContext() { return null; @@ -85,7 +89,7 @@ public abstract class StorageHookFactory<S, L, H extends StorageHookFactory.Stor * for each generation being loaded from persistent storage * by the framework. * @param version the persistent version - * @return the load context object + * @return the load context object or {@code null} if no load context is needed */ public L createLoadContext(int version) { return null; @@ -94,9 +98,11 @@ public abstract class StorageHookFactory<S, L, H extends StorageHookFactory.Stor /** * Creates a storage hook for the specified generation. * @param generation the generation for the storage hook - * @return a storage hook + * @return a storage hook or {@code null} if no hook is needed for the generation */ - protected abstract H createStorageHook(Generation generation); + protected H createStorageHook(Generation generation) { + return null; + } /** * Creates a storage hook for the specified generation and checks that the @@ -105,12 +111,16 @@ public abstract class StorageHookFactory<S, L, H extends StorageHookFactory.Stor * * @param generation - The generation for which a storage hook should be * created. - * @return A newly created storage hook. + * @return A newly created storage hook or {@code null} if no hook is needed + * for the generation * @throws IllegalStateException - If the factory class of the storage hook * is not equal to the class of this storage hook factory. */ public final H createStorageHookAndValidateFactoryClass(Generation generation) { H result = createStorageHook(generation); + if (result == null) { + return result; + } Class<?> factoryClass = getClass(); Class<?> factoryClassOfStorageHook = result.getFactoryClass(); if (!factoryClass.equals(factoryClassOfStorageHook)) @@ -119,6 +129,20 @@ public abstract class StorageHookFactory<S, L, H extends StorageHookFactory.Stor } /** + * Allows a storage hook factory to handle the {@link URLConnection connection} to the + * content for bundle install or update operation. + * @param module the module being updated. Will be {@code null} for install operations + * @param location the bundle location be installed. Will be {@code null} for update operations + * @param in the input stream for the install or update operation. May be {@code null} + * @return a connection to the content or {@code null} to let the framework handle the content + * @throws IOException if any error occurs which will result in a {@link BundleException} being + * thrown from the update or install operation. + */ + public URLConnection handleContentConnection(Module module, String location, InputStream in) throws IOException { + return null; + } + + /** * A storage hook for a specific generation object. This hook * is responsible for persisting and loading data associated * with a specific generation. @@ -126,7 +150,7 @@ public abstract class StorageHookFactory<S, L, H extends StorageHookFactory.Stor * @param <S> the save context type * @param <L> the load context type */ - public static abstract class StorageHook<S, L> { + public static class StorageHook<S, L> { private final Class<? extends StorageHookFactory<S, L, ? extends StorageHook<S, L>>> factoryClass; private final Generation generation; @@ -149,7 +173,9 @@ public abstract class StorageHookFactory<S, L, H extends StorageHookFactory.Stor * @param manifest the bundle manifest to load into this storage hook * @throws BundleException if any error occurs */ - public abstract void initialize(Dictionary<String, String> manifest) throws BundleException; + public void initialize(Dictionary<String, String> manifest) throws BundleException { + // do nothing by default + } /** * Allows a builder to be modified before it is used by the framework to create a {@link ModuleRevision revision} @@ -182,7 +208,9 @@ public abstract class StorageHookFactory<S, L, H extends StorageHookFactory.Stor * @see #save(Object, DataOutputStream) * @throws IOException if any error occurs */ - public abstract void load(L loadContext, DataInputStream is) throws IOException; + public void load(L loadContext, DataInputStream is) throws IOException { + // do nothing by default + } /** * Saves the data from this storage hook into the specified output stream. This method @@ -194,7 +222,9 @@ public abstract class StorageHookFactory<S, L, H extends StorageHookFactory.Stor * @param os an output stream used to save the storage hook's data from. * @throws IOException if any error occurs */ - public abstract void save(S saveContext, DataOutputStream os) throws IOException; + public void save(S saveContext, DataOutputStream os) throws IOException { + // do nothing by default + } /** * Gets called during {@link Generation#delete()} to inform the hook that the generation diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/storage/Storage.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/storage/Storage.java index 282eaf606..c6f7f3129 100644 --- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/storage/Storage.java +++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/storage/Storage.java @@ -282,6 +282,10 @@ public class Storage { return runtimeVersion; } + public MRUBundleFileList getMRUBundleFileList() { + return mruList; + } + private int getBundleFileLimit(EquinoxConfiguration configuration) { int propValue = 100; // enable to 100 open files by default try { @@ -550,6 +554,14 @@ public class Storage { } public URLConnection getContentConnection(Module module, String bundleLocation, final InputStream in) throws IOException { + List<StorageHookFactory<?, ?, ?>> storageHooks = getConfiguration().getHookRegistry().getStorageHookFactories(); + for (StorageHookFactory<?, ?, ?> storageHook : storageHooks) { + URLConnection hookContent = storageHook.handleContentConnection(module, bundleLocation, in); + if (hookContent != null) { + return hookContent; + } + } + if (in != null) { return new URLConnection(null) { /** @@ -913,7 +925,7 @@ public class Storage { if (t instanceof BundleException) { throw (BundleException) t; } - throw new BundleException("Error occurred installing a bundle.", t); //$NON-NLS-1$ + throw new BundleException("Error occurred updating a bundle.", t); //$NON-NLS-1$ } finally { bundleInfo.unlockGeneration(newGen); } @@ -1473,8 +1485,10 @@ public class Storage { continue; // ignore system bundle } StorageHook<Object, Object> hook = factory.createStorageHookAndValidateFactoryClass(generation); - hook.load(loadContext, temp); - getHooks(hookMap, generation).add(hook); + if (hook != null) { + hook.load(loadContext, temp); + getHooks(hookMap, generation).add(hook); + } } } else { // recover by reinitializing the hook @@ -1483,8 +1497,10 @@ public class Storage { continue; // ignore system bundle } StorageHook<Object, Object> hook = factory.createStorageHookAndValidateFactoryClass(generation); - hook.initialize(generation.getHeaders()); - getHooks(hookMap, generation).add(hook); + if (hook != null) { + hook.initialize(generation.getHeaders()); + getHooks(hookMap, generation).add(hook); + } } } } catch (BundleException e) { @@ -1504,11 +1520,13 @@ public class Storage { continue; // ignore system bundle } StorageHook<Object, Object> hook = next.createStorageHookAndValidateFactoryClass(generation); - try { - hook.initialize(generation.getHeaders()); - getHooks(hookMap, generation).add(hook); - } catch (BundleException e) { - throw new IOException(e); + if (hook != null) { + try { + hook.initialize(generation.getHeaders()); + getHooks(hookMap, generation).add(hook); + } catch (BundleException e) { + throw new IOException(e); + } } } } |