package org.eclipse.osgi.container;
import java.io.Closeable;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
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.ResolutionLock.Permits;
import org.eclipse.osgi.container.ModuleContainerAdaptor.ContainerEvent;
import org.eclipse.osgi.container.ModuleContainerAdaptor.ModuleEvent;
import org.eclipse.osgi.container.ModuleDatabase.Sort;
import org.eclipse.osgi.container.ModuleRequirement.DynamicModuleRequirement;
import org.eclipse.osgi.framework.eventmgr.CopyOnWriteIdentityMap;
import org.eclipse.osgi.framework.eventmgr.EventDispatcher;
import org.eclipse.osgi.framework.eventmgr.EventManager;
import org.eclipse.osgi.framework.eventmgr.ListenerQueue;
import org.eclipse.osgi.framework.util.SecureAction;
import org.eclipse.osgi.framework.util.ThreadInfoReport;
import org.eclipse.osgi.internal.container.InternalUtils;
import org.eclipse.osgi.internal.container.LockSet;
import org.eclipse.osgi.internal.debug.Debug;
import org.eclipse.osgi.internal.framework.EquinoxConfiguration;
import org.eclipse.osgi.internal.messages.Msg;
import org.eclipse.osgi.report.resolution.ResolutionReport;
import org.eclipse.osgi.report.resolution.ResolutionReport.Entry;
import org.eclipse.osgi.service.debug.DebugOptions;
import org.eclipse.osgi.service.debug.DebugOptionsListener;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.AdminPermission;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
import org.osgi.framework.FrameworkListener;
import org.osgi.framework.Version;
import org.osgi.framework.namespace.HostNamespace;
import org.osgi.framework.namespace.IdentityNamespace;
import org.osgi.framework.namespace.PackageNamespace;
import org.osgi.framework.startlevel.FrameworkStartLevel;
import org.osgi.framework.wiring.BundleCapability;
import org.osgi.framework.wiring.BundleRevision;
import org.osgi.framework.wiring.FrameworkWiring;
import org.osgi.resource.Namespace;
import org.osgi.resource.Requirement;
import org.osgi.resource.Resource;
import org.osgi.resource.Wire;
import org.osgi.service.resolver.ResolutionException;
public final class ModuleContainer implements DebugOptionsListener {
private final static SecureAction secureAction = AccessController.doPrivileged(SecureAction.createSecureAction());
private final LockSet<String> locationLocks = new LockSet<>();
private final LockSet<String> nameLocks = new LockSet<>();
private final ContainerWiring frameworkWiring;
private final ContainerStartLevel frameworkStartLevel;
final ModuleDatabase moduleDatabase;
final ModuleContainerAdaptor adaptor;
private final ModuleResolver moduleResolver;
private final AtomicReference<SystemModule> refreshingSystemModule = new AtomicReference<>();
private final long moduleLockTimeout;
private final boolean autoStartOnResolve;
final boolean restrictParallelStart;
boolean DEBUG_MONITOR_LAZY = false;
boolean DEBUG_BUNDLE_START_TIME = false;
public ModuleContainer(ModuleContainerAdaptor adaptor, ModuleDatabase moduledataBase) {
this.adaptor = adaptor;
this.moduleResolver = new ModuleResolver(adaptor);
this.moduleDatabase = moduledataBase;
this.frameworkWiring = new ContainerWiring();
this.frameworkStartLevel = new ContainerStartLevel();
long tempModuleLockTimeout = 30;
String moduleLockTimeoutProp = adaptor.getProperty(EquinoxConfiguration.PROP_MODULE_LOCK_TIMEOUT);
if (moduleLockTimeoutProp != null) {
try {
tempModuleLockTimeout = Long.parseLong(moduleLockTimeoutProp);
if (tempModuleLockTimeout < 1) {
tempModuleLockTimeout = 1;
}
} catch (NumberFormatException e) {
}
}
this.moduleLockTimeout = tempModuleLockTimeout;
DebugOptions debugOptions = adaptor.getDebugOptions();
if (debugOptions != null) {
this.DEBUG_MONITOR_LAZY = debugOptions.getBooleanOption(Debug.OPTION_MONITOR_LAZY, false);
}
String autoStartOnResolveProp = adaptor.getProperty(EquinoxConfiguration.PROP_MODULE_AUTO_START_ON_RESOLVE);
if (autoStartOnResolveProp == null) {
autoStartOnResolveProp = Boolean.toString(true);
}
this.autoStartOnResolve = Boolean.parseBoolean(autoStartOnResolveProp);
this.restrictParallelStart = Boolean.parseBoolean(adaptor.getProperty(EquinoxConfiguration.PROP_EQUINOX_START_LEVEL_RESTRICT_PARALLEL));
}
public ModuleContainerAdaptor getAdaptor() {
return adaptor;
}
public List<Module> getModules() {
return moduleDatabase.getModules();
}
public Module getModule(long id) {
return moduleDatabase.getModule(id);
}
public Module getModule(String location) {
return moduleDatabase.getModule(location);
}
public static Requirement createRequirement(String namespace, Map<String, String> directives, Map<String, ?> attributes) {
return new ModuleRequirement(namespace, directives, attributes, null);
}
public Module install(Module origin, String location, ModuleRevisionBuilder builder, Object revisionInfo) throws BundleException {
long id = builder.getId();
ModuleRevisionBuilder adaptBuilder = getAdaptor().adaptModuleRevisionBuilder(ModuleEvent.INSTALLED, origin, builder, revisionInfo);
if (adaptBuilder != null) {
adaptBuilder.setInternalId(id);
builder = adaptBuilder;
}
String name = builder.getSymbolicName();
boolean locationLocked = false;
boolean nameLocked = false;
try {
try {
locationLocked = locationLocks.tryLock(location, 5, TimeUnit.SECONDS);
nameLocked = name != null && nameLocks.tryLock(name, 5, TimeUnit.SECONDS);
if (!locationLocked) {
throw new BundleException("Failed to obtain location lock for installation: " + location, BundleException.STATECHANGE_ERROR, new ThreadInfoReport(locationLocks.getLockInfo(location)));
}
if (name != null && !nameLocked) {
throw new BundleException("Failed to obtain symbolic name lock for installation: " + name, BundleException.STATECHANGE_ERROR, new ThreadInfoReport(nameLocks.getLockInfo(name)));
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new BundleException("Failed to obtain id locks for installation.", BundleException.STATECHANGE_ERROR, e);
}
Module existingLocation = null;
Collection<Module> collisionCandidates = Collections.emptyList();
moduleDatabase.readLock();
try {
existingLocation = moduleDatabase.getModule(location);
if (existingLocation == null) {
List<ModuleCapability> sameIdentity = moduleDatabase.findCapabilities(getIdentityRequirement(name, builder.getVersion()));
if (!sameIdentity.isEmpty()) {
collisionCandidates = new ArrayList<>(1);
for (ModuleCapability identity : sameIdentity) {
ModuleRevision equinoxRevision = identity.getRevision();
if (!equinoxRevision.isCurrent())
continue;
if (!collisionCandidates.contains(equinoxRevision.getRevisions().getModule()))
collisionCandidates.add(equinoxRevision.getRevisions().getModule());
}
}
}
} finally {
moduleDatabase.readUnlock();
}
if (existingLocation != null) {
if (origin != null) {
Bundle bundle = origin.getBundle();
BundleContext context = bundle == null ? null : bundle.getBundleContext();
if (context != null && context.getBundle(existingLocation.getId()) == null) {
Bundle b = existingLocation.getBundle();
throw new BundleException(NLS.bind(Msg.ModuleContainer_NameCollisionWithLocation, new Object[] {b.getSymbolicName(), b.getVersion(), location}), BundleException.REJECTED_BY_HOOK);
}
}
return existingLocation;
}
if (origin != null && !collisionCandidates.isEmpty()) {
adaptor.getModuleCollisionHook().filterCollisions(ModuleCollisionHook.INSTALLING, origin, collisionCandidates);
}
if (!collisionCandidates.isEmpty()) {
throw new BundleException(NLS.bind(Msg.ModuleContainer_NameCollision, name, builder.getVersion()), BundleException.DUPLICATE_BUNDLE_ERROR);
}
Module result = moduleDatabase.install(location, builder, revisionInfo);
adaptor.publishModuleEvent(ModuleEvent.INSTALLED, result, origin);
return result;
} finally {
if (locationLocked)
locationLocks.unlock(location);
if (nameLocked)
nameLocks.unlock(name);
}
}
public void update(Module module, ModuleRevisionBuilder builder, Object revisionInfo) throws BundleException {
long id = builder.getId();
ModuleRevisionBuilder adaptBuilder = getAdaptor().adaptModuleRevisionBuilder(ModuleEvent.UPDATED, module, builder, revisionInfo);
if (adaptBuilder != null) {
adaptBuilder.setInternalId(id);
builder = adaptBuilder;
}
checkAdminPermission(module.getBundle(), AdminPermission.LIFECYCLE);
String name = builder.getSymbolicName();
boolean nameLocked = false;
try {
try {
if (name != null && !(nameLocked = nameLocks.tryLock(name, 5, TimeUnit.SECONDS))) {
throw new BundleException("Failed to obtain id locks for installation.", BundleException.STATECHANGE_ERROR, new ThreadInfoReport(nameLocks.getLockInfo(name)));
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new BundleException("Failed to obtain id locks for installation.", BundleException.STATECHANGE_ERROR, e);
}
Collection<Module> collisionCandidates = Collections.emptyList();
moduleDatabase.readLock();
try {
List<ModuleCapability> sameIdentity = moduleDatabase.findCapabilities(getIdentityRequirement(name, builder.getVersion()));
if (!sameIdentity.isEmpty()) {
collisionCandidates = new ArrayList<>(1);
for (ModuleCapability identity : sameIdentity) {
ModuleRevision equinoxRevision = identity.getRevision();
if (!equinoxRevision.isCurrent())
continue;
Module m = equinoxRevision.getRevisions().getModule();
if (m.equals(module))
continue;
if (!collisionCandidates.contains(m))
collisionCandidates.add(m);
}
}
} finally {
moduleDatabase.readUnlock();
}
if (module != null && !collisionCandidates.isEmpty()) {
adaptor.getModuleCollisionHook().filterCollisions(ModuleCollisionHook.UPDATING, module, collisionCandidates);
}
if (!collisionCandidates.isEmpty()) {
throw new BundleException(NLS.bind(Msg.ModuleContainer_NameCollision, name, builder.getVersion()), BundleException.DUPLICATE_BUNDLE_ERROR);
}
module.lockStateChange(ModuleEvent.UPDATED);
State previousState;
try {
module.checkValid();
previousState = module.getState();
if (Module.ACTIVE_SET.contains(previousState)) {
module.stop(StopOptions.TRANSIENT);
}
if (Module.RESOLVED_SET.contains(previousState)) {
module.setState(State.INSTALLED);
adaptor.publishModuleEvent(ModuleEvent.UNRESOLVED, module, module);
}
moduleDatabase.update(module, builder, revisionInfo);
} finally {
module.unlockStateChange(ModuleEvent.UPDATED);
}
adaptor.publishModuleEvent(ModuleEvent.UPDATED, module, module);
if (Module.ACTIVE_SET.contains(previousState)) {
try {
module.start(StartOptions.TRANSIENT_RESUME);
} catch (BundleException e) {
getAdaptor().publishContainerEvent(ContainerEvent.ERROR, module, e);
}
}
} finally {
if (nameLocked)
nameLocks.unlock(name);
}
}
public void uninstall(Module module) throws BundleException {
checkAdminPermission(module.getBundle(), AdminPermission.LIFECYCLE);
module.lockStateChange(ModuleEvent.UNINSTALLED);
State previousState;
try {
module.checkValid();
previousState = module.getState();
if (Module.ACTIVE_SET.contains(module.getState())) {
try {
module.stop(StopOptions.TRANSIENT);
} catch (BundleException e) {
adaptor.publishContainerEvent(ContainerEvent.ERROR, module, e);
}
}
if (Module.RESOLVED_SET.contains(previousState)) {
module.setState(State.INSTALLED);
adaptor.publishModuleEvent(ModuleEvent.UNRESOLVED, module, module);
}
moduleDatabase.uninstall(module);
module.setState(State.UNINSTALLED);
} finally {
module.unlockStateChange(ModuleEvent.UNINSTALLED);
}
adaptor.publishModuleEvent(ModuleEvent.UNINSTALLED, module, module);
}
ModuleWiring getWiring(ModuleRevision revision) {
return moduleDatabase.getWiring(revision);
}
public FrameworkWiring getFrameworkWiring() {
return frameworkWiring;
}
public FrameworkStartLevel getFrameworkStartLevel() {
return frameworkStartLevel;
}
public ResolutionReport resolve(Collection<Module> triggers, boolean triggersMandatory) {
return resolve(triggers, triggersMandatory, false);
}
private ResolutionReport resolve(Collection<Module> triggers, boolean triggersMandatory, boolean restartTriggers) {
if (isRefreshingSystemModule()) {
return new ModuleResolutionReport(null, Collections.<Resource, List<Entry>> emptyMap(), new ResolutionException("Unable to resolve while shutting down the framework."));
}
ResolutionReport report = null;
try (ResolutionLock.Permits resolutionPermits = _resolutionLock.acquire(1)) {
do {
try {
report = resolveAndApply(triggers, triggersMandatory, restartTriggers, resolutionPermits);
} catch (RuntimeException e) {
if (e.getCause() instanceof BundleException) {
BundleException be = (BundleException) e.getCause();
if (be.getType() == BundleException.REJECTED_BY_HOOK || be.getType() == BundleException.STATECHANGE_ERROR) {
return new ModuleResolutionReport(null, Collections.<Resource, List<Entry>> emptyMap(), new ResolutionException(be));
}
}
throw e;
}
} while (report == null);
} catch (ResolutionLockException e) {
return new ModuleResolutionReport(null, Collections.<Resource, List<Entry>> emptyMap(), new ResolutionException("Timeout acquiring lock for resolution", e, Collections.<Requirement> emptyList()));
}
return report;
}
private ResolutionReport resolveAndApply(Collection<Module> triggers, boolean triggersMandatory, boolean restartTriggers, ResolutionLock.Permits resolutionPermits) {
if (triggers == null)
triggers = new ArrayList<>(0);
Collection<ModuleRevision> triggerRevisions = new ArrayList<>(triggers.size());
Collection<ModuleRevision> unresolved = new ArrayList<>();
Map<ModuleRevision, ModuleWiring> wiringClone;
long timestamp;
moduleDatabase.readLock();
try {
timestamp = moduleDatabase.getRevisionsTimestamp();
wiringClone = moduleDatabase.getWiringsClone();
for (Module module : triggers) {
if (!State.UNINSTALLED.equals(module.getState())) {
ModuleRevision current = module.getCurrentRevision();
if (current != null)
triggerRevisions.add(current);
}
}
Collection<Module> allModules = moduleDatabase.getModules();
for (Module module : allModules) {
ModuleRevision revision = module.getCurrentRevision();
if (revision != null && !wiringClone.containsKey(revision))
unresolved.add(revision);
}
} finally {
moduleDatabase.readUnlock();
}
ModuleResolutionReport report = moduleResolver.resolveDelta(triggerRevisions, triggersMandatory, unresolved, wiringClone, moduleDatabase);
Map<Resource, List<Wire>> resolutionResult = report.getResolutionResult();
Map<ModuleRevision, ModuleWiring> deltaWiring = resolutionResult == null ? Collections.<ModuleRevision, ModuleWiring> emptyMap() : moduleResolver.generateDelta(resolutionResult, wiringClone);
if (deltaWiring.isEmpty())
return report;
Collection<Module> modulesResolved = new ArrayList<>();
for (ModuleRevision deltaRevision : deltaWiring.keySet()) {
if (!wiringClone.containsKey(deltaRevision))
modulesResolved.add(deltaRevision.getRevisions().getModule());
}
return applyDelta(deltaWiring, modulesResolved, triggers, timestamp, restartTriggers, resolutionPermits) ? report : null;
}
public ModuleWire resolveDynamic(String dynamicPkgName, ModuleRevision revision) {
ModuleWire result;
Map<ModuleRevision, ModuleWiring> deltaWiring;
Collection<Module> modulesResolved;
long timestamp;
try (Permits resolutionPermits = _resolutionLock.acquire(ResolutionLock.MAX_RESOLUTION_PERMITS)) {
do {
result = null;
Map<ModuleRevision, ModuleWiring> wiringClone = null;
List<DynamicModuleRequirement> dynamicReqs = null;
Collection<ModuleRevision> unresolved = new ArrayList<>();
moduleDatabase.readLock();
try {
ModuleWiring wiring = revision.getWiring();
if (wiring == null) {
return null;
}
if (wiring.isDynamicPackageMiss(dynamicPkgName)) {
return null;
}
result = findExistingDynamicWire(revision.getWiring(), dynamicPkgName);
if (result != null) {
return result;
}
dynamicReqs = getDynamicRequirements(dynamicPkgName, revision);
if (dynamicReqs.isEmpty()) {
wiring.addDynamicPackageMiss(dynamicPkgName);
return null;
}
timestamp = moduleDatabase.getRevisionsTimestamp();
wiringClone = moduleDatabase.getWiringsClone();
Collection<Module> allModules = moduleDatabase.getModules();
for (Module module : allModules) {
ModuleRevision current = module.getCurrentRevision();
if (current != null && !wiringClone.containsKey(current))
unresolved.add(current);
}
} finally {
moduleDatabase.readUnlock();
}
deltaWiring = null;
boolean foundCandidates = false;
for (DynamicModuleRequirement dynamicReq : dynamicReqs) {
ModuleResolutionReport report = moduleResolver.resolveDynamicDelta(dynamicReq, unresolved, wiringClone, moduleDatabase);
Map<Resource, List<Wire>> resolutionResult = report.getResolutionResult();
deltaWiring = resolutionResult == null ? Collections.<ModuleRevision, ModuleWiring> emptyMap() : moduleResolver.generateDelta(resolutionResult, wiringClone);
if (deltaWiring.get(revision) != null) {
break;
}
List<Entry> revisionEntries = report.getEntries().get(revision);
if (revisionEntries == null || revisionEntries.isEmpty()) {
foundCandidates = true;
} else {
boolean isMissingCapability = false;
for (Entry entry : revisionEntries) {
isMissingCapability |= Entry.Type.MISSING_CAPABILITY.equals(entry.getType());
}
foundCandidates |= !isMissingCapability;
}
}
if (deltaWiring == null || deltaWiring.get(revision) == null) {
if (!foundCandidates) {
ModuleWiring wiring = revision.getWiring();
if (wiring != null) {
wiring.addDynamicPackageMiss(dynamicPkgName);
}
}
return null;
}
modulesResolved = new ArrayList<>();
for (ModuleRevision deltaRevision : deltaWiring.keySet()) {
if (!wiringClone.containsKey(deltaRevision))
modulesResolved.add(deltaRevision.getRevisions().getModule());
}
ModuleWiring wiring = deltaWiring.get(revision);
result = findExistingDynamicWire(wiring, dynamicPkgName);
} while (!applyDelta(deltaWiring, modulesResolved, Collections.<Module> emptyList(), timestamp, false, resolutionPermits));
} catch (ResolutionLockException e) {
return null;
}
return result;
}
private ModuleWire findExistingDynamicWire(ModuleWiring wiring, String dynamicPkgName) {
if (wiring == null) {
return null;
}
List<ModuleWire> wires = wiring.getRequiredModuleWires(PackageNamespace.PACKAGE_NAMESPACE);
for (int i = wires.size() - 1; i >= 0; i--) {
ModuleWire wire = wires.get(i);
if (dynamicPkgName.equals(wire.getCapability().getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE))) {
return wire;
}
if (!PackageNamespace.RESOLUTION_DYNAMIC.equals(wire.getRequirement().getDirectives().get(Namespace.REQUIREMENT_RESOLUTION_DIRECTIVE))) {
return null;
}
}
return null;
}
final ResolutionLock _resolutionLock = new ResolutionLock();
final ReentrantLock _bundleStateLock = new ReentrantLock();
static class ResolutionLockException extends Exception {
private static final long serialVersionUID = 1L;
public ResolutionLockException() {
super();
}
public ResolutionLockException(Throwable cause) {
super(cause);
}
}
static class ResolutionLock {
final static int MAX_RESOLUTION_PERMITS = 10;
final Semaphore permitPool = new Semaphore(MAX_RESOLUTION_PERMITS);
final ReentrantReadWriteLock reenterLock = new ReentrantReadWriteLock();
class Permits implements Closeable {
private final int aquiredPermits;
private final AtomicBoolean closed = new AtomicBoolean();
Permits(int requestedPermits) throws ResolutionLockException {
if (reenterLock.getReadHoldCount() > 0) {
requestedPermits = 0;
}
this.aquiredPermits = requestedPermits;
boolean previousInterruption = Thread.interrupted();
try {
if (!permitPool.tryAcquire(requestedPermits, 30, TimeUnit.SECONDS)) {
throw new ResolutionLockException();
}
} catch (InterruptedException e) {
throw new ResolutionLockException(e);
} finally {
if (previousInterruption) {
Thread.currentThread().interrupt();
}
}
reenterLock.readLock().lock();
}
@Override
public void close() {
if (closed.compareAndSet(false, true)) {
permitPool.release(aquiredPermits);
reenterLock.readLock().unlock();
}
}
}
Permits acquire(int requestedPermits) throws ResolutionLockException {
return new Permits(requestedPermits);
}
}
private boolean applyDelta(Map<ModuleRevision, ModuleWiring> deltaWiring, Collection<Module> modulesResolved, Collection<Module> triggers, long timestamp, boolean restartTriggers, ResolutionLock.Permits resolutionPermits) {
List<Module> modulesLocked = new ArrayList<>(modulesResolved.size());
try {
_bundleStateLock.lock();
try {
for (Module module : modulesResolved) {
try {
if (timestamp != moduleDatabase.getRevisionsTimestamp()) {
return false;
}
module.lockStateChange(ModuleEvent.RESOLVED);
modulesLocked.add(module);
} catch (BundleException e) {
if (timestamp != moduleDatabase.getRevisionsTimestamp()) {
return false;
}
throw new IllegalStateException(Msg.ModuleContainer_StateLockError, e);
}
}
} finally {
_bundleStateLock.unlock();
}
Map<ModuleWiring, Collection<ModuleRevision>> hostsWithDynamicFrags = new HashMap<>(0);
moduleDatabase.writeLock();
try {
if (timestamp != moduleDatabase.getRevisionsTimestamp())
return false;
Map<ModuleRevision, ModuleWiring> wiringCopy = moduleDatabase.getWiringsCopy();
for (Map.Entry<ModuleRevision, ModuleWiring> deltaEntry : deltaWiring.entrySet()) {
ModuleWiring current = wiringCopy.get(deltaEntry.getKey());
if (current != null) {
current.setCapabilities(deltaEntry.getValue().getModuleCapabilities(null));
current.setProvidedWires(deltaEntry.getValue().getProvidedModuleWires(null));
current.setRequiredWires(deltaEntry.getValue().getRequiredModuleWires(null));
deltaEntry.setValue(current);
} else {
ModuleRevision revision = deltaEntry.getValue().getRevision();
modulesResolved.add(revision.getRevisions().getModule());
if ((revision.getTypes() & BundleRevision.TYPE_FRAGMENT) != 0) {
for (ModuleWire hostWire : deltaEntry.getValue().getRequiredModuleWires(HostNamespace.HOST_NAMESPACE)) {
ModuleWiring hostWiring = hostWire.getProvider().getWiring();
if (hostWiring != null) {
Collection<ModuleRevision> dynamicFragments = hostsWithDynamicFrags.get(hostWiring);
if (dynamicFragments == null) {
dynamicFragments = new ArrayList<>();
hostsWithDynamicFrags.put(hostWiring, dynamicFragments);
}
dynamicFragments.add(hostWire.getRequirer());
}
}
}
}
}
moduleDatabase.mergeWiring(deltaWiring);
moduleDatabase.sortModules(modulesLocked, Sort.BY_DEPENDENCY, Sort.BY_START_LEVEL);
} finally {
moduleDatabase.writeUnlock();
}
for (Module module : modulesLocked) {
module.setState(State.RESOLVED);
}
for (Map.Entry<ModuleWiring, Collection<ModuleRevision>> dynamicFragments : hostsWithDynamicFrags.entrySet()) {
dynamicFragments.getKey().loadFragments(dynamicFragments.getValue());
}
} finally {
for (Module module : modulesLocked) {
module.unlockStateChange(ModuleEvent.RESOLVED);
}
}
resolutionPermits.close();
for (Module module : modulesLocked) {
adaptor.publishModuleEvent(ModuleEvent.RESOLVED, module, module);
}
Set<Module> triggerSet = restartTriggers ? new HashSet<>(triggers) : Collections.<Module> emptySet();
if (restartTriggers) {
for (Module module : triggers) {
if (module.getId() != 0 && Module.RESOLVED_SET.contains(module.getState())) {
start(module, StartOptions.TRANSIENT_RESUME);
}
}
}
if (autoStartOnResolve) {
for (Module module : modulesLocked) {
if (!module.inStart() && module.getId() != 0 && !triggerSet.contains(module)) {
start(module, StartOptions.TRANSIENT_IF_AUTO_START, StartOptions.TRANSIENT_RESUME);
}
}
}
return true;
}
private void start(Module module, StartOptions... options) {
try {
secureAction.start(module, options);
} catch (BundleException e) {
if (e.getType() == BundleException.STATECHANGE_ERROR) {
if (Module.ACTIVE_SET.contains(module.getState())) {
return;
}
}
adaptor.publishContainerEvent(ContainerEvent.ERROR, module, e);
} catch (IllegalStateException e) {
return;
}
}
private List<DynamicModuleRequirement> getDynamicRequirements(String dynamicPkgName, ModuleRevision revision) {
if ((revision.getTypes() & BundleRevision.TYPE_FRAGMENT) != 0) {
return Collections.emptyList();
}
ModuleWiring wiring = revision.getWiring();
if (wiring == null) {
return Collections.emptyList();
}
List<DynamicModuleRequirement> result = new ArrayList<>(1);
DynamicModuleRequirement dynamicRequirement;
for (ModuleRequirement requirement : wiring.getModuleRequirements(PackageNamespace.PACKAGE_NAMESPACE)) {
dynamicRequirement = requirement.getDynamicPackageRequirement(revision, dynamicPkgName);
if (dynamicRequirement != null) {
result.add(dynamicRequirement);
}
}
if (!result.isEmpty()) {
for (ModuleCapability capability : wiring.getModuleCapabilities(PackageNamespace.PACKAGE_NAMESPACE)) {
if (dynamicPkgName.equals(capability.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE))) {
return Collections.emptyList();
}
}
}
return result;
}
private Collection<Module> unresolve(Collection<Module> initial) {
Collection<Module> refreshTriggers = null;
while (refreshTriggers == null) {
refreshTriggers = unresolve0(initial);
}
return refreshTriggers;
}
private Collection<Module> unresolve0(Collection<Module> initial) {
Map<ModuleRevision, ModuleWiring> wiringCopy;
List<Module> refreshTriggers;
Collection<ModuleRevision> toRemoveRevisions;
Collection<ModuleWiring> toRemoveWirings;
Map<ModuleWiring, Collection<ModuleWire>> toRemoveWireLists;
long timestamp;
moduleDatabase.readLock();
try {
checkSystemExtensionRefresh(initial);
timestamp = moduleDatabase.getRevisionsTimestamp();
wiringCopy = moduleDatabase.getWiringsCopy();
refreshTriggers = new ArrayList<>(getRefreshClosure(initial, wiringCopy));
toRemoveRevisions = new ArrayList<>();
toRemoveWirings = new ArrayList<>();
toRemoveWireLists = new HashMap<>();
for (Iterator<Module> iTriggers = refreshTriggers.iterator(); iTriggers.hasNext();) {
Module module = iTriggers.next();
boolean first = true;
for (ModuleRevision revision : module.getRevisions().getModuleRevisions()) {
ModuleWiring removedWiring = wiringCopy.remove(revision);
if (removedWiring != null) {
toRemoveWirings.add(removedWiring);
List<ModuleWire> removedWires = removedWiring.getRequiredModuleWires(null);
for (ModuleWire wire : removedWires) {
Collection<ModuleWire> providerWires = toRemoveWireLists.get(wire.getProviderWiring());
if (providerWires == null) {
providerWires = new ArrayList<>();
toRemoveWireLists.put(wire.getProviderWiring(), providerWires);
}
providerWires.add(wire);
}
}
if (!first || revision.getRevisions().isUninstalled()) {
toRemoveRevisions.add(revision);
}
first = false;
}
if (module.getState().equals(State.UNINSTALLED)) {
iTriggers.remove();
}
}
moduleDatabase.sortModules(refreshTriggers, Sort.BY_START_LEVEL, Sort.BY_DEPENDENCY);
} finally {
moduleDatabase.readUnlock();
}
Module systemModule = moduleDatabase.getModule(0);
if (refreshTriggers.contains(systemModule) && Module.ACTIVE_SET.contains(systemModule.getState())) {
refreshSystemModule();
return Collections.emptyList();
}
Collection<Module> modulesLocked = new ArrayList<>(refreshTriggers.size());
Collection<Module> modulesUnresolved = new ArrayList<>();
try {
_bundleStateLock.lock();
try {
for (ListIterator<Module> iTriggers = refreshTriggers.listIterator(refreshTriggers.size()); iTriggers.hasPrevious();) {
Module refreshModule = iTriggers.previous();
refreshModule.lockStateChange(ModuleEvent.UNRESOLVED);
modulesLocked.add(refreshModule);
}
} catch (BundleException e) {
throw new IllegalStateException(Msg.ModuleContainer_StateLockError, e);
} finally {
_bundleStateLock.unlock();
}
for (ListIterator<Module> iTriggers = refreshTriggers.listIterator(refreshTriggers.size()); iTriggers.hasPrevious();) {
Module refreshModule = iTriggers.previous();
State previousState = refreshModule.getState();
if (Module.ACTIVE_SET.contains(previousState)) {
try {
refreshModule.stop(StopOptions.TRANSIENT);
} catch (BundleException e) {
adaptor.publishContainerEvent(ContainerEvent.ERROR, refreshModule, e);
}
} else {
iTriggers.remove();
}
}
for (Module module : modulesLocked) {
if (Module.ACTIVE_SET.contains(module.getState())) {
throw new IllegalStateException("Module is in the wrong state: " + module + ": " + module.getState());
}
}
moduleDatabase.writeLock();
try {
if (timestamp != moduleDatabase.getRevisionsTimestamp())
return null;
for (Map.Entry<ModuleWiring, Collection<ModuleWire>> entry : toRemoveWireLists.entrySet()) {
List<ModuleWire> provided = entry.getKey().getProvidedModuleWires(null);
provided.removeAll(entry.getValue());
entry.getKey().setProvidedWires(provided);
for (ModuleWire removedWire : entry.getValue()) {
removedWire.invalidate();
}
}
for (ModuleRevision removed : toRemoveRevisions) {
removed.getRevisions().removeRevision(removed);
moduleDatabase.removeCapabilities(removed);
}
for (ModuleWiring moduleWiring : toRemoveWirings) {
moduleWiring.invalidate();
}
moduleDatabase.setWiring(wiringCopy);
moduleDatabase.cleanupRemovalPending();
} finally {
moduleDatabase.writeUnlock();
}
for (Module module : modulesLocked) {
if (State.RESOLVED.equals(module.getState())) {
module.setState(State.INSTALLED);
modulesUnresolved.add(module);
}
}
} finally {
for (Module module : modulesLocked) {
module.unlockStateChange(ModuleEvent.UNRESOLVED);
}
}
for (Module module : modulesUnresolved) {
adaptor.publishModuleEvent(ModuleEvent.UNRESOLVED, module, module);
}
return refreshTriggers;
}
private void checkSystemExtensionRefresh(Collection<Module> initial) {
if (initial == null) {
return;
}
Long zero = new Long(0);
for (Iterator<Module> iModules = initial.iterator(); iModules.hasNext();) {
Module m = iModules.next();
if (m.getId().equals(zero)) {
if (Module.ACTIVE_SET.contains(m.getState())) {
iModules.remove();
}
} else {
if (Module.RESOLVED_SET.contains(m.getState())) {
ModuleRevision current = m.getCurrentRevision();
if ((current.getTypes() & BundleRevision.TYPE_FRAGMENT) != 0) {
ModuleWiring wiring = current.getWiring();
if (wiring != null) {
List<ModuleWire> hostWires = wiring.getRequiredModuleWires(HostNamespace.HOST_NAMESPACE);
for (ModuleWire hostWire : hostWires) {
if (hostWire.getProvider().getRevisions().getModule().getId().equals(zero)) {
iModules.remove();
}
}
}
}
}
}
}
}
public ResolutionReport refresh(Collection<Module> initial) {
initial = initial == null ? null : new ArrayList<>(initial);
Collection<Module> refreshTriggers = unresolve(initial);
if (!isRefreshingSystemModule()) {
return resolve(refreshTriggers, false, true);
}
return new ModuleResolutionReport(null, null, null);
}
public Collection<Module> getDependencyClosure(Collection<Module> initial) {
moduleDatabase.readLock();
try {
return getRefreshClosure(initial, moduleDatabase.getWiringsCopy());
} finally {
moduleDatabase.readUnlock();
}
}
public Collection<ModuleRevision> getRemovalPending() {
return moduleDatabase.getRemovalPending();
}
public int getStartLevel() {
return frameworkStartLevel.getStartLevel();
}
void setStartLevel(Module module, int startlevel) {
frameworkStartLevel.setStartLevel(module, startlevel);
}
long getModuleLockTimeout() {
return this.moduleLockTimeout;
}
void open() {
loadModules();
frameworkStartLevel.open();
frameworkWiring.open();
refreshingSystemModule.set(null);
}
void close() {
frameworkStartLevel.close();
frameworkWiring.close();
unloadModules();
}
private void loadModules() {
List<Module> modules = null;
moduleDatabase.readLock();
try {
modules = getModules();
for (Module module : modules) {
try {
module.lockStateChange(ModuleEvent.RESOLVED);
ModuleWiring wiring = moduleDatabase.getWiring(module.getCurrentRevision());
if (wiring != null) {
module.setState(State.RESOLVED);
} else {
module.setState(State.INSTALLED);
}
} catch (BundleException e) {
throw new IllegalStateException("Unable to lock module state.", e);
}
}
Map<ModuleRevision, ModuleWiring> wirings = moduleDatabase.getWiringsCopy();
for (ModuleWiring wiring : wirings.values()) {
wiring.validate();
}
} finally {
if (modules != null) {
for (Module module : modules) {
try {
module.unlockStateChange(ModuleEvent.RESOLVED);
} catch (IllegalMonitorStateException e) {
}
}
}
moduleDatabase.readUnlock();
}
}
private void unloadModules() {
List<Module> modules = null;
moduleDatabase.readLock();
try {
modules = getModules();
for (Module module : modules) {
if (module.getId() != 0) {
try {
module.lockStateChange(ModuleEvent.UNINSTALLED);
} catch (BundleException e) {
throw new IllegalStateException("Unable to lock module state.", e);
}
module.setState(State.UNINSTALLED);
}
}
Map<ModuleRevision, ModuleWiring> wirings = moduleDatabase.getWiringsCopy();
for (ModuleWiring wiring : wirings.values()) {
wiring.unload();
}
} finally {
if (modules != null) {
for (Module module : modules) {
if (module.getId() != 0) {
try {
module.unlockStateChange(ModuleEvent.UNINSTALLED);
} catch (IllegalMonitorStateException e) {
}
}
}
}
moduleDatabase.readUnlock();
}
}
public void setInitialModuleStates() throws BundleException {
moduleDatabase.readLock();
try {
List<Module> modules = getModules();
for (Module module : modules) {
if (module.getId() == 0) {
module.lockStateChange(ModuleEvent.UNINSTALLED);
try {
module.setState(State.INSTALLED);
} finally {
module.unlockStateChange(ModuleEvent.UNINSTALLED);
}
} else {
module.lockStateChange(ModuleEvent.UNINSTALLED);
try {
module.setState(State.UNINSTALLED);
} finally {
module.unlockStateChange(ModuleEvent.UNINSTALLED);
}
}
}
Map<ModuleRevision, ModuleWiring> wirings = moduleDatabase.getWiringsCopy();
for (ModuleWiring wiring : wirings.values()) {
wiring.unload();
}
} finally {
moduleDatabase.readUnlock();
}
}
Set<Module> getRefreshClosure(Collection<Module> initial, Map<ModuleRevision, ModuleWiring> wiringCopy) {
Set<Module> refreshClosure = new HashSet<>();
if (initial == null) {
initial = new HashSet<>();
Collection<ModuleRevision> removalPending = moduleDatabase.getRemovalPending();
for (ModuleRevision revision : removalPending) {
initial.add(revision.getRevisions().getModule());
}
}
for (Module module : initial)
addDependents(module, wiringCopy, refreshClosure);
return refreshClosure;
}
private static void addDependents(Module module, Map<ModuleRevision, ModuleWiring> wiringCopy, Set<Module> refreshClosure) {
if (refreshClosure.contains(module))
return;
refreshClosure.add(module);
List<ModuleRevision> revisions = module.getRevisions().getModuleRevisions();
for (ModuleRevision revision : revisions) {
ModuleWiring wiring = wiringCopy.get(revision);
if (wiring == null)
continue;
List<ModuleWire> provided = wiring.getProvidedModuleWires(null);
for (ModuleWire providedWire : provided) {
addDependents(providedWire.getRequirer().getRevisions().getModule(), wiringCopy, refreshClosure);
}
if (revision.getTypes() == BundleRevision.TYPE_FRAGMENT) {
List<ModuleWire> hosts = wiring.getRequiredModuleWires(HostNamespace.HOST_NAMESPACE);
for (ModuleWire hostWire : hosts) {
addDependents(hostWire.getProvider().getRevisions().getModule(), wiringCopy, refreshClosure);
}
}
}
}
static Collection<ModuleRevision> getDependencyClosure(ModuleRevision initial, Map<ModuleRevision, ModuleWiring> wiringCopy) {
Set<ModuleRevision> dependencyClosure = new HashSet<>();
addDependents(initial, wiringCopy, dependencyClosure);
return dependencyClosure;
}
private static void addDependents(ModuleRevision revision, Map<ModuleRevision, ModuleWiring> wiringCopy, Set<ModuleRevision> dependencyClosure) {
if (dependencyClosure.contains(revision))
return;
dependencyClosure.add(revision);
ModuleWiring wiring = wiringCopy.get(revision);
if (wiring == null)
return;
List<ModuleWire> provided = wiring.getProvidedModuleWires(null);
for (ModuleWire providedWire : provided) {
addDependents(providedWire.getRequirer(), wiringCopy, dependencyClosure);
}
if (revision.getTypes() == BundleRevision.TYPE_FRAGMENT) {
List<ModuleWire> hosts = wiring.getRequiredModuleWires(HostNamespace.HOST_NAMESPACE);
for (ModuleWire hostWire : hosts) {
addDependents(hostWire.getProvider(), wiringCopy, dependencyClosure);
}
}
}
Bundle getSystemBundle() {
Module systemModule = moduleDatabase.getModule(0);
return systemModule == null ? null : systemModule.getBundle();
}
void checkAdminPermission(Bundle bundle, String action) {
if (bundle == null)
return;
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkPermission(new AdminPermission(bundle, action));
}
void refreshSystemModule() {
final SystemModule systemModule = (SystemModule) moduleDatabase.getModule(0);
if (systemModule == refreshingSystemModule.getAndSet(systemModule)) {
return;
}
getAdaptor().refreshedSystemModule();
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
systemModule.lockStateChange(ModuleEvent.UNRESOLVED);
try {
systemModule.stop();
} finally {
systemModule.unlockStateChange(ModuleEvent.UNRESOLVED);
}
} catch (BundleException e) {
e.printStackTrace();
}
}
});
t.start();
}
boolean isRefreshingSystemModule() {
return refreshingSystemModule.get() != null;
}
static Requirement getIdentityRequirement(String name, Version version) {
version = version == null ? Version.emptyVersion : version;
String filter = "(&(" + IdentityNamespace.IDENTITY_NAMESPACE + "=" + name + ")(" + IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE + "=" + version.toString() + "))";
Map<String, String> directives = Collections.<String, String> singletonMap(Namespace.REQUIREMENT_FILTER_DIRECTIVE, filter);
return new ModuleRequirement(IdentityNamespace.IDENTITY_NAMESPACE, directives, Collections.<String, Object> emptyMap(), null);
}
class ContainerWiring implements FrameworkWiring, EventDispatcher<ContainerWiring, FrameworkListener[], Collection<Module>> {
private final Object monitor = new Object();
private EventManager refreshThread = null;
@Override
public Bundle getBundle() {
return getSystemBundle();
}
@Override
public void refreshBundles(Collection<Bundle> bundles, FrameworkListener... listeners) {
checkAdminPermission(getBundle(), AdminPermission.RESOLVE);
Collection<Module> modules = getModules(bundles);
CopyOnWriteIdentityMap<ContainerWiring, FrameworkListener[]> dispatchListeners = new CopyOnWriteIdentityMap<>();
dispatchListeners.put(this, listeners);
ListenerQueue<ContainerWiring, FrameworkListener[], Collection<Module>> queue = new ListenerQueue<>(getManager());
queue.queueListeners(dispatchListeners.entrySet(), this);
queue.dispatchEventAsynchronous(0, modules);
}
@Override
public boolean resolveBundles(Collection<Bundle> bundles) {
checkAdminPermission(getBundle(), AdminPermission.RESOLVE);
Collection<Module> modules = getModules(bundles);
resolve(modules, false);
if (modules == null) {
modules = ModuleContainer.this.getModules();
}
for (Module module : modules) {
if (getWiring(module.getCurrentRevision()) == null)
return false;
}
return true;
}
@Override
public Collection<Bundle> getRemovalPendingBundles() {
moduleDatabase.readLock();
try {
Collection<Bundle> removalPendingBundles = new HashSet<>();
Collection<ModuleRevision> removalPending = moduleDatabase.getRemovalPending();
for (ModuleRevision moduleRevision : removalPending) {
removalPendingBundles.add(moduleRevision.getBundle());
}
return removalPendingBundles;
} finally {
moduleDatabase.readUnlock();
}
}
@Override
public Collection<Bundle> getDependencyClosure(Collection<Bundle> bundles) {
Collection<Module> modules = getModules(bundles);
moduleDatabase.readLock();
try {
Collection<Module> closure = getRefreshClosure(modules, moduleDatabase.getWiringsCopy());
Collection<Bundle> result = new ArrayList<>(closure.size());
for (Module module : closure) {
result.add(module.getBundle());
}
return result;
} finally {
moduleDatabase.readUnlock();
}
}
@Override
public Collection<BundleCapability> findProviders(Requirement requirement) {
return InternalUtils.asListBundleCapability(moduleDatabase.findCapabilities(requirement));
}
private Collection<Module> getModules(final Collection<Bundle> bundles) {
if (bundles == null)
return null;
return AccessController.doPrivileged(new PrivilegedAction<Collection<Module>>() {
@Override
public Collection<Module> run() {
Collection<Module> result = new ArrayList<>(bundles.size());
for (Bundle bundle : bundles) {
Module module = bundle.adapt(Module.class);
if (module == null)
throw new IllegalStateException("Could not adapt a bundle to a module.");
result.add(module);
}
return result;
}
});
}
@Override
public void dispatchEvent(ContainerWiring eventListener, FrameworkListener[] frameworkListeners, int eventAction, Collection<Module> eventObject) {
try {
refresh(eventObject);
} finally {
adaptor.publishContainerEvent(ContainerEvent.REFRESH, moduleDatabase.getModule(0), null, frameworkListeners);
}
}
private EventManager getManager() {
synchronized (monitor) {
if (refreshThread == null) {
refreshThread = new EventManager("Refresh Thread: " + adaptor.toString());
}
return refreshThread;
}
}
void close() {
synchronized (monitor) {
EventManager manager = getManager();
manager.close();
}
}
void open() {
synchronized (monitor) {
if (refreshThread != null) {
refreshThread.close();
refreshThread = null;
}
}
}
}
@Override
public void optionsChanged(DebugOptions options) {
moduleResolver.setDebugOptions();
frameworkStartLevel.setDebugOptions();
if (options != null) {
this.DEBUG_MONITOR_LAZY = options.getBooleanOption(Debug.OPTION_MONITOR_LAZY, false);
this.DEBUG_BUNDLE_START_TIME = options.getBooleanOption(Debug.OPTION_DEBUG_BUNDLE_START_TIME, false);
}
}
class ContainerStartLevel implements FrameworkStartLevel, EventDispatcher<Module, FrameworkListener[], Integer> {
static final int USE_BEGINNING_START_LEVEL = Integer.MIN_VALUE;
private static final int FRAMEWORK_STARTLEVEL = 1;
private static final int MODULE_STARTLEVEL = 2;
private final AtomicInteger activeStartLevel = new AtomicInteger(0);
private final Object eventManagerLock = new Object();
private EventManager startLevelThread = null;
private final Object frameworkStartLevelLock = new Object();
boolean debugStartLevel = false;
{
setDebugOptions();
}
void setDebugOptions() {
DebugOptions options = getAdaptor().getDebugOptions();
debugStartLevel = options == null ? false : options.getBooleanOption(Debug.OPTION_DEBUG_STARTLEVEL, false);
}
@Override
public Bundle getBundle() {
return getSystemBundle();
}
@Override
public int getStartLevel() {
return activeStartLevel.get();
}
void setStartLevel(Module module, int startlevel) {
checkAdminPermission(module.getBundle(), AdminPermission.EXECUTE);
if (module.getId() == 0) {
throw new IllegalArgumentException(Msg.ModuleContainer_SystemStartLevelError);
}
if (startlevel < 1) {
throw new IllegalArgumentException(Msg.ModuleContainer_NegativeStartLevelError + startlevel);
}
int currentLevel = module.getStartLevel();
if (currentLevel == startlevel) {
return;
}
moduleDatabase.setStartLevel(module, startlevel);
if (currentLevel < startlevel || module.isPersistentlyStarted()) {
CopyOnWriteIdentityMap<Module, FrameworkListener[]> dispatchListeners = new CopyOnWriteIdentityMap<>();
dispatchListeners.put(module, new FrameworkListener[0]);
ListenerQueue<Module, FrameworkListener[], Integer> queue = new ListenerQueue<>(getManager());
queue.queueListeners(dispatchListeners.entrySet(), this);
queue.dispatchEventAsynchronous(MODULE_STARTLEVEL, startlevel);
}
}
@Override
public void setStartLevel(int startlevel, FrameworkListener... listeners) {
checkAdminPermission(getBundle(), AdminPermission.STARTLEVEL);
if (startlevel < 1) {
throw new IllegalArgumentException(Msg.ModuleContainer_NegativeStartLevelError + startlevel);
}
if (activeStartLevel.get() == 0) {
throw new IllegalStateException(Msg.ModuleContainer_SystemNotActiveError);
}
if (debugStartLevel) {
Debug.println("StartLevel: setStartLevel: " + startlevel);
}
CopyOnWriteIdentityMap<Module, FrameworkListener[]> dispatchListeners = new CopyOnWriteIdentityMap<>();
dispatchListeners.put(moduleDatabase.getModule(0), listeners);
ListenerQueue<Module, FrameworkListener[], Integer> queue = new ListenerQueue<>(getManager());
queue.queueListeners(dispatchListeners.entrySet(), this);
queue.dispatchEventAsynchronous(FRAMEWORK_STARTLEVEL, startlevel);
}
@Override
public int getInitialBundleStartLevel() {
return moduleDatabase.getInitialModuleStartLevel();
}
@Override
public void setInitialBundleStartLevel(int startlevel) {
checkAdminPermission(getBundle(), AdminPermission.STARTLEVEL);
if (startlevel < 1) {
throw new IllegalArgumentException(Msg.ModuleContainer_NegativeStartLevelError + startlevel);
}
moduleDatabase.setInitialModuleStartLevel(startlevel);
}
@Override
public void dispatchEvent(Module module, FrameworkListener[] listeners, int eventAction, Integer startlevel) {
switch (eventAction) {
case FRAMEWORK_STARTLEVEL :
doContainerStartLevel(module, startlevel, listeners);
break;
case MODULE_STARTLEVEL :
if (debugStartLevel) {
Debug.println("StartLevel: changing bundle startlevel; " + toString(module) + "; newSL=" + startlevel + "; activeSL=" + getStartLevel());
}
try {
if (getStartLevel() < startlevel) {
if (Module.ACTIVE_SET.contains(module.getState())) {
if (debugStartLevel) {
Debug.println("StartLevel: stopping bundle; " + toString(module) + "; with startLevel=" + startlevel);
}
module.stop(StopOptions.TRANSIENT);
}
} else {
if (debugStartLevel) {
Debug.println("StartLevel: resuming bundle; " + toString(module) + "; with startLevel=" + startlevel);
}
module.start(StartOptions.TRANSIENT_IF_AUTO_START, StartOptions.TRANSIENT_RESUME);
}
} catch (BundleException e) {
adaptor.publishContainerEvent(ContainerEvent.ERROR, module, e);
}
break;
default :
break;
}
}
void doContainerStartLevel(Module module, int newStartLevel, FrameworkListener... listeners) {
synchronized (frameworkStartLevelLock) {
if (newStartLevel == USE_BEGINNING_START_LEVEL) {
String beginningSL = adaptor.getProperty(Constants.FRAMEWORK_BEGINNING_STARTLEVEL);
newStartLevel = beginningSL == null ? 1 : Integer.parseInt(beginningSL);
}
try {
int currentSL = getStartLevel();
if (currentSL == 0) {
Module systemModule = moduleDatabase.getModule(0);
if (systemModule != null && !State.STARTING.equals(systemModule.getState())) {
return;
}
}
List<Module> sorted = null;
long currentTimestamp = Long.MIN_VALUE;
if (newStartLevel > currentSL) {
List<Module> lazyStart = null;
List<Module> lazyStartParallel = null;
List<Module> eagerStart = null;
List<Module> eagerStartParallel = null;
for (int i = currentSL; i < newStartLevel; i++) {
int toStartLevel = i + 1;
activeStartLevel.set(toStartLevel);
if (debugStartLevel) {
Debug.println("StartLevel: incremented active start level to; " + toStartLevel);
}
if (sorted == null || currentTimestamp != moduleDatabase.getTimestamp()) {
moduleDatabase.readLock();
try {
sorted = moduleDatabase.getSortedModules(Sort.BY_START_LEVEL);
lazyStart = new ArrayList<>(sorted.size());
lazyStartParallel = new ArrayList<>(sorted.size());
eagerStart = new ArrayList<>(sorted.size());
eagerStartParallel = new ArrayList<>(sorted.size());
separateModulesByActivationPolicy(sorted, lazyStart, lazyStartParallel, eagerStart, eagerStartParallel);
currentTimestamp = moduleDatabase.getTimestamp();
} finally {
moduleDatabase.readUnlock();
}
}
incStartLevel(toStartLevel, lazyStart, lazyStartParallel, eagerStart, eagerStartParallel);
}
} else {
for (int i = currentSL; i > newStartLevel; i--) {
int toStartLevel = i - 1;
activeStartLevel.set(toStartLevel);
if (debugStartLevel) {
Debug.println("StartLevel: decremented active start level to " + toStartLevel);
}
if (sorted == null || currentTimestamp != moduleDatabase.getTimestamp()) {
moduleDatabase.readLock();
try {
sorted = moduleDatabase.getSortedModules(Sort.BY_START_LEVEL, Sort.BY_DEPENDENCY);
currentTimestamp = moduleDatabase.getTimestamp();
} finally {
moduleDatabase.readUnlock();
}
}
decStartLevel(toStartLevel, sorted);
}
}
if (currentSL > 0 && newStartLevel > 0) {
adaptor.publishContainerEvent(ContainerEvent.START_LEVEL, module, null, listeners);
}
} catch (Error | RuntimeException e) {
adaptor.publishContainerEvent(ContainerEvent.ERROR, module, e, listeners);
throw e;
}
}
}
private void incStartLevel(int toStartLevel, List<Module> lazyStart, List<Module> lazyStartParallel, List<Module> eagerStart, List<Module> eagerStartParallel) {
incStartLevel(toStartLevel, lazyStartParallel, true);
incStartLevel(toStartLevel, lazyStart, false);
incStartLevel(toStartLevel, eagerStartParallel, true);
incStartLevel(toStartLevel, eagerStart, false);
}
private void separateModulesByActivationPolicy(List<Module> sortedModules, List<Module> lazyStart, List<Module> lazyStartParallel, List<Module> eagerStart, List<Module> eagerStartParallel) {
for (Module module : sortedModules) {
if (!restrictParallelStart || module.isParallelActivated()) {
if (module.isLazyActivate()) {
lazyStartParallel.add(module);
} else {
eagerStartParallel.add(module);
}
} else {
if (module.isLazyActivate()) {
lazyStart.add(module);
} else {
eagerStart.add(module);
}
}
}
}
private void incStartLevel(final int toStartLevel, List<Module> candidatesToStart, boolean inParallel) {
if (candidatesToStart.isEmpty()) {
return;
}
final List<Module> toStart = new ArrayList<>();
for (final Module module : candidatesToStart) {
if (isRefreshingSystemModule()) {
return;
}
try {
int moduleStartLevel = module.getStartLevel();
if (moduleStartLevel < toStartLevel) {
continue;
} else if (moduleStartLevel == toStartLevel) {
toStart.add(module);
} else {
break;
}
} catch (IllegalStateException e) {
continue;
}
}
if (toStart.isEmpty()) {
return;
}
final Executor executor = inParallel ? adaptor.getStartLevelExecutor() : new Executor() {
@Override
public void execute(Runnable command) {
command.run();
}
};
final CountDownLatch done = new CountDownLatch(toStart.size());
for (final Module module : toStart) {
executor.execute(new Runnable() {
@Override
public void run() {
try {
if (debugStartLevel) {
Debug.println("StartLevel: resuming bundle; " + ContainerStartLevel.this.toString(module) + "; with startLevel=" + toStartLevel);
}
module.start(StartOptions.TRANSIENT_IF_AUTO_START, StartOptions.TRANSIENT_RESUME);
} catch (BundleException e) {
adaptor.publishContainerEvent(ContainerEvent.ERROR, module, e);
} catch (IllegalStateException e) {
} finally {
done.countDown();
}
}
});
}
try {
done.await();
} catch (InterruptedException e) {
adaptor.publishContainerEvent(ContainerEvent.ERROR, moduleDatabase.getModule(0), e);
}
}
private void decStartLevel(int toStartLevel, List<Module> sortedModules) {
ListIterator<Module> iModules = sortedModules.listIterator(sortedModules.size());
while (iModules.hasPrevious()) {
Module module = iModules.previous();
try {
int moduleStartLevel = module.getStartLevel();
if (moduleStartLevel > toStartLevel + 1) {
continue;
} else if (moduleStartLevel <= toStartLevel) {
break;
}
try {
if (Module.ACTIVE_SET.contains(module.getState())) {
if (debugStartLevel) {
Debug.println("StartLevel: stopping bundle; " + toString(module) + "; with startLevel=" + moduleStartLevel);
}
module.stop(StopOptions.TRANSIENT);
}
} catch (BundleException e) {
adaptor.publishContainerEvent(ContainerEvent.ERROR, module, e);
}
} catch (IllegalStateException e) {
continue;
}
}
}
private EventManager getManager() {
synchronized (eventManagerLock) {
if (startLevelThread == null) {
startLevelThread = new EventManager("Start Level: " + adaptor.toString());
}
return startLevelThread;
}
}
void close() {
synchronized (eventManagerLock) {
EventManager manager = getManager();
manager.close();
}
}
void open() {
synchronized (eventManagerLock) {
if (startLevelThread != null) {
startLevelThread.close();
startLevelThread = null;
}
}
}
String toString(Module m) {
Bundle b = m.getBundle();
return b != null ? b.toString() : m.toString();
}
}
}