Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Watson2019-06-28 12:24:47 -0400
committerThomas Watson2019-07-02 09:28:11 -0400
commit441b72b01f521dd2d894e49c8d299a4b7073410b (patch)
tree99862704fb1a9f8271650f584e31355990970435
parent6f3b7ea05d6f805a70ecd313ffc9de61912748dc (diff)
downloadrt.equinox.framework-Y20190704-0900.tar.gz
rt.equinox.framework-Y20190704-0900.tar.xz
rt.equinox.framework-Y20190704-0900.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>
-rw-r--r--bundles/org.eclipse.osgi.tests/bundles_src/storage.hooks.a/org/eclipse/osgi/tests/hooks/framework/storage/a/TestHookConfigurator.java35
-rw-r--r--bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/hooks/framework/StorageHookTests.java46
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/hookregistry/StorageHookFactory.java54
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/storage/Storage.java38
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);
+ }
}
}
}

Back to the top