package org.eclipse.osgi.storage;
import java.io.File;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.eclipse.osgi.container.Module;
import org.eclipse.osgi.container.ModuleCapability;
import org.eclipse.osgi.container.ModuleRevision;
import org.eclipse.osgi.container.ModuleWire;
import org.eclipse.osgi.container.ModuleWiring;
import org.eclipse.osgi.container.namespaces.EquinoxModuleDataNamespace;
import org.eclipse.osgi.framework.util.ArrayMap;
import org.eclipse.osgi.internal.framework.EquinoxConfiguration;
import org.eclipse.osgi.internal.hookregistry.ActivatorHookFactory;
import org.eclipse.osgi.internal.hookregistry.HookRegistry;
import org.eclipse.osgi.internal.messages.Msg;
import org.eclipse.osgi.internal.url.MultiplexingFactory;
import org.eclipse.osgi.storage.BundleInfo.Generation;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.FrameworkEvent;
import org.osgi.framework.namespace.HostNamespace;
import org.osgi.framework.wiring.BundleWiring;
import org.osgi.resource.Capability;
public class FrameworkExtensionInstaller {
private static final ClassLoader CL = FrameworkExtensionInstaller.class.getClassLoader();
private static final Method ADD_FWK_URL_METHOD = findAddURLMethod(CL, "addURL");
private static final Method ADD_FWK_FILE_PATH_METHOD = ADD_FWK_URL_METHOD == null ? findAddFilePathMethod(CL, "appendToClassPathForInstrumentation") : null;
private final ArrayMap<BundleActivator, Bundle> hookActivators = new ArrayMap<>(5);
private static Method findAddURLMethod(ClassLoader cl, String name) {
if (cl == null)
return null;
return findMethod(cl.getClass(), name, new Class[] {URL.class}, null);
}
private static Method findAddFilePathMethod(ClassLoader cl, String name) {
if (cl == null)
return null;
return findMethod(cl.getClass(), name, new Class[] {String.class}, MultiplexingFactory.setAccessible);
}
private static Method findMethod(Class<?> clazz, String name, Class<?>[] args, Collection<AccessibleObject> setAccessible) {
if (clazz == null)
return null;
try {
Method result = clazz.getDeclaredMethod(name, args);
if (setAccessible != null) {
setAccessible.add(result);
} else {
result.setAccessible(true);
}
return result;
} catch (SecurityException e) {
} catch (NoSuchMethodException | RuntimeException e) {
}
return findMethod(clazz.getSuperclass(), name, args, setAccessible);
}
private static void callAddURLMethod(URL arg) throws InvocationTargetException {
try {
ADD_FWK_URL_METHOD.invoke(CL, new Object[] {arg});
} catch (Throwable t) {
throw new InvocationTargetException(t);
}
}
private static void callAddFilePathMethod(File file) throws InvocationTargetException {
try {
ADD_FWK_FILE_PATH_METHOD.invoke(CL, new Object[] {file.getCanonicalPath()});
} catch (Throwable t) {
throw new InvocationTargetException(t);
}
}
private final EquinoxConfiguration configuration;
public FrameworkExtensionInstaller(EquinoxConfiguration configuraiton) {
this.configuration = configuraiton;
}
public void addExtensionContent(final Collection<ModuleRevision> revisions, final Module systemModule) throws BundleException {
if (System.getSecurityManager() == null) {
addExtensionContent0(revisions, systemModule);
} else {
try {
AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
@Override
public Void run() throws BundleException {
addExtensionContent0(revisions, systemModule);
return null;
}
});
} catch (PrivilegedActionException e) {
throw (BundleException) e.getCause();
}
}
}
void addExtensionContent0(Collection<ModuleRevision> revisions, Module systemModule) throws BundleException {
if (revisions.isEmpty()) {
return;
}
for (ModuleRevision revision : revisions) {
if (CL == null || (ADD_FWK_URL_METHOD == null && ADD_FWK_FILE_PATH_METHOD == null)) {
throw new BundleException("Cannot support framework extension bundles without a public addURL(URL) or appendToClassPathForInstrumentation(String) method on the framework class loader: " + revision.getBundle());
}
File[] files = getExtensionFiles(revision);
for (File file : files) {
if (file == null) {
continue;
}
try {
if (ADD_FWK_URL_METHOD != null) {
callAddURLMethod(StorageUtil.encodeFileURL(file));
} else if (ADD_FWK_FILE_PATH_METHOD != null) {
callAddFilePathMethod(file);
}
} catch (InvocationTargetException | MalformedURLException e) {
throw new BundleException("Error adding extension content.", e);
}
}
}
if (CL != null) {
try {
CL.loadClass("thisIsNotAClass");
} catch (ClassNotFoundException e) {
}
}
if (systemModule != null) {
BundleContext systemContext = systemModule.getBundle().getBundleContext();
for (ModuleRevision revision : revisions) {
if (systemContext != null) {
startExtensionActivator(revision, systemContext);
}
}
}
}
private File[] getExtensionFiles(ModuleRevision revision) {
List<ModuleCapability> metaDatas = revision.getModuleCapabilities(EquinoxModuleDataNamespace.MODULE_DATA_NAMESPACE);
@SuppressWarnings("unchecked")
List<String> paths = metaDatas.isEmpty() ? null : (List<String>) metaDatas.get(0).getAttributes().get(EquinoxModuleDataNamespace.CAPABILITY_CLASSPATH);
if (paths == null) {
paths = new ArrayList<>(1);
paths.add(".");
}
if (configuration.inDevelopmentMode()) {
paths = new ArrayList<>(paths);
String[] devPaths = configuration.getDevClassPath(revision.getSymbolicName());
Collections.addAll(paths, devPaths);
}
List<File> results = new ArrayList<>(paths.size());
for (String path : paths) {
if (".".equals(path)) {
results.add(((Generation) revision.getRevisionInfo()).getBundleFile().getBaseFile());
} else {
File result = ((Generation) revision.getRevisionInfo()).getBundleFile().getFile(path, false);
if (result != null)
results.add(result);
}
}
return results.toArray(new File[results.size()]);
}
public void startExtensionActivators(BundleContext context) {
HookRegistry hookRegistry = configuration.getHookRegistry();
List<ActivatorHookFactory> activatorHookFactories = hookRegistry.getActivatorHookFactories();
for (ActivatorHookFactory activatorFactory : activatorHookFactories) {
BundleActivator activator = activatorFactory.createActivator();
try {
startActivator(activator, context, null);
} catch (Exception e) {
configuration.getHookRegistry().getContainer().getEventPublisher().publishFrameworkEvent(FrameworkEvent.ERROR, null, e);
}
}
ModuleWiring systemWiring = (ModuleWiring) context.getBundle().adapt(BundleWiring.class);
if (systemWiring != null) {
List<ModuleWire> extensionWires = systemWiring.getProvidedModuleWires(HostNamespace.HOST_NAMESPACE);
for (ModuleWire extensionWire : extensionWires) {
ModuleRevision extensionRevision = extensionWire.getRequirer();
startExtensionActivator(extensionRevision, context);
}
}
}
public void stopExtensionActivators(BundleContext context) {
ArrayMap<BundleActivator, Bundle> current;
synchronized (hookActivators) {
current = new ArrayMap<>(hookActivators.getKeys(), hookActivators.getValues());
hookActivators.clear();
}
for (BundleActivator activator : current) {
try {
activator.stop(context);
} catch (Exception e) {
Bundle b = current.get(activator);
BundleException eventException = new BundleException(NLS.bind(Msg.BUNDLE_ACTIVATOR_EXCEPTION, new Object[] {activator.getClass(), "stop", b == null ? "" : b.getSymbolicName()}), BundleException.ACTIVATOR_ERROR, e);
configuration.getHookRegistry().getContainer().getEventPublisher().publishFrameworkEvent(FrameworkEvent.ERROR, b, eventException);
}
}
}
private void startExtensionActivator(ModuleRevision extensionRevision, BundleContext context) {
List<Capability> metadata = extensionRevision.getCapabilities(EquinoxModuleDataNamespace.MODULE_DATA_NAMESPACE);
if (metadata.isEmpty()) {
return;
}
String activatorName = (String) metadata.get(0).getAttributes().get(EquinoxModuleDataNamespace.CAPABILITY_ACTIVATOR);
if (activatorName == null) {
return;
}
BundleActivator activator = null;
try {
Class<?> activatorClass = Class.forName(activatorName);
activator = (BundleActivator) activatorClass.getConstructor().newInstance();
startActivator(activator, context, extensionRevision.getBundle());
} catch (Throwable e) {
BundleException eventException;
if (activator == null) {
eventException = new BundleException(Msg.BundleContextImpl_LoadActivatorError, BundleException.ACTIVATOR_ERROR, e);
} else {
eventException = new BundleException(NLS.bind(Msg.BUNDLE_ACTIVATOR_EXCEPTION, new Object[] {activator.getClass(), "start", extensionRevision.getSymbolicName()}), BundleException.ACTIVATOR_ERROR, e);
}
configuration.getHookRegistry().getContainer().getEventPublisher().publishFrameworkEvent(FrameworkEvent.ERROR, extensionRevision.getBundle(), eventException);
}
}
private void startActivator(BundleActivator activator, BundleContext context, Bundle b) throws Exception {
activator.start(context);
synchronized (hookActivators) {
hookActivators.put(activator, b);
}
}
}