blob: c6e1d8a5817447aeab0454a6ceb66ad07aa159c6 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013, 2016 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.compatibility.state;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import org.eclipse.osgi.container.*;
import org.eclipse.osgi.internal.resolver.BaseDescriptionImpl.BaseCapability;
import org.eclipse.osgi.service.resolver.BundleDescription;
import org.eclipse.osgi.service.resolver.State;
import org.osgi.framework.*;
import org.osgi.framework.hooks.resolver.ResolverHook;
import org.osgi.framework.hooks.resolver.ResolverHookFactory;
import org.osgi.framework.namespace.ExecutionEnvironmentNamespace;
import org.osgi.framework.namespace.HostNamespace;
import org.osgi.framework.wiring.*;
class PlatformBundleListener implements SynchronousBundleListener, FrameworkListener, ResolverHookFactory {
private final State systemState;
private final StateConverter converter;
private final ModuleDatabase database;
private final ModuleContainer container;
private long lastResolveStamp = -1;
private AtomicBoolean gotUnresolved = new AtomicBoolean(false);
PlatformBundleListener(State systemState, StateConverter converter, ModuleDatabase database, ModuleContainer container) {
this.systemState = systemState;
this.converter = converter;
this.database = database;
this.container = container;
}
@Override
public void bundleChanged(BundleEvent event) {
switch (event.getType()) {
case BundleEvent.INSTALLED : {
BundleRevision revision = event.getBundle().adapt(BundleRevision.class);
if (revision != null) {
systemState.addBundle(converter.createDescription(revision));
systemState.setTimeStamp(database.getRevisionsTimestamp());
}
break;
}
case BundleEvent.UNINSTALLED : {
systemState.removeBundle(event.getBundle().getBundleId());
systemState.setTimeStamp(database.getRevisionsTimestamp());
break;
}
case BundleEvent.UPDATED : {
BundleRevision revision = event.getBundle().adapt(BundleRevision.class);
if (revision != null) {
systemState.updateBundle(converter.createDescription(revision));
systemState.setTimeStamp(database.getRevisionsTimestamp());
}
break;
}
case BundleEvent.UNRESOLVED : {
gotUnresolved.set(true);
break;
}
case BundleEvent.RESOLVED : {
resolve(gotUnresolved.getAndSet(false));
break;
}
default :
// do nothing
break;
}
}
private void resolve(boolean uninstalled) {
database.readLock();
try {
if (lastResolveStamp != database.getRevisionsTimestamp()) {
Collection<ModuleRevision> containerRemovalPending = container.getRemovalPending();
BundleDescription[] stateRemovalPendingDescs = systemState.getRemovalPending();
Collection<BundleDescription> stateRemovalPending = new ArrayList<>(stateRemovalPendingDescs.length);
for (BundleDescription description : stateRemovalPendingDescs) {
if (!containerRemovalPending.contains(description.getUserObject())) {
stateRemovalPending.add(description);
}
}
if (!stateRemovalPending.isEmpty()) {
systemState.resolve(stateRemovalPending.toArray(new BundleDescription[stateRemovalPending.size()]), true);
} else {
systemState.resolve(!uninstalled);
}
lastResolveStamp = database.getRevisionsTimestamp();
systemState.setTimeStamp(database.getRevisionsTimestamp());
}
} finally {
database.readUnlock();
}
}
@Override
public void frameworkEvent(FrameworkEvent event) {
if (event.getType() == FrameworkEvent.PACKAGES_REFRESHED) {
resolve(gotUnresolved.getAndSet(false));
}
}
@Override
public ResolverHook begin(Collection<BundleRevision> triggers) {
return new ResolverHook() {
@Override
public void filterSingletonCollisions(BundleCapability singleton, Collection<BundleCapability> collisionCandidates) {
BundleDescription desc = (BundleDescription) singleton.getRevision();
ModuleRevision revision = (ModuleRevision) desc.getUserObject();
if (revision.getWiring() != null) {
collisionCandidates.clear();
} else {
for (Iterator<BundleCapability> iCandidates = collisionCandidates.iterator(); iCandidates.hasNext();) {
BundleDescription candDesc = (BundleDescription) iCandidates.next().getRevision();
ModuleRevision candRevision = (ModuleRevision) candDesc.getUserObject();
if (candRevision.getWiring() == null) {
iCandidates.remove();
}
}
}
}
@Override
public void filterResolvable(Collection<BundleRevision> candidates) {
for (Iterator<BundleRevision> iCandidates = candidates.iterator(); iCandidates.hasNext();) {
BundleDescription candDesc = (BundleDescription) iCandidates.next();
ModuleRevision candRevision = (ModuleRevision) candDesc.getUserObject();
if (candRevision.getWiring() == null) {
iCandidates.remove();
}
}
}
@Override
public void filterMatches(BundleRequirement requirement, Collection<BundleCapability> candidates) {
String namespace = requirement.getNamespace();
BundleDescription reqDesc = (BundleDescription) requirement.getRevision();
ModuleRevision reqRevision = (ModuleRevision) reqDesc.getUserObject();
ModuleWiring reqWiring = reqRevision.getWiring();
if (reqWiring == null) {
candidates.clear();
return;
}
Collection<ModuleWiring> wirings = new ArrayList<>(1);
if ((reqRevision.getTypes() & BundleRevision.TYPE_FRAGMENT) != 0) {
if (ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE.equals(namespace) || HostNamespace.HOST_NAMESPACE.equals(namespace)) {
wirings.add(reqWiring);
} else {
List<ModuleWire> hostWires = reqWiring.getRequiredModuleWires(namespace);
for (ModuleWire hostWire : hostWires) {
ModuleWiring hostWiring = hostWire.getProviderWiring();
if (hostWiring != null) {
wirings.add(hostWiring);
}
}
}
} else {
wirings.add(reqWiring);
}
for (Iterator<BundleCapability> iCandidates = candidates.iterator(); iCandidates.hasNext();) {
BaseCapability baseCapability = (BaseCapability) iCandidates.next();
Object userObject = baseCapability.getBaseDescription().getUserObject();
boolean foundCandidate = false;
wirings: for (ModuleWiring wiring : wirings) {
List<ModuleWire> wires = wiring.getRequiredModuleWires(namespace);
for (ModuleWire wire : wires) {
if (userObject instanceof ModuleRevision && userObject.equals(wire.getProvider())) {
foundCandidate = true;
} else if (userObject instanceof ModuleCapability && userObject.equals(wire.getCapability())) {
foundCandidate = true;
}
if (foundCandidate)
break wirings;
}
}
if (!foundCandidate) {
iCandidates.remove();
}
}
}
@Override
public void end() {
// nothing
}
};
}
}