| /******************************************************************************* |
| * Copyright (c) 2012, 2020 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.osgi.internal.framework; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.net.URL; |
| import java.security.AccessControlContext; |
| import java.security.AccessController; |
| import java.security.InvalidKeyException; |
| import java.security.NoSuchAlgorithmException; |
| import java.security.NoSuchProviderException; |
| import java.security.Permission; |
| import java.security.PrivilegedAction; |
| import java.security.ProtectionDomain; |
| import java.security.SignatureException; |
| import java.security.cert.Certificate; |
| import java.security.cert.CertificateException; |
| import java.security.cert.X509Certificate; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Dictionary; |
| import java.util.EnumSet; |
| import java.util.Enumeration; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import org.eclipse.osgi.container.Module; |
| import org.eclipse.osgi.container.Module.Settings; |
| import org.eclipse.osgi.container.Module.StartOptions; |
| import org.eclipse.osgi.container.Module.State; |
| import org.eclipse.osgi.container.Module.StopOptions; |
| import org.eclipse.osgi.container.ModuleContainer; |
| import org.eclipse.osgi.container.ModuleContainerAdaptor.ContainerEvent; |
| import org.eclipse.osgi.container.ModuleContainerAdaptor.ModuleEvent; |
| import org.eclipse.osgi.container.ModuleLoader; |
| import org.eclipse.osgi.container.ModuleRevision; |
| import org.eclipse.osgi.container.ModuleWire; |
| import org.eclipse.osgi.container.ModuleWiring; |
| import org.eclipse.osgi.container.SystemModule; |
| import org.eclipse.osgi.framework.log.FrameworkLogEntry; |
| import org.eclipse.osgi.internal.container.InternalUtils; |
| import org.eclipse.osgi.internal.debug.Debug; |
| import org.eclipse.osgi.internal.loader.BundleLoader; |
| import org.eclipse.osgi.internal.loader.ModuleClassLoader; |
| import org.eclipse.osgi.internal.loader.classpath.ClasspathManager; |
| import org.eclipse.osgi.internal.messages.Msg; |
| import org.eclipse.osgi.internal.permadmin.EquinoxSecurityManager; |
| import org.eclipse.osgi.report.resolution.ResolutionReport; |
| import org.eclipse.osgi.signedcontent.SignedContent; |
| import org.eclipse.osgi.signedcontent.SignedContentFactory; |
| import org.eclipse.osgi.signedcontent.SignerInfo; |
| import org.eclipse.osgi.storage.BundleInfo.Generation; |
| import org.eclipse.osgi.storage.Storage; |
| import org.osgi.framework.AdaptPermission; |
| import org.osgi.framework.AdminPermission; |
| import org.osgi.framework.Bundle; |
| import org.osgi.framework.BundleContext; |
| import org.osgi.framework.BundleException; |
| import org.osgi.framework.BundleReference; |
| import org.osgi.framework.Constants; |
| import org.osgi.framework.FrameworkEvent; |
| import org.osgi.framework.FrameworkListener; |
| import org.osgi.framework.ServiceReference; |
| import org.osgi.framework.Version; |
| import org.osgi.framework.dto.BundleDTO; |
| import org.osgi.framework.dto.FrameworkDTO; |
| import org.osgi.framework.dto.ServiceReferenceDTO; |
| import org.osgi.framework.launch.Framework; |
| import org.osgi.framework.namespace.HostNamespace; |
| import org.osgi.framework.startlevel.BundleStartLevel; |
| import org.osgi.framework.startlevel.FrameworkStartLevel; |
| import org.osgi.framework.startlevel.dto.BundleStartLevelDTO; |
| import org.osgi.framework.startlevel.dto.FrameworkStartLevelDTO; |
| import org.osgi.framework.wiring.BundleRevision; |
| import org.osgi.framework.wiring.BundleRevisions; |
| import org.osgi.framework.wiring.BundleWiring; |
| import org.osgi.framework.wiring.FrameworkWiring; |
| import org.osgi.framework.wiring.dto.BundleRevisionDTO; |
| import org.osgi.framework.wiring.dto.BundleWiringDTO; |
| import org.osgi.framework.wiring.dto.FrameworkWiringDTO; |
| |
| public class EquinoxBundle implements Bundle, BundleReference { |
| |
| static class SystemBundle extends EquinoxBundle implements Framework { |
| class SystemBundleHeaders extends Dictionary<String, String> { |
| private final Dictionary<String, String> headers; |
| |
| public SystemBundleHeaders(Dictionary<String, String> headers) { |
| this.headers = headers; |
| } |
| |
| @Override |
| public Enumeration<String> elements() { |
| return headers.elements(); |
| } |
| |
| @Override |
| public String get(Object key) { |
| if (!(key instanceof String)) |
| return null; |
| |
| String sKey = (String) key; |
| if (Constants.EXPORT_PACKAGE.equalsIgnoreCase(sKey) || Constants.PROVIDE_CAPABILITY.equalsIgnoreCase(sKey)) { |
| String systemProvideHeader = getEquinoxContainer().getConfiguration().getConfiguration(EquinoxConfiguration.PROP_SYSTEM_PROVIDE_HEADER, EquinoxConfiguration.SYSTEM_PROVIDE_HEADER_SYSTEM_EXTRA); |
| boolean useSystemExtra = systemProvideHeader.equals(EquinoxConfiguration.SYSTEM_PROVIDE_HEADER_SYSTEM_EXTRA); |
| boolean useSystem = systemProvideHeader.equals(EquinoxConfiguration.SYSTEM_PROVIDE_HEADER_SYSTEM) || useSystemExtra; |
| String systemProp = useSystem ? (Constants.EXPORT_PACKAGE.equalsIgnoreCase(sKey) ? Constants.FRAMEWORK_SYSTEMPACKAGES : Constants.FRAMEWORK_SYSTEMCAPABILITIES) : null; |
| String systemExtraProp = useSystemExtra ? (Constants.EXPORT_PACKAGE.equalsIgnoreCase(sKey) ? Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA : Constants.FRAMEWORK_SYSTEMCAPABILITIES_EXTRA) : null; |
| return getExtra(sKey, systemProp, systemExtraProp); |
| } |
| |
| return headers.get(key); |
| } |
| |
| private String getExtra(String header, String systemProp, String systemExtraProp) { |
| String systemValue = systemProp != null ? getEquinoxContainer().getConfiguration().getConfiguration(systemProp) : null; |
| String systemExtraValue = systemExtraProp != null ? getEquinoxContainer().getConfiguration().getConfiguration(systemExtraProp) : null; |
| if (systemValue == null) |
| systemValue = systemExtraValue; |
| else if (systemExtraValue != null && systemExtraValue.trim().length() > 0) |
| systemValue += ", " + systemExtraValue; //$NON-NLS-1$ |
| String result = headers.get(header); |
| if (systemValue != null && systemValue.trim().length() > 0) { |
| if (result != null) |
| result += ", " + systemValue; //$NON-NLS-1$ |
| else |
| result = systemValue; |
| } |
| return result; |
| } |
| |
| @Override |
| public boolean isEmpty() { |
| return headers.isEmpty(); |
| } |
| |
| @Override |
| public Enumeration<String> keys() { |
| return headers.keys(); |
| } |
| |
| @Override |
| public String put(String key, String value) { |
| return headers.put(key, value); |
| } |
| |
| @Override |
| public String remove(Object key) { |
| return headers.remove(key); |
| } |
| |
| @Override |
| public int size() { |
| return headers.size(); |
| } |
| |
| } |
| |
| final List<FrameworkListener> initListeners = new ArrayList<>(0); |
| |
| class EquinoxSystemModule extends SystemModule { |
| public EquinoxSystemModule(ModuleContainer container) { |
| super(container); |
| } |
| |
| @Override |
| public Bundle getBundle() { |
| return SystemBundle.this; |
| } |
| |
| @Override |
| protected void cleanup(ModuleRevision revision) { |
| // Nothing to do |
| } |
| |
| @Override |
| protected void initWorker() throws BundleException { |
| EquinoxConfiguration config = getEquinoxContainer().getConfiguration(); |
| String initUUID = config.setConfiguration(EquinoxConfiguration.PROP_INIT_UUID, Boolean.TRUE.toString()); |
| if (initUUID != null) { |
| // this is not the first framework init, need to generate a new UUID |
| config.setConfiguration(Constants.FRAMEWORK_UUID, InternalUtils.newUUID(config)); |
| } |
| getEquinoxContainer().init(); |
| addInitFrameworkListeners(); |
| startWorker0(); |
| } |
| |
| @Override |
| protected void stopWorker() throws BundleException { |
| super.stopWorker(); |
| stopWorker0(); |
| getEquinoxContainer().close(); |
| } |
| |
| void asyncStop() throws BundleException { |
| if (getEquinoxContainer().getConfiguration().getDebug().DEBUG_SYSTEM_BUNDLE) { |
| Debug.printStackTrace(new Exception("Framework has been requested to stop.")); //$NON-NLS-1$ |
| } |
| lockStateChange(ModuleEvent.STOPPED); |
| try { |
| if (Module.ACTIVE_SET.contains(getState())) { |
| // TODO this still has a chance of a race condition: |
| // multiple threads could get started if stop is called over and over |
| Thread t = new Thread(new Runnable() { |
| @Override |
| public void run() { |
| try { |
| stop(); |
| } catch (Throwable e) { |
| SystemBundle.this.getEquinoxContainer().getLogServices().log(EquinoxContainer.NAME, FrameworkLogEntry.ERROR, "Error stopping the framework.", e); //$NON-NLS-1$ |
| } |
| } |
| }, "Framework stop - " + getEquinoxContainer().toString()); //$NON-NLS-1$ |
| t.start(); |
| } |
| } finally { |
| unlockStateChange(ModuleEvent.STOPPED); |
| } |
| } |
| |
| void asyncUpdate() throws BundleException { |
| if (getEquinoxContainer().getConfiguration().getDebug().DEBUG_SYSTEM_BUNDLE) { |
| Debug.printStackTrace(new Exception("Framework has been requested to update (restart).")); //$NON-NLS-1$ |
| } |
| lockStateChange(ModuleEvent.UPDATED); |
| try { |
| if (Module.ACTIVE_SET.contains(getState())) { |
| Thread t = new Thread(new Runnable() { |
| @Override |
| public void run() { |
| try { |
| update(); |
| } catch (Throwable e) { |
| SystemBundle.this.getEquinoxContainer().getLogServices().log(EquinoxContainer.NAME, FrameworkLogEntry.ERROR, "Error updating the framework.", e); //$NON-NLS-1$ |
| } |
| } |
| }, "Framework update - " + getEquinoxContainer().toString()); //$NON-NLS-1$ |
| t.start(); |
| } |
| } finally { |
| unlockStateChange(ModuleEvent.UPDATED); |
| } |
| } |
| } |
| |
| SystemBundle(ModuleContainer moduleContainer, EquinoxContainer equinoxContainer) { |
| super(moduleContainer, equinoxContainer); |
| } |
| |
| @Override |
| public void init() throws BundleException { |
| this.init((FrameworkListener[]) null); |
| } |
| |
| @Override |
| public void init(FrameworkListener... listeners) throws BundleException { |
| if (listeners != null) { |
| if (getEquinoxContainer().getConfiguration().getDebug().DEBUG_SYSTEM_BUNDLE) { |
| Debug.println("Initializing framework with framework listeners: " + listeners); //$NON-NLS-1$ |
| } |
| initListeners.addAll(Arrays.asList(listeners)); |
| } else { |
| if (getEquinoxContainer().getConfiguration().getDebug().DEBUG_SYSTEM_BUNDLE) { |
| Debug.println("Initializing framework with framework no listeners"); //$NON-NLS-1$ |
| } |
| } |
| try { |
| ((SystemModule) getModule()).init(); |
| } finally { |
| if (!initListeners.isEmpty()) { |
| getEquinoxContainer().getEventPublisher().flushFrameworkEvents(); |
| removeInitListeners(); |
| } |
| } |
| } |
| |
| void addInitFrameworkListeners() { |
| BundleContext context = createBundleContext(); |
| for (FrameworkListener initListener : initListeners) { |
| context.addFrameworkListener(initListener); |
| } |
| } |
| |
| void removeInitListeners() { |
| BundleContext context = createBundleContext(); |
| for (FrameworkListener initListener : initListeners) { |
| context.removeFrameworkListener(initListener); |
| } |
| initListeners.clear(); |
| } |
| |
| @Override |
| public FrameworkEvent waitForStop(long timeout) throws InterruptedException { |
| ContainerEvent event = ((SystemModule) getModule()).waitForStop(timeout); |
| return new FrameworkEvent(EquinoxContainerAdaptor.getType(event), this, null); |
| } |
| |
| @Override |
| Module createSystemModule(ModuleContainer moduleContainer) { |
| return new EquinoxSystemModule(moduleContainer); |
| } |
| |
| @Override |
| public void stop(final int options) throws BundleException { |
| getEquinoxContainer().checkAdminPermission(this, AdminPermission.EXECUTE); |
| ((EquinoxSystemModule) getModule()).asyncStop(); |
| } |
| |
| @Override |
| public void stop() throws BundleException { |
| stop(0); |
| } |
| |
| @Override |
| public void update(InputStream input) throws BundleException { |
| getEquinoxContainer().checkAdminPermission(this, AdminPermission.LIFECYCLE); |
| try { |
| if (input != null) |
| input.close(); |
| } catch (IOException e) { |
| // do nothing |
| } |
| ((EquinoxSystemModule) getModule()).asyncUpdate(); |
| } |
| |
| @Override |
| public void update() throws BundleException { |
| update(null); |
| } |
| |
| @Override |
| public void uninstall() throws BundleException { |
| getEquinoxContainer().checkAdminPermission(this, AdminPermission.LIFECYCLE); |
| throw new BundleException(Msg.BUNDLE_SYSTEMBUNDLE_UNINSTALL_EXCEPTION, BundleException.INVALID_OPERATION); |
| } |
| |
| @Override |
| public Dictionary<String, String> getHeaders(String locale) { |
| return new SystemBundleHeaders(super.getHeaders(locale)); |
| } |
| } |
| |
| private final EquinoxContainer equinoxContainer; |
| private final Module module; |
| private final Object monitor = new Object(); |
| private BundleContextImpl context; |
| private volatile SignedContent signedContent; |
| |
| private class EquinoxModule extends Module { |
| |
| @Override |
| protected void startWorker() throws BundleException { |
| startWorker0(); |
| } |
| |
| @Override |
| protected void stopWorker() throws BundleException { |
| stopWorker0(); |
| } |
| |
| public EquinoxModule(Long id, String location, ModuleContainer container, EnumSet<Settings> settings, int startlevel) { |
| super(id, location, container, settings, startlevel); |
| } |
| |
| @Override |
| public Bundle getBundle() { |
| return EquinoxBundle.this; |
| } |
| |
| @Override |
| protected void cleanup(ModuleRevision revision) { |
| Generation generation = (Generation) revision.getRevisionInfo(); |
| generation.delete(); |
| if (revision.equals(getCurrentRevision())) { |
| // uninstall case |
| generation.getBundleInfo().delete(); |
| } |
| } |
| } |
| |
| EquinoxBundle(ModuleContainer moduleContainer, EquinoxContainer equinoxContainer) { |
| this.equinoxContainer = equinoxContainer; |
| this.module = createSystemModule(moduleContainer); |
| } |
| |
| public EquinoxBundle(Long id, String location, ModuleContainer moduleContainer, EnumSet<Settings> settings, int startlevel, EquinoxContainer equinoxContainer) { |
| this.equinoxContainer = equinoxContainer; |
| this.module = new EquinoxModule(id, location, moduleContainer, settings, startlevel); |
| } |
| |
| Module createSystemModule(ModuleContainer moduleContainer) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public int compareTo(Bundle bundle) { |
| long idcomp = getBundleId() - bundle.getBundleId(); |
| return (idcomp < 0L) ? -1 : ((idcomp > 0L) ? 1 : 0); |
| } |
| |
| @Override |
| public int getState() { |
| switch (module.getState()) { |
| case INSTALLED : |
| return Bundle.INSTALLED; |
| case RESOLVED : |
| return Bundle.RESOLVED; |
| case STARTING : |
| case LAZY_STARTING : |
| return Bundle.STARTING; |
| case ACTIVE : |
| return Bundle.ACTIVE; |
| case STOPPING : |
| return Bundle.STOPPING; |
| case UNINSTALLED : |
| return Bundle.UNINSTALLED; |
| default : |
| throw new IllegalStateException("No valid bundle state for module state: " + module.getState()); //$NON-NLS-1$ |
| } |
| } |
| |
| @Override |
| public void start(int options) throws BundleException { |
| if (options == 0 && equinoxContainer.getConfiguration().getDebug().MONITOR_ACTIVATION) { |
| Debug.printStackTrace(new Exception("A persistent start has been called on bundle: " + this)); //$NON-NLS-1$ |
| } |
| module.start(getStartOptions(options)); |
| } |
| |
| private static StartOptions[] getStartOptions(int options) { |
| if (options == 0) { |
| return new StartOptions[0]; |
| } |
| Collection<StartOptions> result = new ArrayList<>(2); |
| if ((options & Bundle.START_TRANSIENT) != 0) { |
| result.add(StartOptions.TRANSIENT); |
| } |
| if ((options & Bundle.START_ACTIVATION_POLICY) != 0) { |
| result.add(StartOptions.USE_ACTIVATION_POLICY); |
| } |
| return result.toArray(new StartOptions[result.size()]); |
| } |
| |
| @Override |
| public void start() throws BundleException { |
| start(0); |
| } |
| |
| @Override |
| public void stop(int options) throws BundleException { |
| if (options == 0 && equinoxContainer.getConfiguration().getDebug().MONITOR_ACTIVATION) { |
| Debug.printStackTrace(new Exception("A persistent stop has been called on bundle: " + this)); //$NON-NLS-1$ |
| } |
| module.stop(getStopOptions(options)); |
| } |
| |
| private StopOptions[] getStopOptions(int options) { |
| if ((options & Bundle.STOP_TRANSIENT) == 0) { |
| return new StopOptions[0]; |
| } |
| return new StopOptions[] {StopOptions.TRANSIENT}; |
| } |
| |
| @Override |
| public void stop() throws BundleException { |
| stop(0); |
| } |
| |
| @Override |
| public void update(InputStream input) throws BundleException { |
| Storage storage = equinoxContainer.getStorage(); |
| storage.update(module, input); |
| signedContent = null; |
| } |
| |
| @Override |
| public void update() throws BundleException { |
| update(null); |
| } |
| |
| @Override |
| public void uninstall() throws BundleException { |
| // be sure to prime the headers with default local; calling priv method to avoid permission check |
| privGetHeaders(null); |
| Storage storage = equinoxContainer.getStorage(); |
| storage.getModuleContainer().uninstall(module); |
| } |
| |
| @Override |
| public Dictionary<String, String> getHeaders() { |
| return getHeaders(null); |
| } |
| |
| @Override |
| public Dictionary<String, String> getHeaders(String locale) { |
| equinoxContainer.checkAdminPermission(this, AdminPermission.METADATA); |
| return privGetHeaders(locale); |
| } |
| |
| private Dictionary<String, String> privGetHeaders(String locale) { |
| Generation current = (Generation) module.getCurrentRevision().getRevisionInfo(); |
| return current.getHeaders(locale); |
| } |
| |
| @Override |
| public long getBundleId() { |
| return module.getId(); |
| } |
| |
| @Override |
| public String getLocation() { |
| equinoxContainer.checkAdminPermission(getBundle(), AdminPermission.METADATA); |
| return module.getLocation(); |
| } |
| |
| @Override |
| public ServiceReference<?>[] getRegisteredServices() { |
| checkValid(); |
| BundleContextImpl current = getBundleContextImpl(); |
| return current == null ? null : equinoxContainer.getServiceRegistry().getRegisteredServices(current); |
| } |
| |
| @Override |
| public ServiceReference<?>[] getServicesInUse() { |
| checkValid(); |
| BundleContextImpl current = getBundleContextImpl(); |
| return current == null ? null : equinoxContainer.getServiceRegistry().getServicesInUse(current); |
| } |
| |
| @Override |
| public boolean hasPermission(Object permission) { |
| Generation current = (Generation) module.getCurrentRevision().getRevisionInfo(); |
| ProtectionDomain domain = current.getDomain(); |
| if (domain != null) { |
| if (permission instanceof Permission) { |
| SecurityManager sm = System.getSecurityManager(); |
| if (sm instanceof EquinoxSecurityManager) { |
| /* |
| * If the FrameworkSecurityManager is active, we need to do checks the "right" way. |
| * We can exploit our knowledge that the security context of FrameworkSecurityManager |
| * is an AccessControlContext to invoke it properly with the ProtectionDomain. |
| */ |
| AccessControlContext acc = new AccessControlContext(new ProtectionDomain[] {domain}); |
| try { |
| sm.checkPermission((Permission) permission, acc); |
| return true; |
| } catch (Exception e) { |
| return false; |
| } |
| } |
| return domain.implies((Permission) permission); |
| } |
| return false; |
| } |
| return true; |
| } |
| |
| @Override |
| public URL getResource(String name) { |
| try { |
| equinoxContainer.checkAdminPermission(this, AdminPermission.RESOURCE); |
| } catch (SecurityException e) { |
| return null; |
| } |
| checkValid(); |
| if (isFragment()) { |
| return null; |
| } |
| |
| ModuleClassLoader classLoader = getModuleClassLoader(false); |
| if (classLoader != null) { |
| return classLoader.getResource(name); |
| } |
| |
| return new ClasspathManager((Generation) module.getCurrentRevision().getRevisionInfo(), null).findLocalResource(name); |
| } |
| |
| @Override |
| public String getSymbolicName() { |
| return module.getCurrentRevision().getSymbolicName(); |
| } |
| |
| @Override |
| public Version getVersion() { |
| return module.getCurrentRevision().getVersion(); |
| } |
| |
| @Override |
| public Class<?> loadClass(String name) throws ClassNotFoundException { |
| try { |
| equinoxContainer.checkAdminPermission(this, AdminPermission.CLASS); |
| } catch (SecurityException e) { |
| throw new ClassNotFoundException(name, e); |
| } |
| checkValid(); |
| if (isFragment()) { |
| throw new ClassNotFoundException("Can not load a class from a fragment bundle: " + this); //$NON-NLS-1$ |
| } |
| try { |
| ModuleClassLoader classLoader = getModuleClassLoader(true); |
| if (classLoader != null) { |
| if (name.length() > 0 && name.charAt(0) == '[') |
| return Class.forName(name, false, classLoader); |
| return classLoader.loadClass(name); |
| } |
| } catch (ClassNotFoundException e) { |
| // This is an equinox-ism, check compatibility flag |
| boolean compatibilityLazyTrigger = equinoxContainer.getConfiguration().compatibilityLazyTriggerOnFailLoad; |
| // On failure attempt to activate lazy activating bundles. |
| if (compatibilityLazyTrigger && State.LAZY_STARTING.equals(module.getState())) { |
| try { |
| module.start(StartOptions.LAZY_TRIGGER); |
| } catch (BundleException e1) { |
| equinoxContainer.getLogServices().log(EquinoxContainer.NAME, FrameworkLogEntry.WARNING, e.getMessage(), e); |
| } |
| } |
| throw e; |
| } |
| throw new ClassNotFoundException("No class loader available for the bundle: " + this); //$NON-NLS-1$ |
| } |
| |
| private ModuleClassLoader getModuleClassLoader(boolean logResolveError) { |
| ResolutionReport report = resolve(); |
| if (logResolveError && !Module.RESOLVED_SET.contains(module.getState())) { |
| String reportMessage = report.getResolutionReportMessage(module.getCurrentRevision()); |
| equinoxContainer.getEventPublisher().publishFrameworkEvent(FrameworkEvent.ERROR, this, new BundleException(reportMessage, BundleException.RESOLVE_ERROR)); |
| } |
| return AccessController.doPrivileged(new PrivilegedAction<ModuleClassLoader>() { |
| @Override |
| public ModuleClassLoader run() { |
| ModuleWiring wiring = getModule().getCurrentRevision().getWiring(); |
| if (wiring != null) { |
| ModuleLoader moduleLoader = wiring.getModuleLoader(); |
| if (moduleLoader instanceof BundleLoader) { |
| return ((BundleLoader) moduleLoader).getModuleClassLoader(); |
| } |
| } |
| return null; |
| } |
| }); |
| } |
| |
| @Override |
| public Enumeration<URL> getResources(String name) throws IOException { |
| try { |
| equinoxContainer.checkAdminPermission(this, AdminPermission.RESOURCE); |
| } catch (SecurityException e) { |
| return null; |
| } |
| checkValid(); |
| if (isFragment()) { |
| return null; |
| } |
| ModuleClassLoader classLoader = getModuleClassLoader(false); |
| Enumeration<URL> result = null; |
| if (classLoader != null) { |
| result = classLoader.getResources(name); |
| } else { |
| result = new ClasspathManager((Generation) module.getCurrentRevision().getRevisionInfo(), null).findLocalResources(name); |
| } |
| return result != null && result.hasMoreElements() ? result : null; |
| } |
| |
| @Override |
| public Enumeration<String> getEntryPaths(String path) { |
| try { |
| equinoxContainer.checkAdminPermission(this, AdminPermission.RESOURCE); |
| } catch (SecurityException e) { |
| return null; |
| } |
| checkValid(); |
| Generation current = (Generation) getModule().getCurrentRevision().getRevisionInfo(); |
| return current.getBundleFile().getEntryPaths(path); |
| } |
| |
| @Override |
| public URL getEntry(String path) { |
| try { |
| equinoxContainer.checkAdminPermission(this, AdminPermission.RESOURCE); |
| } catch (SecurityException e) { |
| return null; |
| } |
| checkValid(); |
| Generation current = (Generation) getModule().getCurrentRevision().getRevisionInfo(); |
| return current.getEntry(path); |
| } |
| |
| @Override |
| public long getLastModified() { |
| return module.getLastModified(); |
| } |
| |
| @Override |
| public Enumeration<URL> findEntries(String path, String filePattern, boolean recurse) { |
| try { |
| equinoxContainer.checkAdminPermission(this, AdminPermission.RESOURCE); |
| } catch (SecurityException e) { |
| return null; |
| } |
| checkValid(); |
| resolve(); |
| return Storage.findEntries(getGenerations(), path, filePattern, recurse ? BundleWiring.FINDENTRIES_RECURSE : 0); |
| } |
| |
| @Override |
| public BundleContext getBundleContext() { |
| equinoxContainer.checkAdminPermission(this, AdminPermission.CONTEXT); |
| return createBundleContext(); |
| } |
| |
| BundleContextImpl createBundleContext() { |
| if (isFragment()) { |
| // fragments cannot have contexts |
| return null; |
| } |
| synchronized (this.monitor) { |
| if (context == null) { |
| // only create the context if we are starting, active or stopping |
| // this is so that SCR can get the context for lazy-start bundles |
| if (Module.ACTIVE_SET.contains(module.getState())) { |
| context = new BundleContextImpl(this, equinoxContainer); |
| } |
| } |
| return context; |
| } |
| } |
| |
| private BundleContextImpl getBundleContextImpl() { |
| synchronized (this.monitor) { |
| return context; |
| } |
| } |
| |
| @Override |
| public Map<X509Certificate, List<X509Certificate>> getSignerCertificates(int signersType) { |
| try { |
| SignedContent current = getSignedContent(); |
| SignerInfo[] infos = current == null ? null : current.getSignerInfos(); |
| if (infos.length == 0) |
| return Collections.emptyMap(); |
| Map<X509Certificate, List<X509Certificate>> results = new HashMap<>(infos.length); |
| for (SignerInfo info : infos) { |
| if (signersType == SIGNERS_TRUSTED && !info.isTrusted()) { |
| continue; |
| } |
| Certificate[] certs = info.getCertificateChain(); |
| if (certs == null || certs.length == 0) |
| continue; |
| List<X509Certificate> certChain = new ArrayList<>(); |
| for (Certificate cert : certs) { |
| certChain.add((X509Certificate) cert); |
| } |
| results.put((X509Certificate) certs[0], certChain); |
| } |
| return results; |
| } catch (Exception e) { |
| return Collections.emptyMap(); |
| } |
| } |
| |
| @Override |
| public final <A> A adapt(Class<A> adapterType) { |
| checkAdaptPermission(adapterType); |
| return adapt0(adapterType); |
| } |
| |
| private void readLock() { |
| equinoxContainer.getStorage().getModuleDatabase().readLock(); |
| } |
| |
| private void readUnlock() { |
| equinoxContainer.getStorage().getModuleDatabase().readUnlock(); |
| } |
| |
| @SuppressWarnings("unchecked") |
| private <A> A adapt0(Class<A> adapterType) { |
| if (AccessControlContext.class.equals(adapterType)) { |
| Generation current = (Generation) module.getCurrentRevision().getRevisionInfo(); |
| ProtectionDomain domain = current.getDomain(); |
| return (A) (domain == null ? null : new AccessControlContext(new ProtectionDomain[] {domain})); |
| } |
| |
| if (BundleContext.class.equals(adapterType)) { |
| try { |
| return (A) getBundleContext(); |
| } catch (SecurityException e) { |
| return null; |
| } |
| } |
| |
| if (BundleRevision.class.equals(adapterType)) { |
| if (module.getState().equals(State.UNINSTALLED)) { |
| return null; |
| } |
| return (A) module.getCurrentRevision(); |
| } |
| |
| if (BundleRevisions.class.equals(adapterType)) { |
| return (A) module.getRevisions(); |
| } |
| |
| if (BundleStartLevel.class.equals(adapterType)) { |
| return (A) module; |
| } |
| |
| if (BundleWiring.class.equals(adapterType)) { |
| if (module.getState().equals(State.UNINSTALLED)) { |
| return null; |
| } |
| ModuleRevision revision = module.getCurrentRevision(); |
| if (revision == null) { |
| return null; |
| } |
| return (A) revision.getWiring(); |
| } |
| |
| if (BundleDTO.class.equals(adapterType)) { |
| // Unfortunately we need to lock here to make sure the BSN and version |
| // are consistent in case of updates |
| readLock(); |
| try { |
| return (A) DTOBuilder.newBundleDTO(this); |
| } finally { |
| readUnlock(); |
| } |
| } |
| |
| if (BundleStartLevelDTO.class.equals(adapterType)) { |
| if (module.getState().equals(State.UNINSTALLED)) { |
| return null; |
| } |
| return (A) DTOBuilder.newBundleStartLevelDTO(this, module); |
| } |
| |
| if (BundleRevisionDTO.class.equals(adapterType)) { |
| if (module.getState().equals(State.UNINSTALLED)) { |
| return null; |
| } |
| return (A) DTOBuilder.newBundleRevisionDTO(module.getCurrentRevision()); |
| } |
| |
| if (BundleRevisionDTO[].class.equals(adapterType)) { |
| if (module.getState().equals(State.UNINSTALLED)) { |
| return null; |
| } |
| // No need to lock the database here since the ModuleRevisions object does the |
| // proper locking for us. |
| return (A) DTOBuilder.newArrayBundleRevisionDTO(module.getRevisions()); |
| } |
| |
| if (BundleWiringDTO.class.equals(adapterType)) { |
| if (module.getState().equals(State.UNINSTALLED)) { |
| return null; |
| } |
| readLock(); |
| try { |
| return (A) DTOBuilder.newBundleWiringDTO(module.getCurrentRevision()); |
| } finally { |
| readUnlock(); |
| } |
| } |
| |
| if (BundleWiringDTO[].class.equals(adapterType)) { |
| if (module.getState().equals(State.UNINSTALLED)) { |
| return null; |
| } |
| readLock(); |
| try { |
| return (A) DTOBuilder.newArrayBundleWiringDTO(module.getRevisions()); |
| } finally { |
| readUnlock(); |
| } |
| } |
| |
| if (ServiceReferenceDTO[].class.equals(adapterType)) { |
| if (module.getState().equals(State.UNINSTALLED)) { |
| return null; |
| } |
| BundleContextImpl current = getBundleContextImpl(); |
| ServiceReference<?>[] references = (current == null) ? null : equinoxContainer.getServiceRegistry().getRegisteredServices(current); |
| return (A) DTOBuilder.newArrayServiceReferenceDTO(references); |
| } |
| |
| if (getBundleId() == 0) { |
| if (Framework.class.equals(adapterType)) { |
| return (A) this; |
| } |
| |
| if (FrameworkStartLevel.class.equals(adapterType)) { |
| return (A) module.getContainer().getFrameworkStartLevel(); |
| } |
| |
| if (FrameworkWiring.class.equals(adapterType)) { |
| return (A) module.getContainer().getFrameworkWiring(); |
| } |
| |
| if (FrameworkDTO.class.equals(adapterType)) { |
| BundleContextImpl current = getBundleContextImpl(); |
| Map<String, String> configuration = equinoxContainer.getConfiguration().getConfiguration(); |
| readLock(); |
| try { |
| return (A) DTOBuilder.newFrameworkDTO(current, configuration); |
| } finally { |
| readUnlock(); |
| } |
| } |
| |
| if (FrameworkStartLevelDTO.class.equals(adapterType)) { |
| return (A) DTOBuilder.newFrameworkStartLevelDTO(module.getContainer().getFrameworkStartLevel()); |
| } |
| |
| if (FrameworkWiringDTO.class.equals(adapterType)) { |
| readLock(); |
| try { |
| Set<BundleWiring> allWirings = new HashSet<>(); |
| for (Module m : module.getContainer().getModules()) { |
| for (BundleRevision revision : m.getRevisions().getRevisions()) { |
| BundleWiring wiring = revision.getWiring(); |
| if (wiring != null) { |
| allWirings.add(wiring); |
| } |
| } |
| } |
| for (ModuleRevision revision : module.getContainer().getRemovalPending()) { |
| BundleWiring wiring = revision.getWiring(); |
| if (wiring != null) { |
| allWirings.add(wiring); |
| } |
| } |
| return (A) DTOBuilder.newFrameworkWiringDTO(allWirings); |
| } finally { |
| readUnlock(); |
| } |
| } |
| } |
| |
| // Equinox extras |
| if (Module.class.equals(adapterType)) { |
| return (A) module; |
| } |
| if (ProtectionDomain.class.equals(adapterType)) { |
| Generation current = (Generation) module.getCurrentRevision().getRevisionInfo(); |
| return (A) current.getDomain(); |
| } |
| if (SignedContent.class.equals(adapterType)) { |
| return (A) getSignedContent(); |
| } |
| return null; |
| } |
| |
| private SignedContent getSignedContent() { |
| SignedContent current = signedContent; |
| if (current == null) { |
| SignedContentFactory factory = equinoxContainer.getSignedContentFactory(); |
| if (factory == null) { |
| return null; |
| } |
| try { |
| signedContent = current = factory.getSignedContent(this); |
| } catch (InvalidKeyException | SignatureException | CertificateException | NoSuchAlgorithmException |
| | NoSuchProviderException | IOException e) { |
| return null; |
| } |
| } |
| return current; |
| } |
| |
| /** |
| * Check for permission to adapt. |
| */ |
| private <A> void checkAdaptPermission(Class<A> adapterType) { |
| SecurityManager sm = System.getSecurityManager(); |
| if (sm == null) { |
| return; |
| } |
| sm.checkPermission(new AdaptPermission(adapterType.getName(), this, AdaptPermission.ADAPT)); |
| } |
| |
| @Override |
| public File getDataFile(String filename) { |
| checkValid(); |
| Generation current = (Generation) module.getCurrentRevision().getRevisionInfo(); |
| return current.getBundleInfo().getDataFile(filename); |
| } |
| |
| @Override |
| public Bundle getBundle() { |
| return this; |
| } |
| |
| public Module getModule() { |
| return module; |
| } |
| |
| private final void checkValid() { |
| if (module.getState().equals(State.UNINSTALLED)) |
| throw new IllegalStateException("Bundle has been uninstalled: " + this); //$NON-NLS-1$ |
| } |
| |
| public boolean isFragment() { |
| return (getModule().getCurrentRevision().getTypes() & BundleRevision.TYPE_FRAGMENT) != 0; |
| } |
| |
| void startWorker0() throws BundleException { |
| BundleContextImpl current = createBundleContext(); |
| if (current == null) { |
| throw new BundleException("Unable to create bundle context! " + this); //$NON-NLS-1$ |
| } |
| try { |
| current.start(); |
| } catch (BundleException e) { |
| current.close(); |
| synchronized (EquinoxBundle.this.monitor) { |
| context = null; |
| } |
| throw e; |
| } |
| } |
| |
| void stopWorker0() throws BundleException { |
| BundleContextImpl current = getBundleContextImpl(); |
| if (current != null) { |
| try { |
| current.stop(); |
| } finally { |
| current.close(); |
| } |
| synchronized (EquinoxBundle.this.monitor) { |
| context = null; |
| } |
| } |
| } |
| |
| ResolutionReport resolve() { |
| if (!Module.RESOLVED_SET.contains(module.getState())) { |
| return module.getContainer().resolve(Collections.singletonList(module), true); |
| } |
| return null; |
| } |
| |
| List<Generation> getGenerations() { |
| List<Generation> result = new ArrayList<>(); |
| ModuleRevision current = getModule().getCurrentRevision(); |
| result.add((Generation) current.getRevisionInfo()); |
| ModuleWiring wiring = current.getWiring(); |
| if (wiring != null) { |
| List<ModuleWire> hostWires = wiring.getProvidedModuleWires(HostNamespace.HOST_NAMESPACE); |
| if (hostWires != null) { |
| for (ModuleWire hostWire : hostWires) { |
| result.add((Generation) hostWire.getRequirer().getRevisionInfo()); |
| } |
| } |
| } |
| return result; |
| } |
| |
| EquinoxContainer getEquinoxContainer() { |
| return equinoxContainer; |
| } |
| |
| @Override |
| public String toString() { |
| String name = getSymbolicName(); |
| if (name == null) |
| name = "unknown"; //$NON-NLS-1$ |
| return (name + '_' + getVersion() + " [" + getBundleId() + "]"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| } |