blob: b81704cc6bc54c244171222658599de00acf46a5 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2012, 2017 IBM Corporation and others.
* 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.osgi.internal.framework;
import java.io.IOException;
import java.security.AccessController;
import java.util.*;
import java.util.concurrent.*;
import org.eclipse.osgi.framework.eventmgr.ListenerQueue;
import org.eclipse.osgi.framework.log.FrameworkLogEntry;
import org.eclipse.osgi.framework.util.SecureAction;
import org.eclipse.osgi.internal.framework.legacy.PackageAdminImpl;
import org.eclipse.osgi.internal.framework.legacy.StartLevelImpl;
import org.eclipse.osgi.internal.hookregistry.ClassLoaderHook;
import org.eclipse.osgi.internal.hookregistry.HookRegistry;
import org.eclipse.osgi.internal.location.EquinoxLocations;
import org.eclipse.osgi.internal.log.EquinoxLogServices;
import org.eclipse.osgi.internal.messages.Msg;
import org.eclipse.osgi.internal.serviceregistry.ServiceRegistry;
import org.eclipse.osgi.signedcontent.SignedContentFactory;
import org.eclipse.osgi.storage.Storage;
import org.eclipse.osgi.util.ManifestElement;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.*;
import org.osgi.service.packageadmin.PackageAdmin;
import org.osgi.service.startlevel.StartLevel;
import org.osgi.util.tracker.ServiceTracker;
@SuppressWarnings("deprecation")
public class EquinoxContainer implements ThreadFactory, Runnable {
public static final String NAME = "org.eclipse.osgi"; //$NON-NLS-1$
static final SecureAction secureAction = AccessController.doPrivileged(SecureAction.createSecureAction());
private final EquinoxConfiguration equinoxConfig;
private final EquinoxLogServices logServices;
private final Storage storage;
private final PackageAdmin packageAdmin;
private final StartLevel startLevel;
private final Set<String> bootDelegation;
private final String[] bootDelegationStems;
private final boolean bootDelegateAll;
private final boolean isProcessClassRecursionSupportedByAll;
private final EquinoxEventPublisher eventPublisher;
private final Object monitor = new Object();
private ServiceRegistry serviceRegistry;
private ContextFinder contextFinder;
private ServiceTracker<SignedContentFactory, SignedContentFactory> signedContentFactory;
private ScheduledExecutorService executor;
private StorageSaver storageSaver;
public EquinoxContainer(Map<String, ?> configuration) {
this.equinoxConfig = new EquinoxConfiguration(configuration, new HookRegistry(this));
this.logServices = new EquinoxLogServices(this.equinoxConfig);
this.equinoxConfig.getHookRegistry().initialize();
try {
this.storage = Storage.createStorage(this);
} catch (IOException e) {
throw new RuntimeException("Error initializing storage.", e); //$NON-NLS-1$
} catch (BundleException e) {
throw new RuntimeException("Error initializing storage.", e); //$NON-NLS-1$
}
this.packageAdmin = new PackageAdminImpl(storage.getModuleContainer());
this.startLevel = new StartLevelImpl(storage.getModuleContainer());
this.eventPublisher = new EquinoxEventPublisher(this);
// set the boot delegation according to the osgi boot delegation property
// TODO unfortunately this has to be done after constructing storage so the vm profile is loaded
// TODO ideally this should be in equinox configuration or perhaps in storage
String bootDelegationProp = equinoxConfig.getConfiguration(Constants.FRAMEWORK_BOOTDELEGATION);
String[] bootPackages = ManifestElement.getArrayFromList(bootDelegationProp, ","); //$NON-NLS-1$
HashSet<String> exactMatch = new HashSet<>(bootPackages.length);
List<String> stemMatch = new ArrayList<>(bootPackages.length);
boolean delegateAllValue = false;
for (int i = 0; i < bootPackages.length; i++) {
if (bootPackages[i].equals("*")) { //$NON-NLS-1$
delegateAllValue = true;
exactMatch.clear();
stemMatch.clear();
break;
} else if (bootPackages[i].endsWith("*")) { //$NON-NLS-1$
if (bootPackages[i].length() > 2 && bootPackages[i].endsWith(".*")) //$NON-NLS-1$
stemMatch.add(bootPackages[i].substring(0, bootPackages[i].length() - 1));
} else {
exactMatch.add(bootPackages[i]);
}
}
bootDelegateAll = delegateAllValue;
bootDelegation = exactMatch;
bootDelegationStems = stemMatch.isEmpty() ? null : stemMatch.toArray(new String[stemMatch.size()]);
// Detect if all hooks can support recursive class processing
boolean supportRecursion = true;
for (ClassLoaderHook hook : equinoxConfig.getHookRegistry().getClassLoaderHooks()) {
supportRecursion &= hook.isProcessClassRecursionSupported();
}
isProcessClassRecursionSupportedByAll = supportRecursion;
}
public Storage getStorage() {
return storage;
}
public EquinoxConfiguration getConfiguration() {
return equinoxConfig;
}
public EquinoxLocations getLocations() {
return equinoxConfig.getEquinoxLocations();
}
public EquinoxLogServices getLogServices() {
return logServices;
}
public PackageAdmin getPackageAdmin() {
return packageAdmin;
}
public StartLevel getStartLevel() {
return startLevel;
}
public SignedContentFactory getSignedContentFactory() {
ServiceTracker<SignedContentFactory, SignedContentFactory> current;
synchronized (this.monitor) {
current = signedContentFactory;
}
return current == null ? null : current.getService();
}
public boolean isBootDelegationPackage(String name) {
if (bootDelegateAll)
return true;
if (bootDelegation.contains(name))
return true;
if (bootDelegationStems != null)
for (int i = 0; i < bootDelegationStems.length; i++)
if (name.startsWith(bootDelegationStems[i]))
return true;
return false;
}
public boolean isProcessClassRecursionSupportedByAll() {
return isProcessClassRecursionSupportedByAll;
}
void init() {
eventPublisher.init();
synchronized (this.monitor) {
serviceRegistry = new ServiceRegistry(this);
initializeContextFinder();
executor = Executors.newScheduledThreadPool(1, this);
// be sure to initialize the executor threads
executor.execute(this);
storageSaver = new StorageSaver(this);
}
}
void close() {
StorageSaver currentSaver;
Storage currentStorage;
ScheduledExecutorService currentExecutor;
synchronized (this.monitor) {
serviceRegistry = null;
currentSaver = storageSaver;
currentStorage = storage;
currentExecutor = executor;
}
// do this outside of the lock to avoid deadlock
currentSaver.close();
currentStorage.close();
// Must be done last since it will result in termination of the
// framework active thread.
currentExecutor.shutdown();
}
private void initializeContextFinder() {
Thread current = Thread.currentThread();
try {
ClassLoader parent = null;
// check property for specified parent
String type = equinoxConfig.getConfiguration(EquinoxConfiguration.PROP_CONTEXTCLASSLOADER_PARENT);
if (EquinoxConfiguration.CONTEXTCLASSLOADER_PARENT_APP.equals(type))
parent = ClassLoader.getSystemClassLoader();
else if (EquinoxConfiguration.CONTEXTCLASSLOADER_PARENT_BOOT.equals(type))
parent = EquinoxContainerAdaptor.BOOT_CLASSLOADER;
else if (EquinoxConfiguration.CONTEXTCLASSLOADER_PARENT_FWK.equals(type))
parent = EquinoxContainer.class.getClassLoader();
else if (EquinoxConfiguration.CONTEXTCLASSLOADER_PARENT_EXT.equals(type)) {
ClassLoader appCL = ClassLoader.getSystemClassLoader();
if (appCL != null)
parent = appCL.getParent();
} else { // default is ccl (null or any other value will use ccl)
parent = current.getContextClassLoader();
}
contextFinder = new ContextFinder(parent);
current.setContextClassLoader(contextFinder);
return;
} catch (Exception e) {
logServices.log(NAME, FrameworkLogEntry.INFO, NLS.bind(Msg.CANNOT_SET_CONTEXTFINDER, null), e);
}
}
public EquinoxEventPublisher getEventPublisher() {
synchronized (this.monitor) {
return eventPublisher;
}
}
ScheduledExecutorService getScheduledExecutor() {
synchronized (this.monitor) {
return executor;
}
}
public ServiceRegistry getServiceRegistry() {
synchronized (this.monitor) {
return serviceRegistry;
}
}
public ContextFinder getContextFinder() {
synchronized (this.monitor) {
return contextFinder;
}
}
public <K, V, E> ListenerQueue<K, V, E> newListenerQueue() {
return eventPublisher.newListenerQueue();
}
void checkAdminPermission(Bundle bundle, String action) {
if (bundle == null)
return;
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkPermission(new AdminPermission(bundle, action));
}
public void handleRuntimeError(Throwable t) {
// TODO need to call some hook here
}
void systemStart(BundleContext bc) {
synchronized (this.monitor) {
signedContentFactory = new ServiceTracker<>(bc, SignedContentFactory.class, null);
}
signedContentFactory.open();
}
void systemStop(BundleContext bc) {
ServiceTracker<SignedContentFactory, SignedContentFactory> current;
synchronized (this.monitor) {
current = signedContentFactory;
}
if (current != null) {
current.close();
}
}
@Override
public String toString() {
String UUID = equinoxConfig == null ? null : equinoxConfig.getConfiguration(Constants.FRAMEWORK_UUID);
return "Equinox Container: " + UUID; //$NON-NLS-1$
}
StorageSaver getStorageSaver() {
synchronized (this.monitor) {
return storageSaver;
}
}
@Override
public Thread newThread(Runnable r) {
String type = equinoxConfig.getConfiguration(EquinoxConfiguration.PROP_ACTIVE_THREAD_TYPE, EquinoxConfiguration.ACTIVE_THREAD_TYPE_NORMAL);
Thread t = new Thread(r, "Active Thread: " + toString()); //$NON-NLS-1$
if (EquinoxConfiguration.ACTIVE_THREAD_TYPE_NORMAL.equals(type)) {
t.setDaemon(false);
} else {
t.setDaemon(true);
}
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
@Override
public void run() {
// Do nothing; just used to ensure the active thread is created during init
}
}