Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristopher Frost2012-08-03 15:06:49 -0400
committerChristopher Frost2012-08-03 15:06:49 -0400
commit2952ee9850149aefc0880f1a3ebe836efb35caf5 (patch)
tree4ad1d407d98ca291f1e4c983c74f10f95001caa6 /org.eclipse.virgo.kernel.equinox.extensions/src
parentc94ef82447154b8a228dd9a75097423be20763bd (diff)
downloadorg.eclipse.virgo.kernel-2952ee9850149aefc0880f1a3ebe836efb35caf5.tar.gz
org.eclipse.virgo.kernel-2952ee9850149aefc0880f1a3ebe836efb35caf5.tar.xz
org.eclipse.virgo.kernel-2952ee9850149aefc0880f1a3ebe836efb35caf5.zip
(385314) Consolidate git repos - extensions and test-stubs
Diffstat (limited to 'org.eclipse.virgo.kernel.equinox.extensions/src')
-rw-r--r--org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/EquinoxLauncherConfiguration.java71
-rw-r--r--org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/ExtendedEquinoxLauncher.java90
-rw-r--r--org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/BundleFileClosingBundleFileWrapperFactoryHook.java76
-rw-r--r--org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/BundleFileWrapper.java34
-rw-r--r--org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/ClassLoaderCreator.java32
-rw-r--r--org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/ExtendedBundleFileWrapperFactoryHook.java200
-rw-r--r--org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/ExtensionsHookConfigurator.java38
-rw-r--r--org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/MetaInfResourceClassLoaderDelegateHook.java366
-rw-r--r--org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/PluggableBundleFileWrapperFactoryHook.java66
-rw-r--r--org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/PluggableClassLoadingHook.java122
-rw-r--r--org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/PluggableDelegatingClassLoaderDelegateHook.java137
-rw-r--r--org.eclipse.virgo.kernel.equinox.extensions/src/main/resources/.gitignore0
-rw-r--r--org.eclipse.virgo.kernel.equinox.extensions/src/main/resources/about.html28
-rw-r--r--org.eclipse.virgo.kernel.equinox.extensions/src/test/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/MetaInfResourceClassLoaderDelegateHookTests.java147
-rw-r--r--org.eclipse.virgo.kernel.equinox.extensions/src/test/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/PluggableClassLoadingHookTests.java102
-rw-r--r--org.eclipse.virgo.kernel.equinox.extensions/src/test/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/PluggableDelegatingClassLoaderDelegateHookTests.java122
-rw-r--r--org.eclipse.virgo.kernel.equinox.extensions/src/test/resources/hooks/classloading/bundle/META-INF/MANIFEST.MF3
17 files changed, 1634 insertions, 0 deletions
diff --git a/org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/EquinoxLauncherConfiguration.java b/org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/EquinoxLauncherConfiguration.java
new file mode 100644
index 00000000..17302ce8
--- /dev/null
+++ b/org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/EquinoxLauncherConfiguration.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.osgi.extensions.equinox;
+
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ */
+public final class EquinoxLauncherConfiguration {
+
+ private final Map<String, String> frameworkProperties = new HashMap<String, String>();
+
+ private URI profilePath;
+
+ private URI installPath;
+
+ private URI configPath;
+
+ private boolean clean = false;
+
+ public URI getProfilePath() {
+ return profilePath;
+ }
+
+ public void setProfilePath(URI profilePath) {
+ this.profilePath = profilePath;
+ }
+
+ public URI getInstallPath() {
+ return installPath;
+ }
+
+ public void setInstallPath(URI installPath) {
+ this.installPath = installPath;
+ }
+
+ public URI getConfigPath() {
+ return configPath;
+ }
+
+ public void setConfigPath(URI configPath) {
+ this.configPath = configPath;
+ }
+
+ public boolean isClean() {
+ return clean;
+ }
+
+ public void setClean(boolean clean) {
+ this.clean = clean;
+ }
+
+ public void setFrameworkProperty(String key, String value) {
+ this.frameworkProperties.put(key, value);
+ }
+
+ public Map<String, String> getFrameworkProperties() {
+ return this.frameworkProperties;
+ }
+}
diff --git a/org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/ExtendedEquinoxLauncher.java b/org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/ExtendedEquinoxLauncher.java
new file mode 100644
index 00000000..9dc1dc6b
--- /dev/null
+++ b/org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/ExtendedEquinoxLauncher.java
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.osgi.extensions.equinox;
+
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.runtime.adaptor.LocationManager;
+import org.eclipse.osgi.baseadaptor.HookRegistry;
+import org.eclipse.osgi.framework.internal.core.Constants;
+import org.eclipse.osgi.framework.internal.core.FrameworkProperties;
+import org.eclipse.osgi.launch.Equinox;
+import org.eclipse.virgo.osgi.extensions.equinox.hooks.ExtensionsHookConfigurator;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.launch.Framework;
+
+
+/**
+ */
+public final class ExtendedEquinoxLauncher {
+
+ private static final String PROP_CONTEXT_BOOTDELEGATION = "osgi.context.bootdelegation";
+ private static final String PROP_COMPATIBILITY_BOOTDELEGATION = "osgi.compatibility.bootdelegation";
+
+ /**
+ * Equinox-specific property to control clean startup.
+ */
+ private static final String PROP_OSGI_CLEAN = "osgi.clean";
+
+ /**
+ * Equinox-specific property for setting the parent <code>ClassLoader</code>.
+ */
+ private static final String PROP_OSGI_PARENT_CLASSLOADER = "osgi.parentClassloader";
+
+ public static Equinox launch(EquinoxLauncherConfiguration config) throws BundleException {
+ try {
+ Field f = FrameworkProperties.class.getDeclaredField("properties");
+ f.setAccessible(true);
+ f.set(null, null);
+ } catch (Exception e) {
+ System.out.println("Unable to reset Equinox FrameworkProperties");
+ e.printStackTrace(System.out);
+ }
+
+ Map<String, String> configuration = populateConfiguration(config);
+ Equinox equinox = new Equinox(configuration);
+ equinox.start();
+ equinox.getBundleContext().registerService(Framework.class.getName(), equinox, null);
+
+ return equinox;
+ }
+
+ private static Map<String, String> populateConfiguration(EquinoxLauncherConfiguration config) {
+ Map<String, String> configuration = new HashMap<String, String>();
+ configuration.putAll(config.getFrameworkProperties());
+
+ mergeListProperty(configuration, HookRegistry.PROP_HOOK_CONFIGURATORS_INCLUDE, ExtensionsHookConfigurator.class.getName());
+
+ configuration.put(PROP_OSGI_PARENT_CLASSLOADER, "fwk");
+ configuration.put(PROP_CONTEXT_BOOTDELEGATION, "false");
+ configuration.put(PROP_COMPATIBILITY_BOOTDELEGATION, "false");
+ if (config.getProfilePath() != null) {
+ configuration.put(Constants.OSGI_JAVA_PROFILE, config.getProfilePath().toString());
+ }
+ configuration.put(Constants.OSGI_JAVA_PROFILE_BOOTDELEGATION, Constants.OSGI_BOOTDELEGATION_OVERRIDE);
+ configuration.put(LocationManager.PROP_CONFIG_AREA, config.getConfigPath().toString());
+ configuration.put(LocationManager.PROP_INSTALL_AREA, config.getInstallPath().toString());
+ configuration.put(PROP_OSGI_CLEAN, Boolean.toString(config.isClean()));
+ return configuration;
+ }
+
+ private static void mergeListProperty(Map<String, String> properties, String key, String value) {
+ String existingValue = properties.get(key);
+ if (existingValue != null) {
+ properties.put(key, existingValue + "," + value);
+ } else {
+ properties.put(key, value);
+ }
+ }
+}
diff --git a/org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/BundleFileClosingBundleFileWrapperFactoryHook.java b/org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/BundleFileClosingBundleFileWrapperFactoryHook.java
new file mode 100644
index 00000000..9ba68ee0
--- /dev/null
+++ b/org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/BundleFileClosingBundleFileWrapperFactoryHook.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.osgi.extensions.equinox.hooks;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.osgi.baseadaptor.BaseData;
+import org.eclipse.osgi.baseadaptor.bundlefile.BundleFile;
+import org.eclipse.osgi.baseadaptor.hooks.BundleFileWrapperFactoryHook;
+
+
+/**
+ * A {@link BundleFileWrapperFactoryHook} that keeps track of {@link BundleFile BundleFiles} and,
+ * when instructed to clean up, ensures that they are closed.
+ *
+ * <p />
+ *
+ * This is a workaround for Equinox bug 290389. Unfortunately when working with nested frameworks
+ * the suggested workaround of calling PackageAdmin.refreshPackages does not work for the
+ * child framework's composite bundle.
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Thread-safe.
+ *
+ */
+public final class BundleFileClosingBundleFileWrapperFactoryHook implements BundleFileWrapperFactoryHook {
+
+ private final Object monitor = new Object();
+
+ private final List<BundleFile> bundleFiles = new ArrayList<BundleFile>();
+
+ private static final BundleFileClosingBundleFileWrapperFactoryHook INSTANCE = new BundleFileClosingBundleFileWrapperFactoryHook();
+
+ private BundleFileClosingBundleFileWrapperFactoryHook() {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public BundleFile wrapBundleFile(BundleFile bundleFile, Object content, BaseData data, boolean base) throws IOException {
+ synchronized (this.monitor) {
+ this.bundleFiles.add(bundleFile);
+ }
+ return null;
+ }
+
+ public void cleanup() {
+ List<BundleFile> localBundleFiles;
+ synchronized (this.monitor) {
+ localBundleFiles = new ArrayList<BundleFile>(this.bundleFiles);
+ this.bundleFiles.clear();
+ }
+ for (BundleFile bundleFile : localBundleFiles) {
+ try {
+ bundleFile.close();
+ } catch (IOException _) {
+ }
+ }
+ }
+
+ public static BundleFileClosingBundleFileWrapperFactoryHook getInstance() {
+ return INSTANCE;
+ }
+}
diff --git a/org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/BundleFileWrapper.java b/org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/BundleFileWrapper.java
new file mode 100644
index 00000000..953a4772
--- /dev/null
+++ b/org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/BundleFileWrapper.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.osgi.extensions.equinox.hooks;
+
+import org.eclipse.osgi.baseadaptor.bundlefile.BundleFile;
+
+/**
+ * A <code>BundleFileWrapper</code> implementation can be plugged into the {@link PluggableBundleFileWrapperFactoryHook}
+ * at runtime. It will be called to wrap each {@BundleFile BundleFile} that is subsequently accessed by
+ * Equinox.<p />
+ *
+ * <strong>Concurrent Semantics</strong><br /> Implementations <strong>must</strong> be thread-safe.
+ *
+ */
+public interface BundleFileWrapper {
+
+ /**
+ * Provides an opportunity to wrap the supplied {@link BundleFile}. If the wrapper does not wish to wrap the
+ * supplied <code>BundleFile</code> then <code>null</code> must be returned.
+ *
+ * @param bundleFile The <code>BundleFile</code> to be wrapped
+ * @return The wrapped <code>BundleFile</code>, or <code>null</code> if the no wrapping is required.
+ */
+ BundleFile wrapBundleFile(BundleFile bundleFile);
+}
diff --git a/org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/ClassLoaderCreator.java b/org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/ClassLoaderCreator.java
new file mode 100644
index 00000000..700b49d2
--- /dev/null
+++ b/org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/ClassLoaderCreator.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.osgi.extensions.equinox.hooks;
+
+import org.eclipse.osgi.baseadaptor.BaseData;
+import org.eclipse.osgi.baseadaptor.loader.BaseClassLoader;
+import org.eclipse.osgi.framework.adaptor.BundleProtectionDomain;
+import org.eclipse.osgi.framework.adaptor.ClassLoaderDelegate;
+
+
+/**
+ * A <code>ClassLoaderCreator</code> is used to create an Equinox {@link BaseClassLoader} for a {@link org.osgi.framework.Bundle Bundle}.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Implementations must be thread-safe.
+ *
+ */
+public interface ClassLoaderCreator {
+
+ public BaseClassLoader createClassLoader(ClassLoader parent, ClassLoaderDelegate delegate, BundleProtectionDomain domain, BaseData data, String[] bundleclasspath);
+}
diff --git a/org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/ExtendedBundleFileWrapperFactoryHook.java b/org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/ExtendedBundleFileWrapperFactoryHook.java
new file mode 100644
index 00000000..562d0982
--- /dev/null
+++ b/org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/ExtendedBundleFileWrapperFactoryHook.java
@@ -0,0 +1,200 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.osgi.extensions.equinox.hooks;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.JarURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Enumeration;
+import java.util.jar.JarFile;
+
+import org.eclipse.osgi.baseadaptor.BaseData;
+import org.eclipse.osgi.baseadaptor.bundlefile.BundleEntry;
+import org.eclipse.osgi.baseadaptor.bundlefile.BundleFile;
+import org.eclipse.osgi.baseadaptor.hooks.BundleFileWrapperFactoryHook;
+
+/**
+ * A {@link BundleFileWrapperFactoryHook} implementation that wraps {@link BundleFile BundleFiles} to ensure that all
+ * returned resource {@link URL URLs} have a <code>file:</code> protocol, not a <code>bundleresource:</code>
+ * protocol as is the Equinox default.
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ * This class is <strong>thread-safe</strong>.
+ *
+ */
+final class ExtendedBundleFileWrapperFactoryHook implements BundleFileWrapperFactoryHook {
+
+ /**
+ * {@inheritDoc}
+ */
+ public BundleFile wrapBundleFile(BundleFile bundleFile, Object content, BaseData data, boolean base) throws IOException {
+ return new FileResourceEnforcingBundleFile(bundleFile);
+ }
+
+ /**
+ * A concrete extension of {@link BundleFile} that ensures that all resource {@link URL URLs} returned have a
+ * <code>file:</code> protocol, not a <code>bundleresource:</code> protocol as is the Equinox default.
+ * <p>
+ * <strong>Concurrent Semantics</strong><br />
+ * As thread-safe as the encapsulated <code>BundleFile</code> instance.
+ *
+ */
+ private static class FileResourceEnforcingBundleFile extends BundleFile {
+
+ private final BundleFile bundleFile;
+
+ private FileResourceEnforcingBundleFile(BundleFile bundleFile) {
+ this.bundleFile = bundleFile;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void close() throws IOException {
+ this.bundleFile.close();
+
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean containsDir(String dir) {
+ return this.bundleFile.containsDir(dir);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public BundleEntry getEntry(String path) {
+ BundleEntry entry = this.bundleFile.getEntry(path);
+ return entry;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Enumeration<String> getEntryPaths(String path) {
+ Enumeration<String> paths = this.bundleFile.getEntryPaths(path);
+ return paths;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public File getFile(String path, boolean nativeCode) {
+ return this.bundleFile.getFile(path, nativeCode);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void open() throws IOException {
+ this.bundleFile.open();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public File getBaseFile() {
+ return this.bundleFile.getBaseFile();
+ }
+
+ /**
+ * Locates the resource in the BundleFile identified by the supplied path and, if found, returns a
+ * <code>file</code> protocol URL for the resource.
+ *
+ * @return a <code>file</code> protocol URL for the resource
+ */
+ @Override
+ public URL getResourceURL(String path, long hostBundleID, int index) {
+ return doGetResourceURL(path);
+ }
+
+ /**
+ * Locates the resource in the BundleFile identified by the supplied path and, if found, returns a
+ * <code>file</code> protocol URL for the resource.
+ *
+ * @return a <code>file</code> protocol URL for the resource
+ */
+ @Override
+ public URL getResourceURL(String path, long hostBundleID) {
+ return doGetResourceURL(path);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public URL getResourceURL(String path, BaseData hostData, int index) {
+ return doGetResourceURL(path);
+ }
+
+ private URL doGetResourceURL(String path) {
+ BundleEntry entry = getEntry(path);
+ if (entry != null) {
+ return getLocalURLForEntry(entry);
+ } else {
+ return null;
+ }
+ }
+
+ private URL getLocalURLForEntry(BundleEntry entry) {
+ URL url = entry.getLocalURL();
+ try {
+ url.openConnection().setDefaultUseCaches(false);
+ url.openConnection().setUseCaches(false);
+ } catch (Exception e) {
+
+ }
+ if (!"jar".equals(url.getProtocol()) || doesJarEntryReallyExist(url)) {
+ return url;
+ } else {
+ return null;
+ }
+ }
+
+ private boolean doesJarEntryReallyExist(URL url) {
+ boolean entryExists = false;
+ JarFile jarFile = null;
+ try {
+ URLConnection connection = url.openConnection();
+ if (connection instanceof JarURLConnection) {
+ JarURLConnection jarURLConnection = (JarURLConnection) connection;
+ jarFile = jarURLConnection.getJarFile();
+ String entryName = jarURLConnection.getEntryName();
+ if (entryName != null && jarFile != null && jarFile.getEntry(entryName) != null) {
+ entryExists = true;
+ }
+ }
+ } catch (IOException ioe) {
+ entryExists = false;
+ } finally {
+ if (jarFile != null) {
+ try {
+ jarFile.close();
+ } catch (IOException _) {
+ }
+ }
+ }
+ return entryExists;
+ }
+ }
+}
diff --git a/org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/ExtensionsHookConfigurator.java b/org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/ExtensionsHookConfigurator.java
new file mode 100644
index 00000000..30e5b33d
--- /dev/null
+++ b/org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/ExtensionsHookConfigurator.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.osgi.extensions.equinox.hooks;
+
+import org.eclipse.osgi.baseadaptor.HookConfigurator;
+import org.eclipse.osgi.baseadaptor.HookRegistry;
+
+/**
+ * Configures Equinox hooks with which its runtime behaviour is customised.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Thread-safe.
+ *
+ */
+public class ExtensionsHookConfigurator implements HookConfigurator {
+
+ /**
+ * {@inheritDoc}
+ */
+ public void addHooks(HookRegistry hookRegistry) {
+ hookRegistry.addClassLoadingHook(PluggableClassLoadingHook.getInstance());
+ hookRegistry.addBundleFileWrapperFactoryHook(new ExtendedBundleFileWrapperFactoryHook());
+ hookRegistry.addBundleFileWrapperFactoryHook(BundleFileClosingBundleFileWrapperFactoryHook.getInstance());
+ hookRegistry.addBundleFileWrapperFactoryHook(PluggableBundleFileWrapperFactoryHook.getInstance());
+ hookRegistry.addClassLoaderDelegateHook(PluggableDelegatingClassLoaderDelegateHook.getInstance());
+ }
+}
diff --git a/org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/MetaInfResourceClassLoaderDelegateHook.java b/org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/MetaInfResourceClassLoaderDelegateHook.java
new file mode 100644
index 00000000..1449535c
--- /dev/null
+++ b/org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/MetaInfResourceClassLoaderDelegateHook.java
@@ -0,0 +1,366 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.osgi.extensions.equinox.hooks;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.WeakHashMap;
+
+import org.eclipse.osgi.framework.adaptor.BundleClassLoader;
+import org.eclipse.osgi.framework.adaptor.BundleData;
+import org.eclipse.osgi.framework.adaptor.ClassLoaderDelegateHook;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.BundleListener;
+import org.osgi.service.packageadmin.ExportedPackage;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+/**
+ * A {@link ClassLoaderDelegateHook} which in {@link #postFindResource} and {@link #postFindResources} propagates the
+ * attempt to get <code>META-INF</code> resource(s) to the principle bundle's dependencies, unless the request is being
+ * driven through Spring DM's DelgatedNamespaceHandlerResolver.
+ *
+ * <p />
+ *
+ * The list of a bundle's dependencies are cached to avoid determining the dependencies every time. A bundle's entry
+ * in the cached is cleared whenever an <code>UNRESOLVED</code> event is received for the bundle. <code>UNRESOLVED</code>
+ * events are fired both during uninstall and during {@link PackageAdmin#refreshPackages(Bundle[]) refreshPackages}
+ * processing.
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Thread-safe.
+ *
+ */
+@SuppressWarnings("deprecation")
+public class MetaInfResourceClassLoaderDelegateHook implements ClassLoaderDelegateHook {
+
+ private static final String SPRINGDM_DELEGATED_NAMESPACE_HANDLER_RESOLVER_CLASS_NAME = "org.springframework.osgi.context.support.DelegatedNamespaceHandlerResolver";
+
+ private static final String SPRINGDM_DELEGATED_ENTITY_RESOLVER_CLASS_NAME = "org.springframework.osgi.context.support.DelegatedEntityResolver";
+
+ private static final String BLUEPRINT_DELEGATED_NAMESPACE_HANDLER_RESOLVER_CLASS_NAME = "org.eclipse.gemini.blueprint.context.support.DelegatedNamespaceHandlerResolver";
+
+ private static final String BLUEPRINT_DELEGATED_ENTITY_RESOLVER_CLASS_NAME = "org.eclipse.gemini.blueprint.context.support.ChainedEntityResolver";
+
+ private static final String EXCLUDED_RESOURCE_MANIFEST = "MANIFEST.MF";
+
+ private static final String EXCLUDED_RESOURCE_SPRING_DIR = "spring";
+
+ private static final String EXCLUDED_RESOURCE_BLUEPRINT_DIR = "blueprint";
+
+ private static final String EXCLUDED_RESOURCE_SPRING_DIR_SUFFIX = ".xml";
+
+ private final BundleContext systemBundleContext;
+
+ private final PackageAdmin packageAdmin;
+
+ private final ThreadLocal<Object> resourceSearchInProgress = new ThreadLocal<Object>();
+
+ private final Object SEARCH_IN_PROGRESS_MARKER = new Object();
+
+ private final Object monitor = new Object();
+
+ private final WeakHashMap<Bundle, Set<Bundle>> dependenciesCache = new WeakHashMap<Bundle, Set<Bundle>>();
+
+ private final BundleListener cacheClearingBundleListener = new CacheClearingBundleListener();
+
+ /**
+ * Create a new hook that will use the supplied <code>systemBundleContext</code> to lookup bundles, and the supplied
+ * <code>packageAdmin</code> to determine a bundle's dependencies.
+ *
+ * @param systemBundleContext the {@link BundleContext} of the system bundle
+ * @param packageAdmin the {@link PackageAdmin} to use to determine a bundle's dependencies
+ */
+ public MetaInfResourceClassLoaderDelegateHook(BundleContext systemBundleContext, PackageAdmin packageAdmin) {
+ this.systemBundleContext = systemBundleContext;
+ this.packageAdmin = packageAdmin;
+ }
+
+ public void init() {
+ this.systemBundleContext.addBundleListener(this.cacheClearingBundleListener);
+ }
+
+ public void destroy() {
+ this.systemBundleContext.removeBundleListener(this.cacheClearingBundleListener);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public URL postFindResource(String name, BundleClassLoader classLoader, BundleData data) throws FileNotFoundException {
+ if (this.resourceSearchInProgress.get() == null && isDelegatedResource(name)) {
+ try {
+ this.resourceSearchInProgress.set(SEARCH_IN_PROGRESS_MARKER);
+
+ Bundle[] bundles = getDependencyBundles(classLoader.getBundle());
+ for (Bundle dependency : bundles) {
+ try {
+ int state = dependency.getState();
+ if (state == Bundle.ACTIVE || state == Bundle.RESOLVED) {
+ URL resource = dependency.getResource(name);
+ if (resource != null) {
+ return resource;
+ }
+ } else {
+ removeDependency(classLoader.getBundle(), dependency);
+ }
+ } catch (IllegalStateException _) {
+ // Dependency now UNINSTALLED
+ removeDependency(classLoader.getBundle(), dependency);
+ }
+ }
+ } finally {
+ this.resourceSearchInProgress.set(null);
+ }
+ }
+ return null;
+
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Enumeration<URL> postFindResources(String name, BundleClassLoader classLoader, BundleData data) throws FileNotFoundException {
+ if (this.resourceSearchInProgress.get() == null && isDelegatedResource(name)) {
+ try {
+ this.resourceSearchInProgress.set(SEARCH_IN_PROGRESS_MARKER);
+
+ Set<URL> found = new HashSet<URL>();
+ Bundle[] bundles = getDependencyBundles(classLoader.getBundle());
+ for (Bundle dependency : bundles) {
+ try {
+ int state = dependency.getState();
+ if (state == Bundle.RESOLVED || state == Bundle.ACTIVE) {
+ addAll(found, dependency.getResources(name));
+ } else {
+ removeDependency(classLoader.getBundle(), dependency);
+ }
+ } catch (IOException _) {
+ } catch (IllegalStateException _) {
+ // Dependency now UNINSTALLED
+ removeDependency(classLoader.getBundle(), dependency);
+ }
+ }
+
+ if (!found.isEmpty()) {
+ return new IteratorEnumerationAdaptor<URL>(found.iterator());
+ }
+ } finally {
+ this.resourceSearchInProgress.set(null);
+ }
+ }
+
+ return null;
+ }
+
+ private boolean isDelegatedResource(String name) {
+ return isMetaInfResource(name) && !isDelegatedResolverCall();
+ }
+
+ /**
+ * Queries whether or not the supplied resource name is a META-INF resource.
+ *
+ * @param name the resource name.
+ * @return <code>true</code> if the resource is a META-INF resource.
+ */
+ private boolean isMetaInfResource(String name) {
+ if (!name.startsWith("/META-INF") && !name.startsWith("META-INF")) {
+ return false;
+ }
+ if (name.contains(EXCLUDED_RESOURCE_MANIFEST)) {
+ return false;
+ }
+ if ((name.contains(EXCLUDED_RESOURCE_SPRING_DIR) || name.contains(EXCLUDED_RESOURCE_BLUEPRINT_DIR))
+ && name.endsWith(EXCLUDED_RESOURCE_SPRING_DIR_SUFFIX)) {
+ return false;
+ }
+ return true;
+ }
+
+ private boolean isDelegatedResolverCall() {
+ Class<?>[] stackTrace = new SecurityManagerExecutionStackAccessor().getExecutionStack();
+ return isSpringDmDelegatedResolverCall(stackTrace) || isBlueprintDelegatedResolverCall(stackTrace);
+ }
+
+ private final class SecurityManagerExecutionStackAccessor extends SecurityManager {
+
+ public Class<?>[] getExecutionStack() {
+ Class<?>[] classes = super.getClassContext();
+ Class<?>[] executionStack = new Class<?>[classes.length - 1];
+
+ System.arraycopy(classes, 1, executionStack, 0, executionStack.length);
+
+ return executionStack;
+ }
+ }
+
+ private boolean isSpringDmDelegatedResolverCall(Class<?>[] stackTrace) {
+ return isDelegatedResolverCall(stackTrace, SPRINGDM_DELEGATED_NAMESPACE_HANDLER_RESOLVER_CLASS_NAME, SPRINGDM_DELEGATED_ENTITY_RESOLVER_CLASS_NAME);
+ }
+
+ private boolean isBlueprintDelegatedResolverCall(Class<?>[] stackTrace) {
+ return isDelegatedResolverCall(stackTrace, BLUEPRINT_DELEGATED_NAMESPACE_HANDLER_RESOLVER_CLASS_NAME, BLUEPRINT_DELEGATED_ENTITY_RESOLVER_CLASS_NAME);
+ }
+
+ private boolean isDelegatedResolverCall(Class<?>[] stackTrace, String namespaceResolver, String entityResolver) {
+ for (Class<?> clazz : stackTrace) {
+ String className = clazz.getName();
+ if (namespaceResolver.equals(className) || entityResolver.equals(className)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void addAll(Collection<URL> target, Enumeration<URL> source) {
+ while (source != null && source.hasMoreElements()) {
+ target.add(source.nextElement());
+ }
+ }
+
+ private Bundle[] getDependencyBundles(Bundle bundle) {
+ synchronized (this.monitor) {
+ Set<Bundle> dependencies = this.dependenciesCache.get(bundle);
+ if (dependencies != null) {
+ return dependencies.toArray(new Bundle[dependencies.size()]);
+ }
+ }
+
+ Set<Bundle> dependencies = determineDependencies(bundle);
+ synchronized (this.monitor) {
+ this.dependenciesCache.put(bundle, dependencies);
+ return dependencies.toArray(new Bundle[dependencies.size()]);
+ }
+ }
+
+ private void removeDependency(Bundle bundle, Bundle dependency) {
+ synchronized (this.monitor) {
+ Set<Bundle> dependencies = this.dependenciesCache.get(bundle);
+ if (dependencies != null) {
+ dependencies.remove(dependency);
+ }
+ }
+ }
+
+ private Set<Bundle> determineDependencies(Bundle bundle) {
+ Set<Bundle> bundles = new HashSet<Bundle>();
+ for (Bundle candidate : this.systemBundleContext.getBundles()) {
+ if (!candidate.equals(bundle)) {
+ ExportedPackage[] exportedPackages = getExportedPackages(candidate);
+ if (exportedPackages != null) {
+ for (ExportedPackage exportedPackage : exportedPackages) {
+ Bundle[] importingBundles = exportedPackage.getImportingBundles();
+ if (importingBundles != null) {
+ for (Bundle importer : importingBundles) {
+ if (importer.equals(bundle)) {
+ bundles.add(candidate);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return bundles;
+ }
+
+ protected ExportedPackage[] getExportedPackages(Bundle bundle) {
+ return this.packageAdmin.getExportedPackages(bundle);
+ }
+
+ private static class IteratorEnumerationAdaptor<T> implements Enumeration<T> {
+
+ private final Iterator<T> iterator;
+
+ private IteratorEnumerationAdaptor(Iterator<T> iterator) {
+ this.iterator = iterator;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean hasMoreElements() {
+ return this.iterator.hasNext();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public T nextElement() {
+ return this.iterator.next();
+ }
+ }
+
+ private final class CacheClearingBundleListener implements BundleListener {
+
+ /**
+ * {@inheritDoc}
+ */
+ public void bundleChanged(BundleEvent event) {
+ if (BundleEvent.UNRESOLVED == event.getType()) {
+ synchronized (monitor) {
+ dependenciesCache.remove(event.getBundle());
+ }
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Class<?> postFindClass(String name, BundleClassLoader classLoader, BundleData data) throws ClassNotFoundException {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String postFindLibrary(String name, BundleClassLoader classLoader, BundleData data) {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Class<?> preFindClass(String name, BundleClassLoader classLoader, BundleData data) throws ClassNotFoundException {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String preFindLibrary(String name, BundleClassLoader classLoader, BundleData data) throws FileNotFoundException {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public URL preFindResource(String name, BundleClassLoader classLoader, BundleData data) throws FileNotFoundException {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Enumeration<URL> preFindResources(String name, BundleClassLoader classLoader, BundleData data) throws FileNotFoundException {
+ return null;
+ }
+}
diff --git a/org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/PluggableBundleFileWrapperFactoryHook.java b/org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/PluggableBundleFileWrapperFactoryHook.java
new file mode 100644
index 00000000..0dd13d91
--- /dev/null
+++ b/org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/PluggableBundleFileWrapperFactoryHook.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.osgi.extensions.equinox.hooks;
+
+import java.io.IOException;
+
+import org.eclipse.osgi.baseadaptor.BaseData;
+import org.eclipse.osgi.baseadaptor.bundlefile.BundleFile;
+import org.eclipse.osgi.baseadaptor.hooks.BundleFileWrapperFactoryHook;
+
+
+/**
+ * A {@link BundleFileWrapperFactoryHook} into which a {@link BundleFileWrapper} implementation can be plugged. The {@link BundleFileWrapper}
+ * implementation is called to wrap {@link BundleFile BundleFiles} during bundle installation and update.
+ *
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Thread-safe.
+ *
+ */
+public class PluggableBundleFileWrapperFactoryHook implements BundleFileWrapperFactoryHook {
+
+ private volatile BundleFileWrapper wrapper;
+
+ private final Object monitor = new Object();
+
+ private static final PluggableBundleFileWrapperFactoryHook INSTANCE = new PluggableBundleFileWrapperFactoryHook();
+
+ private PluggableBundleFileWrapperFactoryHook() {
+
+ }
+
+ public static PluggableBundleFileWrapperFactoryHook getInstance() {
+ return INSTANCE;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public BundleFile wrapBundleFile(BundleFile bundleFile, Object content, BaseData data, boolean base) throws IOException {
+ synchronized(this.monitor) {
+ if (wrapper != null) {
+ return wrapper.wrapBundleFile(bundleFile);
+ } else {
+ return null;
+ }
+ }
+ }
+
+ public void setBundleFileWrapper(BundleFileWrapper wrapper) {
+ synchronized(this.monitor) {
+ this.wrapper = wrapper;
+ }
+ }
+}
diff --git a/org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/PluggableClassLoadingHook.java b/org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/PluggableClassLoadingHook.java
new file mode 100644
index 00000000..fabc1fde
--- /dev/null
+++ b/org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/PluggableClassLoadingHook.java
@@ -0,0 +1,122 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.osgi.extensions.equinox.hooks;
+
+import java.security.ProtectionDomain;
+import java.util.ArrayList;
+
+import org.eclipse.osgi.baseadaptor.BaseData;
+import org.eclipse.osgi.baseadaptor.bundlefile.BundleEntry;
+import org.eclipse.osgi.baseadaptor.hooks.ClassLoadingHook;
+import org.eclipse.osgi.baseadaptor.loader.BaseClassLoader;
+import org.eclipse.osgi.baseadaptor.loader.ClasspathEntry;
+import org.eclipse.osgi.baseadaptor.loader.ClasspathManager;
+import org.eclipse.osgi.framework.adaptor.BundleProtectionDomain;
+import org.eclipse.osgi.framework.adaptor.ClassLoaderDelegate;
+
+/**
+ * A {@link ClassLoadingHook} into which a {@link ClassLoaderCreator} can be plugged to provide a custom class loader
+ * for a bundle.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Thread-safe.
+ *
+ */
+public final class PluggableClassLoadingHook implements ClassLoadingHook {
+
+ private static final PluggableClassLoadingHook INSTANCE = new PluggableClassLoadingHook();
+
+ private final Object monitor = new Object();
+
+ private volatile ClassLoaderCreator creator;
+
+ private ClassLoader bundleClassLoaderParent;
+
+ private PluggableClassLoadingHook() {
+
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean addClassPathEntry(ArrayList<ClasspathEntry> cpEntries, String cp, ClasspathManager hostmanager, BaseData sourcedata, ProtectionDomain sourcedomain) {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public BaseClassLoader createClassLoader(ClassLoader parent, ClassLoaderDelegate delegate, BundleProtectionDomain domain, BaseData data,
+ String[] bundleclasspath) {
+ if (this.creator != null) {
+ return this.creator.createClassLoader(parent, delegate, domain, data, bundleclasspath);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String findLibrary(BaseData data, String libName) {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ClassLoader getBundleClassLoaderParent() {
+ synchronized (this.monitor) {
+ return this.bundleClassLoaderParent;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void initializedClassLoader(BaseClassLoader baseClassLoader, BaseData data) {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public byte[] processClass(String name, byte[] classbytes, ClasspathEntry classpathEntry, BundleEntry entry, ClasspathManager manager) {
+ return null;
+ }
+
+ public void setClassLoaderCreator(ClassLoaderCreator creator) {
+ synchronized (this.monitor) {
+ this.creator = creator;
+ }
+ }
+
+ /**
+ * @return singleton
+ */
+ public static PluggableClassLoadingHook getInstance() {
+ return INSTANCE;
+ }
+
+ /**
+ * Sets the class loader to be used as the parent of bundle class loaders.
+ *
+ * @param bundleClassLoaderParent the class loader to be used
+ */
+ public void setBundleClassLoaderParent(ClassLoader bundleClassLoaderParent) {
+ synchronized (this.monitor) {
+ this.bundleClassLoaderParent = bundleClassLoaderParent;
+ }
+ }
+
+}
diff --git a/org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/PluggableDelegatingClassLoaderDelegateHook.java b/org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/PluggableDelegatingClassLoaderDelegateHook.java
new file mode 100644
index 00000000..a45a1463
--- /dev/null
+++ b/org.eclipse.virgo.kernel.equinox.extensions/src/main/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/PluggableDelegatingClassLoaderDelegateHook.java
@@ -0,0 +1,137 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.osgi.extensions.equinox.hooks;
+
+import java.io.FileNotFoundException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.eclipse.osgi.framework.adaptor.BundleClassLoader;
+import org.eclipse.osgi.framework.adaptor.BundleData;
+import org.eclipse.osgi.framework.adaptor.ClassLoaderDelegateHook;
+
+
+/**
+ * A pluggable {@link ClassLoaderDelegateHook} into which one or more <code>ClassLoaderDelegateHook</code>
+ * can be plugged.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Thread-safe.
+ *
+ */
+public class PluggableDelegatingClassLoaderDelegateHook implements ClassLoaderDelegateHook {
+
+ private final List<ClassLoaderDelegateHook> delegates = new CopyOnWriteArrayList<ClassLoaderDelegateHook>();
+
+ private static final PluggableDelegatingClassLoaderDelegateHook INSTANCE = new PluggableDelegatingClassLoaderDelegateHook();
+
+ private PluggableDelegatingClassLoaderDelegateHook() {
+ }
+
+ public static PluggableDelegatingClassLoaderDelegateHook getInstance() {
+ return INSTANCE;
+ }
+
+ public void addDelegate(ClassLoaderDelegateHook delegate) {
+ this.delegates.add(delegate);
+ }
+
+ public void removeDelegate(ClassLoaderDelegateHook delegate) {
+ this.delegates.remove(delegate);
+ }
+
+ public Class<?> postFindClass(String name, BundleClassLoader classLoader, BundleData data) throws ClassNotFoundException {
+ for (ClassLoaderDelegateHook delegate : this.delegates) {
+ Class<?> clazz = delegate.postFindClass(name, classLoader, data);
+ if (clazz != null) {
+ return clazz;
+ }
+ }
+ return null;
+ }
+
+ public String postFindLibrary(String name, BundleClassLoader classLoader, BundleData data) {
+ for (ClassLoaderDelegateHook delegate : this.delegates) {
+ String library = delegate.postFindLibrary(name, classLoader, data);
+ if (library != null) {
+ return library;
+ }
+ }
+ return null;
+ }
+
+ public URL postFindResource(String name, BundleClassLoader classLoader, BundleData data) throws FileNotFoundException {
+ for (ClassLoaderDelegateHook delegate : this.delegates) {
+ URL resource = delegate.postFindResource(name, classLoader, data);
+ if (resource != null) {
+ return resource;
+ }
+ }
+ return null;
+ }
+
+ public Enumeration<URL> postFindResources(String name, BundleClassLoader classLoader, BundleData data) throws FileNotFoundException {
+ for (ClassLoaderDelegateHook delegate : this.delegates) {
+ Enumeration<URL> resources = delegate.postFindResources(name, classLoader, data);
+ if (resources != null) {
+ return resources;
+ }
+ }
+ return null;
+ }
+
+ public Class<?> preFindClass(String name, BundleClassLoader classLoader, BundleData data) throws ClassNotFoundException {
+ for (ClassLoaderDelegateHook delegate : this.delegates) {
+ Class<?> clazz = delegate.preFindClass(name, classLoader, data);
+ if (clazz != null) {
+ return clazz;
+ }
+ }
+ return null;
+ }
+
+ public String preFindLibrary(String name, BundleClassLoader classLoader, BundleData data) throws FileNotFoundException {
+ for (ClassLoaderDelegateHook delegate : this.delegates) {
+ String library = delegate.preFindLibrary(name, classLoader, data);
+ if (library != null) {
+ return library;
+ }
+ }
+ return null;
+ }
+
+ public URL preFindResource(String name, BundleClassLoader classLoader, BundleData data) throws FileNotFoundException {
+ for (ClassLoaderDelegateHook delegate : this.delegates) {
+ URL resource = delegate.preFindResource(name, classLoader, data);
+ if (resource != null) {
+ return resource;
+ }
+ }
+ return null;
+ }
+
+ public Enumeration<URL> preFindResources(String name, BundleClassLoader classLoader, BundleData data) throws FileNotFoundException {
+ for (ClassLoaderDelegateHook delegate : this.delegates) {
+ Enumeration<URL> resources = delegate.preFindResources(name, classLoader, data);
+ if (resources != null) {
+ return resources;
+ }
+ }
+ return null;
+ }
+
+
+}
diff --git a/org.eclipse.virgo.kernel.equinox.extensions/src/main/resources/.gitignore b/org.eclipse.virgo.kernel.equinox.extensions/src/main/resources/.gitignore
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/org.eclipse.virgo.kernel.equinox.extensions/src/main/resources/.gitignore
diff --git a/org.eclipse.virgo.kernel.equinox.extensions/src/main/resources/about.html b/org.eclipse.virgo.kernel.equinox.extensions/src/main/resources/about.html
new file mode 100644
index 00000000..c258ef55
--- /dev/null
+++ b/org.eclipse.virgo.kernel.equinox.extensions/src/main/resources/about.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
+<title>About</title>
+</head>
+<body lang="EN-US">
+<h2>About This Content</h2>
+
+<p>June 5, 2006</p>
+<h3>License</h3>
+
+<p>The Eclipse Foundation makes available all content in this plug-in (&quot;Content&quot;). Unless otherwise
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 (&quot;EPL&quot;). A copy of the EPL is available
+at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>.
+For purposes of the EPL, &quot;Program&quot; will mean the Content.</p>
+
+<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is
+being redistributed by another party (&quot;Redistributor&quot;) and different terms and conditions may
+apply to your use of any object code in the Content. Check the Redistributor's license that was
+provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise
+indicated below, the terms and conditions of the EPL still apply to any source code in the Content
+and such source code may be obtained at <a href="http://www.eclipse.org/">http://www.eclipse.org</a>.</p>
+
+</body>
+</html> \ No newline at end of file
diff --git a/org.eclipse.virgo.kernel.equinox.extensions/src/test/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/MetaInfResourceClassLoaderDelegateHookTests.java b/org.eclipse.virgo.kernel.equinox.extensions/src/test/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/MetaInfResourceClassLoaderDelegateHookTests.java
new file mode 100644
index 00000000..173416f2
--- /dev/null
+++ b/org.eclipse.virgo.kernel.equinox.extensions/src/test/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/MetaInfResourceClassLoaderDelegateHookTests.java
@@ -0,0 +1,147 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.osgi.extensions.equinox.hooks;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.FileNotFoundException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+
+import org.eclipse.osgi.framework.adaptor.BundleClassLoader;
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Version;
+import org.osgi.service.packageadmin.ExportedPackage;
+
+import org.eclipse.virgo.osgi.extensions.equinox.hooks.MetaInfResourceClassLoaderDelegateHook;
+import org.eclipse.virgo.teststubs.osgi.framework.StubBundle;
+import org.eclipse.virgo.teststubs.osgi.framework.StubBundleContext;
+
+
+/**
+ */
+@SuppressWarnings("deprecation")
+public class MetaInfResourceClassLoaderDelegateHookTests {
+
+ private final BundleClassLoader classLoader = createMock(BundleClassLoader.class);
+
+ private final StubBundle principleBundle = new StubBundle(3L, "principle", new Version(1, 0, 0), "");
+
+ private final StubBundle installedBundleOne = new StubBundle(1L, "one", new Version(1, 0, 0), "");
+
+ private final StubBundle installedBundleTwo = new StubBundle(2L, "two", new Version(1, 0, 0), "");
+
+ private final StubBundleContext principleBundleContext = new StubBundleContext(this.principleBundle);
+
+ private final UnitTestMetaInfResourceClassLoaderDelegateHook hook = new UnitTestMetaInfResourceClassLoaderDelegateHook(this.principleBundleContext);
+
+ @Before
+ public void initialise() {
+ this.principleBundle.setBundleContext(this.principleBundleContext);
+
+ expect(this.classLoader.getBundle()).andReturn(this.principleBundle).anyTimes();
+ replay(this.classLoader);
+
+ this.installedBundleOne.setState(Bundle.ACTIVE);
+ this.installedBundleTwo.setState(Bundle.RESOLVED);
+
+ this.principleBundleContext.addInstalledBundle(this.installedBundleOne);
+ this.principleBundleContext.addInstalledBundle(this.installedBundleTwo);
+ }
+
+ @Test
+ public void findResourceWithNoExportedPackages() throws FileNotFoundException {
+ assertNull(hook.postFindResource("META-INF/the.resource", classLoader, null));
+ }
+
+ @Test
+ public void findResourcesWithNoExportedPackages() throws FileNotFoundException {
+ assertNull(hook.postFindResources("META-INF/the.resource", classLoader, null));
+ }
+
+ @Test
+ public void findResource() throws FileNotFoundException, MalformedURLException {
+ ExportedPackage exportedPackage = createMock(ExportedPackage.class);
+ expect(exportedPackage.getImportingBundles()).andReturn(new Bundle[] {this.principleBundle});
+ replay(exportedPackage);
+
+ hook.exportedPackages.put(this.installedBundleTwo, new ExportedPackage[] {exportedPackage});
+
+ URL resourceUrl = new URL("file:/resource");
+ this.installedBundleTwo.addResource("META-INF/the.resource", resourceUrl);
+
+ assertEquals(resourceUrl, this.hook.postFindResource("META-INF/the.resource", this.classLoader, null));
+ }
+
+ @Test
+ public void findResources() throws FileNotFoundException, MalformedURLException {
+ ExportedPackage exportedPackage = createMock(ExportedPackage.class);
+ expect(exportedPackage.getImportingBundles()).andReturn(new Bundle[] {this.principleBundle}).anyTimes();
+ replay(exportedPackage);
+
+ hook.exportedPackages.put(this.installedBundleTwo, new ExportedPackage[] {exportedPackage});
+ hook.exportedPackages.put(this.installedBundleOne, new ExportedPackage[] {exportedPackage});
+
+ URL resourceUrlOne = new URL("file:/resource/one");
+ URL resourceUrlTwo = new URL("file:/resource/two");
+
+ this.installedBundleOne.addResources("META-INF/the.resource", createEnumeration(resourceUrlOne));
+ this.installedBundleTwo.addResources("META-INF/the.resource", createEnumeration(resourceUrlTwo));
+
+ Enumeration<?> postFindResources = this.hook.postFindResources("META-INF/the.resource", this.classLoader, null);
+ assertNotNull(postFindResources);
+
+ List<URL> results = new ArrayList<URL>();
+ while (postFindResources.hasMoreElements()) {
+ results.add((URL)postFindResources.nextElement());
+ }
+
+ assertEquals(2, results.size());
+ assertTrue(results.contains(resourceUrlOne));
+ assertTrue(results.contains(resourceUrlTwo));
+ }
+
+ private Enumeration<URL> createEnumeration(URL url) {
+ Vector<URL> vector = new Vector<URL>();
+ vector.add(url);
+ return vector.elements();
+ }
+
+ private static final class UnitTestMetaInfResourceClassLoaderDelegateHook extends MetaInfResourceClassLoaderDelegateHook {
+
+ public UnitTestMetaInfResourceClassLoaderDelegateHook(BundleContext systemBundleContext) {
+ super(systemBundleContext, null);
+ }
+
+ private final Map<Bundle, ExportedPackage[]> exportedPackages = new HashMap<Bundle, ExportedPackage[]>();
+
+ @Override
+ protected ExportedPackage[] getExportedPackages(Bundle bundle) {
+ return this.exportedPackages.get(bundle);
+ }
+ }
+}
diff --git a/org.eclipse.virgo.kernel.equinox.extensions/src/test/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/PluggableClassLoadingHookTests.java b/org.eclipse.virgo.kernel.equinox.extensions/src/test/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/PluggableClassLoadingHookTests.java
new file mode 100644
index 00000000..70715e14
--- /dev/null
+++ b/org.eclipse.virgo.kernel.equinox.extensions/src/test/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/PluggableClassLoadingHookTests.java
@@ -0,0 +1,102 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.osgi.extensions.equinox.hooks;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.osgi.baseadaptor.BaseData;
+import org.eclipse.osgi.baseadaptor.loader.BaseClassLoader;
+import org.eclipse.osgi.framework.adaptor.BundleProtectionDomain;
+import org.eclipse.osgi.framework.adaptor.ClassLoaderDelegate;
+import org.eclipse.osgi.launch.Equinox;
+import org.eclipse.virgo.osgi.extensions.equinox.EquinoxLauncherConfiguration;
+import org.eclipse.virgo.osgi.extensions.equinox.ExtendedEquinoxLauncher;
+import org.eclipse.virgo.osgi.extensions.equinox.hooks.ClassLoaderCreator;
+import org.eclipse.virgo.osgi.extensions.equinox.hooks.PluggableClassLoadingHook;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+
+
+/**
+ */
+public class PluggableClassLoadingHookTests {
+
+ private Equinox osgi;
+
+ private BundleContext context;
+
+ @Before
+ public void setUp() throws BundleException {
+ EquinoxLauncherConfiguration config = new EquinoxLauncherConfiguration();
+ config.setConfigPath(new File("target/config").toURI());
+ config.setInstallPath(new File("target/install").toURI());
+
+ this.osgi = ExtendedEquinoxLauncher.launch(config);
+ this.context = osgi.getBundleContext();
+ }
+
+ @After
+ public void after() throws BundleException {
+ if(this.osgi != null) {
+ this.osgi.stop();
+ }
+ }
+
+ @Test
+ public void testAddClassLoaderCreator() throws Exception {
+ final List<BaseData> baseDatas = new ArrayList<BaseData>();
+
+ ClassLoaderCreator creator = new ClassLoaderCreator() {
+
+ public BaseClassLoader createClassLoader(ClassLoader parent, ClassLoaderDelegate delegate, BundleProtectionDomain domain, BaseData data,
+ String[] bundleclasspath) {
+ baseDatas.add(data);
+ return null;
+ }
+
+ };
+
+ PluggableClassLoadingHook.getInstance().setClassLoaderCreator(creator);
+ this.context.registerService(ClassLoaderCreator.class.getName(), creator, null);
+
+ Bundle b = this.context.installBundle(new File("src/test/resources/hooks/classloading/bundle").toURI().toString());
+ try {
+ b.loadClass("foo");
+ fail("shouldn't be able to load foo!");
+ } catch (ClassNotFoundException e) {
+ // expected
+ assertEquals(1, baseDatas.size());
+ assertEquals("hooks.classloading", baseDatas.get(0).getSymbolicName());
+ }
+ }
+
+ @Test
+ public void testBundleClassLoaderParent() {
+ PluggableClassLoadingHook hook = PluggableClassLoadingHook.getInstance();
+
+ assertNull(hook.getBundleClassLoaderParent());
+
+ ClassLoader classLoader = getClass().getClassLoader();
+ hook.setBundleClassLoaderParent(classLoader);
+ assertEquals(classLoader, hook.getBundleClassLoaderParent());
+ }
+}
diff --git a/org.eclipse.virgo.kernel.equinox.extensions/src/test/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/PluggableDelegatingClassLoaderDelegateHookTests.java b/org.eclipse.virgo.kernel.equinox.extensions/src/test/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/PluggableDelegatingClassLoaderDelegateHookTests.java
new file mode 100644
index 00000000..7db05d62
--- /dev/null
+++ b/org.eclipse.virgo.kernel.equinox.extensions/src/test/java/org/eclipse/virgo/kernel/equinox/extensions/hooks/PluggableDelegatingClassLoaderDelegateHookTests.java
@@ -0,0 +1,122 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.osgi.extensions.equinox.hooks;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.reset;
+import static org.easymock.EasyMock.verify;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.util.Vector;
+
+import org.eclipse.osgi.framework.adaptor.BundleClassLoader;
+import org.eclipse.osgi.framework.adaptor.BundleData;
+import org.eclipse.osgi.framework.adaptor.ClassLoaderDelegateHook;
+import org.eclipse.virgo.osgi.extensions.equinox.hooks.PluggableDelegatingClassLoaderDelegateHook;
+import org.junit.Test;
+
+/**
+ * Tests for {@link PluggableDelegatingClassLoaderDelegateHook}
+ */
+public class PluggableDelegatingClassLoaderDelegateHookTests {
+
+ private final ClassLoaderDelegateHook hook1 = createMock(ClassLoaderDelegateHook.class);
+
+ private final ClassLoaderDelegateHook hook2 = createMock(ClassLoaderDelegateHook.class);
+
+ private final PluggableDelegatingClassLoaderDelegateHook delegatingHook = PluggableDelegatingClassLoaderDelegateHook.getInstance();
+
+ private final BundleClassLoader classLoader = createMock(BundleClassLoader.class);
+
+ private final BundleData bundleData = createMock(BundleData.class);
+
+ @Test
+ public void preFindClass() throws Exception {
+ performTest(ClassLoaderDelegateHook.class.getMethod("preFindClass", String.class, BundleClassLoader.class, BundleData.class), String.class);
+ }
+
+ @Test
+ public void postFindClass() throws Exception {
+ performTest(ClassLoaderDelegateHook.class.getMethod("postFindClass", String.class, BundleClassLoader.class, BundleData.class), String.class);
+ }
+
+ @Test
+ public void preFindResource() throws Exception {
+ performTest(ClassLoaderDelegateHook.class.getMethod("preFindResource", String.class, BundleClassLoader.class, BundleData.class), new URL("file:foo"));
+ }
+
+ @Test
+ public void postFindResource() throws Exception {
+ performTest(ClassLoaderDelegateHook.class.getMethod("postFindResource", String.class, BundleClassLoader.class, BundleData.class), new URL("file:foo"));
+ }
+
+ @Test
+ public void preFindLibrary() throws Exception {
+ performTest(ClassLoaderDelegateHook.class.getMethod("preFindLibrary", String.class, BundleClassLoader.class, BundleData.class), "library");
+ }
+
+ @Test
+ public void postFindLibrary() throws Exception {
+ performTest(ClassLoaderDelegateHook.class.getMethod("postFindLibrary", String.class, BundleClassLoader.class, BundleData.class), "library");
+ }
+
+ @Test
+ public void preFindResources() throws Exception {
+ performTest(ClassLoaderDelegateHook.class.getMethod("preFindResources", String.class, BundleClassLoader.class, BundleData.class), new Vector<URL>().elements());
+ }
+
+ @Test
+ public void postFindResources() throws Exception {
+ performTest(ClassLoaderDelegateHook.class.getMethod("postFindResources", String.class, BundleClassLoader.class, BundleData.class), new Vector<URL>().elements());
+ }
+
+ private void performTest(Method method, Object mockResult) throws Exception {
+
+ Object result = method.invoke(this.delegatingHook, "foo", this.classLoader, bundleData);
+ assertNull(result);
+
+ delegatingHook.addDelegate(hook1);
+ delegatingHook.addDelegate(hook2);
+
+ expect(method.invoke(hook1, "foo", this.classLoader, this.bundleData)).andReturn(null);
+ expect(method.invoke(hook2, "foo", this.classLoader, this.bundleData)).andReturn(null);
+
+ replay(hook1, hook2);
+
+ result = method.invoke(this.delegatingHook, "foo", this.classLoader, bundleData);
+ assertNull(result);
+
+ verify(hook1, hook2);
+
+ reset(hook1, hook2);
+
+ expect(method.invoke(hook1, "foo", this.classLoader, this.bundleData)).andReturn(mockResult);
+
+ replay(hook1, hook2);
+
+ result = method.invoke(this.delegatingHook, "foo", this.classLoader, bundleData);
+ assertEquals(mockResult, result);
+
+ delegatingHook.removeDelegate(hook1);
+ delegatingHook.removeDelegate(hook2);
+
+ result = method.invoke(this.delegatingHook, "foo", this.classLoader, bundleData);
+ assertNull(result);
+
+ verify(hook1, hook2);
+ }
+}
diff --git a/org.eclipse.virgo.kernel.equinox.extensions/src/test/resources/hooks/classloading/bundle/META-INF/MANIFEST.MF b/org.eclipse.virgo.kernel.equinox.extensions/src/test/resources/hooks/classloading/bundle/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..b018ed42
--- /dev/null
+++ b/org.eclipse.virgo.kernel.equinox.extensions/src/test/resources/hooks/classloading/bundle/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1
+Bundle-Manifest-Version: 2
+Bundle-SymbolicName: hooks.classloading

Back to the top