package org.apache.felix.resolver;
import java.security.*;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.felix.resolver.reason.ReasonException;
import org.apache.felix.resolver.util.ArrayMap;
import org.apache.felix.resolver.util.CandidateSelector;
import org.apache.felix.resolver.util.OpenHashMap;
import org.osgi.framework.namespace.*;
import org.osgi.resource.*;
import org.osgi.service.resolver.*;
public class ResolverImpl implements Resolver
{
private final AccessControlContext m_acc =
System.getSecurityManager() != null ?
AccessController.getContext() :
null;
private final Logger m_logger;
private final int m_parallelism;
private final Executor m_executor;
enum PermutationType {
USES,
IMPORT,
SUBSTITUTE
}
static class ResolveSession implements Runnable
{
private final ResolveContext m_resolveContext;
private final Collection<Resource> m_mandatoryResources;
private final Collection<Resource> m_optionalResources;
private final Resource m_dynamicHost;
private final Requirement m_dynamicReq;
private final List<Capability> m_dynamicCandidates;
private Map<Resource, Boolean> m_validRelatedResources = new HashMap<Resource, Boolean>(0);
private Map<Resource, Collection<Resource>> m_relatedResources = new HashMap<Resource, Collection<Resource>>(0);
private final List<Candidates> m_usesPermutations = new LinkedList<Candidates>();
private int m_usesIndex = 0;
private final List<Candidates> m_importPermutations = new LinkedList<Candidates>();
private int m_importIndex = 0;
private final List<Candidates> m_substPermutations = new LinkedList<Candidates>();
private int m_substituteIndex = 0;
private Candidates m_multipleCardCandidates = null;
private final Set<Object> m_processedDeltas = new HashSet<Object>();
private final Executor m_executor;
private final Set<Requirement> m_mutated = new HashSet<Requirement>();
private final Set<Requirement> m_sub_mutated = new HashSet<Requirement>();
private final ConcurrentMap<String, List<String>> m_usesCache = new ConcurrentHashMap<String, List<String>>();
private ResolutionError m_currentError;
volatile private CancellationException m_isCancelled = null;
static ResolveSession createSession(ResolveContext resolveContext, Executor executor, Resource dynamicHost, Requirement dynamicReq, List<Capability> dynamicCandidates)
{
ResolveSession session = new ResolveSession(resolveContext, executor, dynamicHost, dynamicReq, dynamicCandidates);
session.getContext().onCancel(session);
session.initMandatoryAndOptionalResources();
return session;
}
private ResolveSession(ResolveContext resolveContext, Executor executor, Resource dynamicHost, Requirement dynamicReq, List<Capability> dynamicCandidates)
{
m_resolveContext = resolveContext;
m_executor = executor;
m_dynamicHost = dynamicHost;
m_dynamicReq = dynamicReq;
m_dynamicCandidates = dynamicCandidates;
if (m_dynamicHost != null) {
m_mandatoryResources = Collections.singletonList(dynamicHost);
m_optionalResources = Collections.emptyList();
} else {
m_mandatoryResources = new ArrayList<Resource>();
m_optionalResources = new ArrayList<Resource>();
}
}
private void initMandatoryAndOptionalResources() {
if (!isDynamic()) {
m_mandatoryResources.addAll(getContext().getMandatoryResources());
m_optionalResources.addAll(getContext().getOptionalResources());
}
}
Candidates getMultipleCardCandidates()
{
return m_multipleCardCandidates;
}
ResolveContext getContext()
{
return m_resolveContext;
}
ConcurrentMap<String, List<String>> getUsesCache() {
return m_usesCache;
}
void permutateIfNeeded(PermutationType type, Requirement req, Candidates permutation) {
List<Capability> candidates = permutation.getCandidates(req);
if ((candidates != null) && (candidates.size() > 1))
{
if ((type == PermutationType.SUBSTITUTE)) {
if (!m_sub_mutated.add(req)) {
return;
}
} else if (!m_mutated.add(req)) {
return;
}
addPermutation(type, permutation.permutate(req));
}
}
private void clearMutateIndexes() {
m_usesIndex = 0;
m_importIndex = 0;
m_substituteIndex = 0;
m_mutated.clear();
}
void addPermutation(PermutationType type, Candidates permutation) {
if (permutation != null)
{
List<Candidates> typeToAddTo = null;
try {
switch (type) {
case USES :
typeToAddTo = m_usesPermutations;
m_usesPermutations.add(m_usesIndex++, permutation);
break;
case IMPORT :
typeToAddTo = m_importPermutations;
m_importPermutations.add(m_importIndex++, permutation);
break;
case SUBSTITUTE :
typeToAddTo = m_substPermutations;
m_substPermutations.add(m_substituteIndex++, permutation);
break;
default :
throw new IllegalArgumentException("Unknown permitation type: " + type);
}
} catch (IndexOutOfBoundsException e) {
typeToAddTo.add(permutation);
}
}
}
Candidates getNextPermutation() {
Candidates next = null;
do {
if (!m_usesPermutations.isEmpty())
{
next = m_usesPermutations.remove(0);
}
else if (!m_importPermutations.isEmpty())
{
next = m_importPermutations.remove(0);
}
else if (!m_substPermutations.isEmpty())
{
next = m_substPermutations.remove(0);
}
else {
return null;
}
}
while(!m_processedDeltas.add(next.getDelta()));
m_multipleCardCandidates = null;
clearMutateIndexes();
return next;
}
void clearPermutations() {
m_usesPermutations.clear();
m_importPermutations.clear();
m_substPermutations.clear();
m_multipleCardCandidates = null;
m_processedDeltas.clear();
m_currentError = null;
}
boolean checkMultiple(
UsedBlames usedBlames,
Blame usedBlame,
Candidates permutation)
{
CandidateSelector candidates = null;
Requirement req = usedBlame.m_reqs.get(0);
if (Util.isMultiple(req))
{
if (m_multipleCardCandidates == null)
{
m_multipleCardCandidates = permutation.copy();
}
candidates = m_multipleCardCandidates.clearMultipleCardinalityCandidates(req, usedBlames.getRootCauses(req));
}
return (candidates != null) && !candidates.isEmpty();
}
long getPermutationCount() {
return m_usesPermutations.size() + m_importPermutations.size() + m_substPermutations.size();
}
Executor getExecutor() {
return m_executor;
}
ResolutionError getCurrentError() {
return m_currentError;
}
void setCurrentError(ResolutionError error) {
this.m_currentError = error;
}
boolean isDynamic() {
return m_dynamicHost != null;
}
Collection<Resource> getMandatoryResources() {
return m_mandatoryResources;
}
Collection<Resource> getOptionalResources() {
return m_optionalResources;
}
Resource getDynamicHost() {
return m_dynamicHost;
}
Requirement getDynamicRequirement() {
return m_dynamicReq;
}
List<Capability> getDynamicCandidates() {
return m_dynamicCandidates;
}
public boolean isValidRelatedResource(Resource resource) {
Boolean valid = m_validRelatedResources.get(resource);
if (valid == null)
{
m_validRelatedResources.put(resource, Boolean.TRUE);
valid = Boolean.TRUE;
}
return valid;
}
public boolean invalidateRelatedResource(Resource faultyResource) {
Boolean valid = m_validRelatedResources.get(faultyResource);
if (valid != null && valid)
{
m_validRelatedResources.put(faultyResource, Boolean.FALSE);
return true;
}
return false;
}
public Collection<Resource> getRelatedResources(Resource resource) {
Collection<Resource> related = m_relatedResources.get(resource);
return related == null ? Collections.<Resource> emptyList() : related;
}
public void setRelatedResources(Resource resource, Collection<Resource> related) {
m_relatedResources.put(resource, related);
}
@Override
public void run() {
m_isCancelled = new CancellationException();
}
boolean isCancelled() {
return m_isCancelled != null;
}
void checkForCancel() throws ResolutionException {
if (isCancelled()) {
throw new ResolutionException("Resolver operation has been cancelled.", m_isCancelled, null);
}
}
}
public ResolverImpl(Logger logger)
{
this(logger, Runtime.getRuntime().availableProcessors());
}
public ResolverImpl(Logger logger, int parallelism)
{
this.m_logger = logger;
this.m_parallelism = parallelism;
this.m_executor = null;
}
public ResolverImpl(Logger logger, Executor executor)
{
this.m_logger = logger;
this.m_parallelism = -1;
this.m_executor = executor;
}
public Map<Resource, List<Wire>> resolve(ResolveContext rc) throws ResolutionException
{
if (m_executor != null)
{
return resolve(rc, m_executor);
}
else if (m_parallelism > 1)
{
final ExecutorService executor =
System.getSecurityManager() != null ?
AccessController.doPrivileged(
new PrivilegedAction<ExecutorService>()
{
public ExecutorService run()
{
return Executors.newFixedThreadPool(m_parallelism);
}
}, m_acc)
:
Executors.newFixedThreadPool(m_parallelism);
try
{
return resolve(rc, executor);
}
finally
{
if (System.getSecurityManager() != null)
{
AccessController.doPrivileged(new PrivilegedAction<Void>(){
public Void run() {
executor.shutdownNow();
return null;
}
}, m_acc);
}
else
{
executor.shutdownNow();
}
}
}
else
{
return resolve(rc, new DumbExecutor());
}
}
public Map<Resource, List<Wire>> resolve(ResolveContext rc, Executor executor) throws ResolutionException
{
ResolveSession session = ResolveSession.createSession(rc, executor, null, null, null);
return doResolve(session);
}
private Map<Resource, List<Wire>> doResolve(ResolveSession session) throws ResolutionException {
Map<Resource, List<Wire>> wireMap = new HashMap<Resource, List<Wire>>();
boolean retry;
do
{
retry = false;
try
{
getInitialCandidates(session);
if (session.getCurrentError() != null) {
throw session.getCurrentError().toException();
}
Map<Resource, ResolutionError> faultyResources = new HashMap<Resource, ResolutionError>();
Candidates allCandidates = findValidCandidates(session, faultyResources);
session.checkForCancel();
if (session.getCurrentError() != null)
{
Set<Resource> resourceKeys = faultyResources.keySet();
retry = (session.getOptionalResources().removeAll(resourceKeys));
for (Resource faultyResource : resourceKeys)
{
if (session.invalidateRelatedResource(faultyResource))
{
retry = true;
}
}
for (Map.Entry<Resource, ResolutionError> usesError : faultyResources.entrySet())
{
m_logger.logUsesConstraintViolation(usesError.getKey(), usesError.getValue());
}
if (!retry)
{
throw session.getCurrentError().toException();
}
}
else
{
if (session.getMultipleCardCandidates() != null)
{
allCandidates = session.getMultipleCardCandidates();
}
if (session.isDynamic() )
{
wireMap = populateDynamicWireMap(session,
wireMap, allCandidates);
}
else
{
for (Resource resource : allCandidates.getRootHosts().keySet())
{
if (allCandidates.isPopulated(resource))
{
wireMap =
populateWireMap(
session, allCandidates.getWrappedHost(resource),
wireMap, allCandidates);
}
}
}
}
}
finally
{
session.clearPermutations();
}
}
while (retry);
return wireMap;
}
private void getInitialCandidates(ResolveSession session) {
Candidates initialCandidates;
if (session.isDynamic()) {
initialCandidates = new Candidates(session);
ResolutionError prepareError = initialCandidates.populateDynamic();
if (prepareError != null) {
session.setCurrentError(prepareError);
return;
}
} else {
List<Resource> toPopulate = new ArrayList<Resource>();
for (Resource resource : session.getMandatoryResources())
{
if (Util.isFragment(resource) || (session.getContext().getWirings().get(resource) == null))
{
toPopulate.add(resource);
}
}
for (Resource resource : session.getOptionalResources())
{
if (Util.isFragment(resource) || (session.getContext().getWirings().get(resource) == null))
{
toPopulate.add(resource);
}
}
initialCandidates = new Candidates(session);
initialCandidates.populate(toPopulate);
}
ResolutionError prepareError = initialCandidates.prepare();
if (prepareError != null)
{
session.setCurrentError(prepareError);
}
else
{
session.addPermutation(PermutationType.USES, initialCandidates);
}
}
private Candidates findValidCandidates(ResolveSession session, Map<Resource, ResolutionError> faultyResources) {
Candidates allCandidates = null;
boolean foundFaultyResources = false;
do
{
allCandidates = session.getNextPermutation();
if (allCandidates == null)
{
break;
}
Map<Resource, ResolutionError> currentFaultyResources = new HashMap<Resource, ResolutionError>();
session.setCurrentError(
checkConsistency(
session,
allCandidates,
currentFaultyResources
)
);
if (!currentFaultyResources.isEmpty())
{
if (!foundFaultyResources)
{
foundFaultyResources = true;
faultyResources.putAll(currentFaultyResources);
}
else if (faultyResources.size() > currentFaultyResources.size())
{
faultyResources.clear();
faultyResources.putAll(currentFaultyResources);
}
}
}
while (!session.isCancelled() && session.getCurrentError() != null);
return allCandidates;
}
private ResolutionError checkConsistency(
ResolveSession session,
Candidates allCandidates,
Map<Resource, ResolutionError> currentFaultyResources)
{
ResolutionError rethrow = allCandidates.checkSubstitutes();
if (rethrow != null)
{
return rethrow;
}
Map<Resource, Resource> allhosts = allCandidates.getRootHosts();
Map<Resource, Packages> resourcePkgMap =
calculatePackageSpaces(session, allCandidates, allhosts.values());
ResolutionError error = null;
Map<Resource, Object> resultCache =
new OpenHashMap<Resource, Object>(resourcePkgMap.size());
for (Entry<Resource, Resource> entry : allhosts.entrySet())
{
rethrow = checkPackageSpaceConsistency(
session, entry.getValue(),
allCandidates, session.isDynamic(), resourcePkgMap, resultCache);
if (session.isCancelled()) {
return null;
}
if (rethrow != null)
{
Resource faultyResource = entry.getKey();
for (Requirement faultyReq : rethrow.getUnresolvedRequirements())
{
if (faultyReq instanceof WrappedRequirement)
{
faultyResource =
((WrappedRequirement) faultyReq)
.getDeclaredRequirement().getResource();
break;
}
}
currentFaultyResources.put(faultyResource, rethrow);
error = rethrow;
}
}
return error;
}
public Map<Resource,List<Wire>> resolveDynamic(ResolveContext context,
Wiring hostWiring, Requirement dynamicRequirement)
throws ResolutionException
{
Resource host = hostWiring.getResource();
List<Capability> matches = context.findProviders(dynamicRequirement);
if (!matches.isEmpty())
{
for (Capability cap : matches)
{
if (!cap.getNamespace().equals(PackageNamespace.PACKAGE_NAMESPACE))
{
throw new IllegalArgumentException(
"Matching candidate does not provide a package name.");
}
}
ResolveSession session = ResolveSession.createSession(context, new DumbExecutor(), host, dynamicRequirement, matches);
return doResolve(session);
}
throw new Candidates.MissingRequirementError(dynamicRequirement).toException();
}
private static List<WireCandidate> getWireCandidates(ResolveSession session, Candidates allCandidates, Resource resource)
{
List<WireCandidate> wireCandidates = new ArrayList<WireCandidate>(256);
Wiring wiring = session.getContext().getWirings().get(resource);
if (wiring != null)
{
for (Wire wire : wiring.getRequiredResourceWires(null))
{
Requirement r = wire.getRequirement();
if (!r.getResource().equals(wire.getRequirer())
|| Util.isDynamic(r))
{
r = new WrappedRequirement(wire.getRequirer(), r);
}
Capability c = wire.getCapability();
if (!c.getResource().equals(wire.getProvider()))
{
c = new WrappedCapability(wire.getProvider(), c);
}
wireCandidates.add(new WireCandidate(r, c));
}
Requirement dynamicReq = session.getDynamicRequirement();
if (dynamicReq != null && resource.equals(session.getDynamicHost()))
{
Capability cap = allCandidates.getFirstCandidate(dynamicReq);
wireCandidates.add(new WireCandidate(dynamicReq, cap));
}
}
else
{
for (Requirement req : resource.getRequirements(null))
{
if (!Util.isDynamic(req))
{
List<Capability> candCaps = allCandidates.getCandidates(req);
if (candCaps == null)
{
continue;
}
if (Util.isMultiple(req))
{
for (Capability cap : candCaps)
{
wireCandidates.add(new WireCandidate(req, cap));
}
}
else
{
Capability cap = candCaps.get(0);
wireCandidates.add(new WireCandidate(req, cap));
}
}
}
}
return wireCandidates;
}
private static Packages getPackages(
ResolveSession session,
Candidates allCandidates,
Map<Resource, List<WireCandidate>> allWireCandidates,
Map<Resource, Packages> allPackages,
Resource resource,
Packages resourcePkgs)
{
for (WireCandidate wire : allWireCandidates.get(resource))
{
if (Util.isDynamic(wire.requirement))
{
String pkgName = (String) wire.capability.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE);
if (resourcePkgs.m_exportedPkgs.containsKey(pkgName)
|| resourcePkgs.m_importedPkgs.containsKey(pkgName)
|| resourcePkgs.m_requiredPkgs.containsKey(pkgName))
{
throw new IllegalArgumentException(
"Resource "
+ resource
+ " cannot dynamically import package '"
+ pkgName
+ "' since it already has access to it.");
}
}
mergeCandidatePackages(
session,
allPackages,
allCandidates,
resourcePkgs,
wire.requirement,
wire.capability,
new HashSet<Capability>(),
new HashSet<Resource>());
}
return resourcePkgs;
}
private static void computeUses(
ResolveSession session,
Map<Resource, List<WireCandidate>> allWireCandidates,
Map<Resource, Packages> resourcePkgMap,
Resource resource)
{
List<WireCandidate> wireCandidates = allWireCandidates.get(resource);
Packages resourcePkgs = resourcePkgMap.get(resource);
Wiring wiring = session.getContext().getWirings().get(resource);
Set<Capability> usesCycleMap = new HashSet<Capability>();
int size = wireCandidates.size();
boolean isDynamicImporting = size > 0
&& Util.isDynamic(wireCandidates.get(size - 1).requirement);
if ((wiring == null) || isDynamicImporting)
{
for (WireCandidate w : wireCandidates)
{
Requirement req = w.requirement;
Capability cap = w.capability;
if (!req.getNamespace().equals(BundleNamespace.BUNDLE_NAMESPACE)
&& !req.getNamespace().equals(PackageNamespace.PACKAGE_NAMESPACE))
{
List<Requirement> blameReqs =
Collections.singletonList(req);
mergeUses(
session,
resource,
resourcePkgs,
cap,
blameReqs,
cap,
resourcePkgMap,
usesCycleMap);
}
}
for (List<Blame> blames : resourcePkgs.m_importedPkgs.values())
{
for (Blame blame : blames)
{
List<Requirement> blameReqs =
Collections.singletonList(blame.m_reqs.get(0));
mergeUses(
session,
resource,
resourcePkgs,
blame.m_cap,
blameReqs,
null,
resourcePkgMap,
usesCycleMap);
}
}
for (List<Blame> blames : resourcePkgs.m_requiredPkgs.values())
{
for (Blame blame : blames)
{
List<Requirement> blameReqs =
Collections.singletonList(blame.m_reqs.get(0));
mergeUses(
session,
resource,
resourcePkgs,
blame.m_cap,
blameReqs,
null,
resourcePkgMap,
usesCycleMap);
}
}
}
}
private static void mergeCandidatePackages(
ResolveSession session,
Map<Resource, Packages> resourcePkgMap,
Candidates allCandidates,
Packages packages,
Requirement currentReq,
Capability candCap,
Set<Capability> capabilityCycles,
Set<Resource> visitedRequiredBundles)
{
if (!capabilityCycles.add(candCap))
{
return;
}
if (candCap.getNamespace().equals(PackageNamespace.PACKAGE_NAMESPACE))
{
mergeCandidatePackage(
packages.m_importedPkgs,
currentReq, candCap);
}
else if (candCap.getNamespace().equals(BundleNamespace.BUNDLE_NAMESPACE))
{
if (visitedRequiredBundles.add(candCap.getResource()))
{
for (Blame blame : resourcePkgMap.get(candCap.getResource()).m_exportedPkgs.values())
{
mergeCandidatePackage(
packages.m_requiredPkgs,
currentReq,
blame.m_cap);
}
for (Blame blame : resourcePkgMap.get(
candCap.getResource()).m_substitePkgs.values())
{
mergeCandidatePackage(packages.m_requiredPkgs, currentReq,
blame.m_cap);
}
}
Wiring candWiring = session.getContext().getWirings().get(candCap.getResource());
if (candWiring != null)
{
for (Wire w : candWiring.getRequiredResourceWires(null))
{
if (w.getRequirement().getNamespace()
.equals(BundleNamespace.BUNDLE_NAMESPACE))
{
if (Util.isReexport(w.getRequirement()))
{
mergeCandidatePackages(
session,
resourcePkgMap,
allCandidates,
packages,
currentReq,
w.getCapability(),
capabilityCycles,
visitedRequiredBundles);
}
}
}
}
else
{
for (Requirement req : candCap.getResource().getRequirements(null))
{
if (req.getNamespace().equals(BundleNamespace.BUNDLE_NAMESPACE))
{
if (Util.isReexport(req))
{
Capability cap = allCandidates.getFirstCandidate(req);
if (cap != null)
{
mergeCandidatePackages(
session,
resourcePkgMap,
allCandidates,
packages,
currentReq,
cap,
capabilityCycles,
visitedRequiredBundles);
}
}
}
}
}
}
}
private static void mergeCandidatePackage(
OpenHashMap<String, List<Blame>> packages,
Requirement currentReq, Capability candCap)
{
if (candCap.getNamespace().equals(PackageNamespace.PACKAGE_NAMESPACE))
{
String pkgName = (String) candCap.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE);
List<Requirement> blameReqs = Collections.singletonList(currentReq);
List<Blame> blames = packages.getOrCompute(pkgName);
blames.add(new Blame(candCap, blameReqs));
}
}
private static void mergeUses(
ResolveSession session, Resource current, Packages currentPkgs,
Capability mergeCap, List<Requirement> blameReqs, Capability matchingCap,
Map<Resource, Packages> resourcePkgMap,
Set<Capability> cycleMap)
{
if (current.equals(mergeCap.getResource()))
{
return;
}
if (!cycleMap.add(mergeCap))
{
return;
}
for (Capability candSourceCap : getPackageSources(mergeCap, resourcePkgMap))
{
List<String> uses;
{
String s = candSourceCap.getDirectives().get(Namespace.CAPABILITY_USES_DIRECTIVE);
if (s != null && s.length() > 0)
{
uses = session.getUsesCache().get(s);
if (uses == null)
{
uses = parseUses(s);
session.getUsesCache().put(s, uses);
}
}
else
{
continue;
}
}
Packages candSourcePkgs = resourcePkgMap.get(candSourceCap.getResource());
for (String usedPkgName : uses)
{
List<Blame> candSourceBlames;
Blame candExportedBlame = candSourcePkgs.m_exportedPkgs.get(usedPkgName);
if (candExportedBlame != null)
{
candSourceBlames = Collections.singletonList(candExportedBlame);
}
else
{
candSourceBlames = candSourcePkgs.m_requiredPkgs.get(usedPkgName);
if (candSourceBlames == null)
{
candSourceBlames = candSourcePkgs.m_importedPkgs.get(usedPkgName);
}
}
if (candSourceBlames == null)
{
continue;
}
ArrayMap<Capability, UsedBlames> usedPkgBlames = currentPkgs.m_usedPkgs.getOrCompute(usedPkgName);
for (Blame blame : candSourceBlames)
{
if (blame.m_reqs != null)
{
List<Requirement> blameReqs2 = new ArrayList<Requirement>(blameReqs.size() + 1);
blameReqs2.addAll(blameReqs);
blameReqs2.add(blame.m_reqs.get(blame.m_reqs.size() - 1));
addUsedBlame(usedPkgBlames, blame.m_cap, blameReqs2, matchingCap);
mergeUses(session, current, currentPkgs, blame.m_cap, blameReqs2, matchingCap,
resourcePkgMap, cycleMap);
}
else
{
addUsedBlame(usedPkgBlames, blame.m_cap, blameReqs, matchingCap);
mergeUses(session, current, currentPkgs, blame.m_cap, blameReqs, matchingCap,
resourcePkgMap, cycleMap);
}
}
}
}
}
private static Map<Resource, Packages> calculatePackageSpaces(
final ResolveSession session,
final Candidates allCandidates,
Collection<Resource> hosts)
{
final EnhancedExecutor executor = new EnhancedExecutor(session.getExecutor());
final Map<Resource, List<WireCandidate>> allWireCandidates = new ConcurrentHashMap<Resource, List<WireCandidate>>();
{
final ConcurrentMap<Resource, Runnable> tasks = new ConcurrentHashMap<Resource, Runnable>(allCandidates.getNbResources());
class Computer implements Runnable
{
final Resource resource;
public Computer(Resource resource)
{
this.resource = resource;
}
public void run()
{
List<WireCandidate> wireCandidates = getWireCandidates(session, allCandidates, resource);
allWireCandidates.put(resource, wireCandidates);
for (WireCandidate w : wireCandidates)
{
Resource u = w.capability.getResource();
if (!tasks.containsKey(u))
{
Computer c = new Computer(u);
if (tasks.putIfAbsent(u, c) == null)
{
executor.execute(c);
}
}
}
}
}
for (Resource resource : hosts)
{
executor.execute(new Computer(resource));
}
executor.await();
}
final OpenHashMap<Resource, Packages> allPackages = new OpenHashMap<Resource, Packages>(allCandidates.getNbResources());
for (final Resource resource : allWireCandidates.keySet())
{
final Packages packages = new Packages(resource);
allPackages.put(resource, packages);
executor.execute(new Runnable()
{
public void run()
{
calculateExportedPackages(session, allCandidates, resource,
packages.m_exportedPkgs, packages.m_substitePkgs);
}
});
}
executor.await();
for (final Resource resource : allWireCandidates.keySet())
{
executor.execute(new Runnable()
{
public void run()
{
getPackages(session, allCandidates, allWireCandidates, allPackages, resource, allPackages.get(resource));
}
});
}
executor.await();
for (Map.Entry<Resource, Packages> entry : allPackages.fast())
{
final Resource resource = entry.getKey();
final Packages packages = entry.getValue();
if (!packages.m_requiredPkgs.isEmpty())
{
getPackageSourcesInternal(session, allPackages, resource, packages);
}
}
for (Map.Entry<Resource, Packages> entry : allPackages.fast())
{
final Resource resource = entry.getKey();
final Packages packages = entry.getValue();
if (packages.m_sources.isEmpty())
{
executor.execute(new Runnable()
{
public void run()
{
getPackageSourcesInternal(session, allPackages, resource, packages);
}
});
}
}
executor.await();
for (final Resource resource : allWireCandidates.keySet())
{
executor.execute(new Runnable()
{
public void run()
{
computeUses(session, allWireCandidates, allPackages, resource);
}
});
}
executor.await();
return allPackages;
}
private static List<String> parseUses(String s) {
int nb = 1;
int l = s.length();
for (int i = 0; i < l; i++) {
if (s.charAt(i) == ',') {
nb++;
}
}
List<String> uses = new ArrayList<String>(nb);
int start = 0;
while (true) {
while (start < l) {
char c = s.charAt(start);
if (c != ' ' && c != ',') {
break;
}
start++;
}
int end = start + 1;
while (end < l) {
char c = s.charAt(end);
if (c == ' ' || c == ',') {
break;
}
end++;
}
if (start < l) {
uses.add(s.substring(start, end));
start = end + 1;
} else {
break;
}
}
return uses;
}
private static void addUsedBlame(
ArrayMap<Capability, UsedBlames> usedBlames, Capability usedCap,
List<Requirement> blameReqs, Capability matchingCap)
{
Blame newBlame = new Blame(usedCap, blameReqs);
UsedBlames addToBlame = usedBlames.getOrCompute(usedCap);
addToBlame.addBlame(newBlame, matchingCap);
}
private ResolutionError checkPackageSpaceConsistency(
ResolveSession session,
Resource resource,
Candidates allCandidates,
boolean dynamic,
Map<Resource, Packages> resourcePkgMap,
Map<Resource, Object> resultCache)
{
if (!dynamic && session.getContext().getWirings().containsKey(resource))
{
return null;
}
Object cache = resultCache.get(resource);
if (cache != null)
{
return cache instanceof ResolutionError ? (ResolutionError) cache : null;
}
Packages pkgs = resourcePkgMap.get(resource);
ResolutionError rethrow = null;
for (Entry<String, List<Blame>> entry : pkgs.m_importedPkgs.fast())
{
String pkgName = entry.getKey();
List<Blame> blames = entry.getValue();
if (blames.size() > 1)
{
Blame sourceBlame = null;
for (Blame blame : blames)
{
if (sourceBlame == null)
{
sourceBlame = blame;
}
else if (!sourceBlame.m_cap.getResource().equals(blame.m_cap.getResource()))
{
session.addPermutation(PermutationType.IMPORT, allCandidates.permutate(blame.m_reqs.get(0)));
session.addPermutation(PermutationType.IMPORT, allCandidates.permutate(sourceBlame.m_reqs.get(0)));
rethrow = new UseConstraintError(
session.getContext(), allCandidates,
resource, pkgName,
sourceBlame, blame);
if (m_logger.isDebugEnabled())
{
m_logger.debug(
"Candidate permutation failed due to a conflict with a "
+ "fragment import; will try another if possible."
+ " (" + rethrow.getMessage() + ")");
}
return rethrow;
}
}
}
}
AtomicReference<Candidates> permRef1 = new AtomicReference<Candidates>();
AtomicReference<Candidates> permRef2 = new AtomicReference<Candidates>();
Set<Requirement> mutated = null;
for (Entry<String, Blame> entry : pkgs.m_exportedPkgs.fast())
{
String pkgName = entry.getKey();
Blame exportBlame = entry.getValue();
ArrayMap<Capability, UsedBlames> pkgBlames = pkgs.m_usedPkgs.get(pkgName);
if (pkgBlames == null)
{
continue;
}
for (UsedBlames usedBlames : pkgBlames.values())
{
if (!isCompatible(exportBlame, usedBlames.m_cap, resourcePkgMap))
{
mutated = (mutated != null)
? mutated
: new HashSet<Requirement>();
rethrow = permuteUsedBlames(session, rethrow, allCandidates, resource,
pkgName, null, usedBlames, permRef1, permRef2, mutated);
}
}
if (rethrow != null)
{
if (!mutated.isEmpty())
{
session.addPermutation(PermutationType.USES, permRef1.get());
session.addPermutation(PermutationType.USES, permRef2.get());
}
if (m_logger.isDebugEnabled())
{
m_logger.debug("Candidate permutation failed due to a conflict between "
+ "an export and import; will try another if possible."
+ " (" + rethrow.getMessage() + ")");
}
return rethrow;
}
}
OpenHashMap<String, List<Blame>> allImportRequirePkgs;
if (pkgs.m_requiredPkgs.isEmpty())
{
allImportRequirePkgs = pkgs.m_importedPkgs;
}
else
{
allImportRequirePkgs = new OpenHashMap<String, List<Blame>>(pkgs.m_requiredPkgs.size() + pkgs.m_importedPkgs.size());
allImportRequirePkgs.putAll(pkgs.m_requiredPkgs);
allImportRequirePkgs.putAll(pkgs.m_importedPkgs);
}
for (Entry<String, List<Blame>> entry : allImportRequirePkgs.fast())
{
String pkgName = entry.getKey();
ArrayMap<Capability, UsedBlames> pkgBlames = pkgs.m_usedPkgs.get(pkgName);
if (pkgBlames == null)
{
continue;
}
List<Blame> requirementBlames = entry.getValue();
for (UsedBlames usedBlames : pkgBlames.values())
{
if (!isCompatible(requirementBlames, usedBlames.m_cap, resourcePkgMap))
{
mutated = (mutated != null)
? mutated
: new HashSet<Requirement>();
Blame requirementBlame = requirementBlames.get(0);
rethrow = permuteUsedBlames(session, rethrow, allCandidates, resource, pkgName, requirementBlame, usedBlames, permRef1, permRef2, mutated);
}
if (rethrow != null)
{
if (!mutated.isEmpty())
{
session.addPermutation(PermutationType.USES, permRef1.get());
session.addPermutation(PermutationType.USES, permRef2.get());
}
for (Blame requirementBlame : requirementBlames)
{
Requirement req = requirementBlame.m_reqs.get(0);
if (!mutated.contains(req))
{
session.permutateIfNeeded(PermutationType.IMPORT, req, allCandidates);
}
}
if (m_logger.isDebugEnabled())
{
m_logger.debug("Candidate permutation failed due to a conflict between "
+ "imports; will try another if possible."
+ " (" + rethrow.getMessage() + ")"
);
}
return rethrow;
}
}
}
resultCache.put(resource, Boolean.TRUE);
long permCount = session.getPermutationCount();
for (Requirement req : resource.getRequirements(null))
{
Capability cap = allCandidates.getFirstCandidate(req);
if (cap != null)
{
if (!resource.equals(cap.getResource()))
{
rethrow = checkPackageSpaceConsistency(
session, cap.getResource(),
allCandidates, false, resourcePkgMap, resultCache);
if (session.isCancelled()) {
return null;
}
if (rethrow != null)
{
if (permCount == session.getPermutationCount())
{
session.addPermutation(PermutationType.IMPORT, allCandidates.permutate(req));
}
return rethrow;
}
}
}
}
return null;
}
private ResolutionError permuteUsedBlames(ResolveSession session,
ResolutionError rethrow, Candidates allCandidates, Resource resource,
String pkgName, Blame requirementBlame, UsedBlames usedBlames,
AtomicReference<Candidates> permRef1, AtomicReference<Candidates> permRef2,
Set<Requirement> mutated)
{
for (Blame usedBlame : usedBlames.m_blames)
{
if (session.checkMultiple(usedBlames, usedBlame, allCandidates))
{
continue;
}
if (rethrow == null)
{
if (requirementBlame == null)
{
rethrow = new UseConstraintError(session.getContext(), allCandidates,
resource, pkgName, usedBlame);
}
else
{
rethrow = new UseConstraintError(session.getContext(), allCandidates,
resource, pkgName, requirementBlame, usedBlame);
}
}
Candidates perm1 = permRef1.get();
if (perm1 == null)
{
perm1 = allCandidates.copy();
permRef1.set(perm1);
}
for (int reqIdx = usedBlame.m_reqs.size() - 1; reqIdx >= 0; reqIdx--)
{
Requirement req = usedBlame.m_reqs.get(reqIdx);
if (permuteUsedBlameRequirement(req, mutated, perm1))
{
break;
}
}
Candidates perm2 = permRef2.get();
if (perm2 == null)
{
perm2 = allCandidates.copy();
permRef2.set(perm2);
}
for (int reqIdx = 0; reqIdx < usedBlame.m_reqs.size(); reqIdx++)
{
Requirement req = usedBlame.m_reqs.get(reqIdx);
if (permuteUsedBlameRequirement(req, mutated, perm2))
{
break;
}
}
}
return rethrow;
}
private boolean permuteUsedBlameRequirement(Requirement req, Set<Requirement> mutated, Candidates permutation)
{
if (Util.isMultiple(req))
{
return false;
}
if (mutated.contains(req))
{
return true;
}
if (permutation.canRemoveCandidate(req))
{
permutation.removeFirstCandidate(req);
mutated.add(req);
return true;
}
return false;
}
private static OpenHashMap<String, Blame> calculateExportedPackages(
ResolveSession session,
Candidates allCandidates,
Resource resource,
OpenHashMap<String, Blame> exports, OpenHashMap<String, Blame> substitutes)
{
Wiring wiring = session.getContext().getWirings().get(resource);
List<Capability> caps = (wiring != null)
? wiring.getResourceCapabilities(null)
: resource.getCapabilities(null);
for (Capability cap : caps)
{
if (cap.getNamespace().equals(PackageNamespace.PACKAGE_NAMESPACE))
{
if (!cap.getResource().equals(resource))
{
cap = new WrappedCapability(resource, cap);
}
exports.put(
(String) cap.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE),
new Blame(cap, null));
}
}
if (wiring != null)
{
for (Wire wire : session.getContext().getSubstitutionWires(wiring))
{
Capability cap = wire.getCapability();
if (!cap.getResource().equals(wire.getProvider()))
{
cap = new WrappedCapability(wire.getProvider(), cap);
}
substitutes.put(
(String) cap.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE),
new Blame(cap, null));
}
}
else
{
if (!exports.isEmpty())
{
for (Requirement req : resource.getRequirements(null))
{
if (req.getNamespace().equals(PackageNamespace.PACKAGE_NAMESPACE))
{
Capability cand = allCandidates.getFirstCandidate(req);
if (cand != null)
{
String pkgName = (String) cand.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE);
Blame blame = exports.remove(pkgName);
if (blame != null)
{
substitutes.put(pkgName, new Blame(cand, null));
}
}
}
}
}
}
return exports;
}
private static boolean isCompatible(
Blame currentBlame, Capability candCap,
Map<Resource, Packages> resourcePkgMap)
{
if (currentBlame.m_cap.equals(candCap))
{
return true;
}
Set<Capability> candSources = getPackageSources(candCap, resourcePkgMap);
Set<Capability> currentSources = getPackageSources(currentBlame.m_cap, resourcePkgMap);
return currentSources.containsAll(candSources)
|| candSources.containsAll(currentSources);
}
private static boolean isCompatible(
List<Blame> currentBlames, Capability candCap,
Map<Resource, Packages> resourcePkgMap)
{
int size = currentBlames.size();
switch (size)
{
case 0:
return true;
case 1:
return isCompatible(currentBlames.get(0), candCap, resourcePkgMap);
default:
Set<Capability> currentSources = new HashSet<Capability>(currentBlames.size());
for (Blame currentBlame : currentBlames)
{
Set<Capability> blameSources = getPackageSources(currentBlame.m_cap, resourcePkgMap);
currentSources.addAll(blameSources);
}
Set<Capability> candSources = getPackageSources(candCap, resourcePkgMap);
return currentSources.containsAll(candSources)
|| candSources.containsAll(currentSources);
}
}
private static Set<Capability> getPackageSources(
Capability cap, Map<Resource, Packages> resourcePkgMap)
{
Resource resource = cap.getResource();
if(resource == null)
{
return new HashSet<Capability>();
}
OpenHashMap<Capability, Set<Capability>> sources = resourcePkgMap.get(resource).m_sources;
if(sources == null)
{
return new HashSet<Capability>();
}
Set<Capability> packageSources = sources.get(cap);
if(packageSources == null)
{
return new HashSet<Capability>();
}
return packageSources;
}
private static void getPackageSourcesInternal(
ResolveSession session, Map<Resource, Packages> resourcePkgMap,
Resource resource, Packages packages)
{
Wiring wiring = session.getContext().getWirings().get(resource);
List<Capability> caps = (wiring != null)
? wiring.getResourceCapabilities(null)
: resource.getCapabilities(null);
@SuppressWarnings("serial")
OpenHashMap<String, Set<Capability>> pkgs = new OpenHashMap<String, Set<Capability>>(caps.size()) {
public Set<Capability> compute(String pkgName) {
return new HashSet<Capability>();
}
};
Map<Capability, Set<Capability>> sources = packages.m_sources;
for (Capability sourceCap : caps)
{
if (sourceCap.getNamespace().equals(PackageNamespace.PACKAGE_NAMESPACE))
{
String pkgName = (String) sourceCap.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE);
Set<Capability> pkgCaps = pkgs.getOrCompute(pkgName);
if (!resource.equals(sourceCap.getResource()))
{
sourceCap = new WrappedCapability(resource, sourceCap);
}
sources.put(sourceCap, pkgCaps);
pkgCaps.add(sourceCap);
}
else
{
String uses = sourceCap.getDirectives().get(Namespace.CAPABILITY_USES_DIRECTIVE);
if ((uses != null) && uses.length() > 0)
{
sources.put(sourceCap, Collections.singleton(sourceCap));
}
else
{
sources.put(sourceCap, Collections.<Capability>emptySet());
}
}
}
for (Map.Entry<String, Set<Capability>> pkg : pkgs.fast())
{
String pkgName = pkg.getKey();
List<Blame> required = packages.m_requiredPkgs.get(pkgName);
if (required != null)
{
Set<Capability> srcs = pkg.getValue();
for (Blame blame : required)
{
Capability bcap = blame.m_cap;
if (srcs.add(bcap))
{
Resource capResource = bcap.getResource();
Packages capPackages = resourcePkgMap.get(capResource);
Set<Capability> additional = capPackages.m_sources.get(bcap);
if (additional == null)
{
getPackageSourcesInternal(session, resourcePkgMap, capResource, capPackages);
additional = capPackages.m_sources.get(bcap);
}
srcs.addAll(additional);
}
}
}
}
}
private static Resource getDeclaredResource(Resource resource)
{
if (resource instanceof WrappedResource)
{
return ((WrappedResource) resource).getDeclaredResource();
}
return resource;
}
private static Capability getDeclaredCapability(Capability c)
{
if (c instanceof HostedCapability)
{
return ((HostedCapability) c).getDeclaredCapability();
}
return c;
}
private static Requirement getDeclaredRequirement(Requirement r)
{
if (r instanceof WrappedRequirement)
{
return ((WrappedRequirement) r).getDeclaredRequirement();
}
return r;
}
private static Map<Resource, List<Wire>> populateWireMap(
ResolveSession session, Resource resource,
Map<Resource, List<Wire>> wireMap, Candidates allCandidates)
{
Resource unwrappedResource = getDeclaredResource(resource);
if (!session.getContext().getWirings().containsKey(unwrappedResource)
&& !wireMap.containsKey(unwrappedResource))
{
wireMap.put(unwrappedResource, Collections.<Wire>emptyList());
List<Wire> packageWires = new ArrayList<Wire>();
List<Wire> bundleWires = new ArrayList<Wire>();
List<Wire> capabilityWires = new ArrayList<Wire>();
for (Requirement req : resource.getRequirements(null))
{
List<Capability> cands = allCandidates.getCandidates(req);
if ((cands != null) && (cands.size() > 0))
{
for (Capability cand : cands)
{
if (!cand.getNamespace().startsWith("osgi.wiring.")
|| !resource.equals(cand.getResource()))
{
populateWireMap(session, cand.getResource(),
wireMap, allCandidates);
Resource provider;
if (req.getNamespace().equals(IdentityNamespace.IDENTITY_NAMESPACE)) {
provider = getDeclaredCapability(cand).getResource();
} else {
provider = getDeclaredResource(cand.getResource());
}
Wire wire = new WireImpl(
unwrappedResource,
getDeclaredRequirement(req),
provider,
getDeclaredCapability(cand));
if (req.getNamespace().equals(PackageNamespace.PACKAGE_NAMESPACE))
{
packageWires.add(wire);
}
else if (req.getNamespace().equals(BundleNamespace.BUNDLE_NAMESPACE))
{
bundleWires.add(wire);
}
else
{
capabilityWires.add(wire);
}
}
if (!Util.isMultiple(req))
{
break;
}
}
}
}
packageWires.addAll(bundleWires);
packageWires.addAll(capabilityWires);
wireMap.put(unwrappedResource, packageWires);
if (resource instanceof WrappedResource)
{
List<Resource> fragments = ((WrappedResource) resource).getFragments();
for (Resource fragment : fragments)
{
List<Wire> fragmentWires = wireMap.get(fragment);
fragmentWires = (fragmentWires == null)
? new ArrayList<Wire>() : fragmentWires;
for (Requirement req : fragment.getRequirements(null))
{
if (!isPayload(req))
{
if (req.getNamespace().equals(HostNamespace.HOST_NAMESPACE))
{
fragmentWires.add(
new WireImpl(
getDeclaredResource(fragment),
req,
unwrappedResource,
unwrappedResource.getCapabilities(
HostNamespace.HOST_NAMESPACE).get(0)));
}
else if (!session.getContext().getWirings().containsKey(fragment)
&& !wireMap.containsKey(fragment))
{
Wire wire = createWire(req, allCandidates);
if (wire != null)
{
fragmentWires.add(wire);
}
}
}
}
wireMap.put(fragment, fragmentWires);
}
}
for (Resource related : session.getRelatedResources(unwrappedResource)) {
if (allCandidates.isPopulated(related)) {
populateWireMap(session, related, wireMap, allCandidates);
}
}
}
return wireMap;
}
private static Wire createWire(Requirement requirement, Candidates allCandidates)
{
Capability cand = allCandidates.getFirstCandidate(requirement);
if (cand == null) {
return null;
}
return new WireImpl(
getDeclaredResource(requirement.getResource()),
getDeclaredRequirement(requirement),
getDeclaredResource(cand.getResource()),
getDeclaredCapability(cand));
}
private static boolean isPayload(Requirement fragmentReq)
{
if (ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE
.equals(fragmentReq.getNamespace()))
{
return false;
}
if (HostNamespace.HOST_NAMESPACE.equals(fragmentReq.getNamespace()))
{
return false;
}
return true;
}
private static Map<Resource, List<Wire>> populateDynamicWireMap(
ResolveSession session, Map<Resource,
List<Wire>> wireMap, Candidates allCandidates)
{
wireMap.put(session.getDynamicHost(), Collections.<Wire>emptyList());
List<Wire> packageWires = new ArrayList<Wire>();
Capability dynCand = allCandidates.getFirstCandidate(session.getDynamicRequirement());
if (!session.getContext().getWirings().containsKey(dynCand.getResource()))
{
populateWireMap(session, dynCand.getResource(),
wireMap, allCandidates);
}
packageWires.add(
new WireImpl(
session.getDynamicHost(),
session.getDynamicRequirement(),
getDeclaredResource(dynCand.getResource()),
getDeclaredCapability(dynCand)));
wireMap.put(session.getDynamicHost(), packageWires);
return wireMap;
}
@SuppressWarnings("unused")
private static void dumpResourcePkgMap(
ResolveContext rc, Map<Resource, Packages> resourcePkgMap)
{
System.out.println("+++RESOURCE PKG MAP+++");
for (Entry<Resource, Packages> entry : resourcePkgMap.entrySet())
{
dumpResourcePkgs(rc, entry.getKey(), entry.getValue());
}
}
private static void dumpResourcePkgs(
ResolveContext rc, Resource resource, Packages packages)
{
Wiring wiring = rc.getWirings().get(resource);
System.out.println(resource
+ " (" + ((wiring != null) ? "RESOLVED)" : "UNRESOLVED)"));
System.out.println(" EXPORTED");
for (Entry<String, Blame> entry : packages.m_exportedPkgs.entrySet())
{
System.out.println(" " + entry.getKey() + " - " + entry.getValue());
}
System.out.println(" IMPORTED");
for (Entry<String, List<Blame>> entry : packages.m_importedPkgs.entrySet())
{
System.out.println(" " + entry.getKey() + " - " + entry.getValue());
}
System.out.println(" REQUIRED");
for (Entry<String, List<Blame>> entry : packages.m_requiredPkgs.entrySet())
{
System.out.println(" " + entry.getKey() + " - " + entry.getValue());
}
System.out.println(" USED");
for (Entry<String, ArrayMap<Capability, UsedBlames>> entry : packages.m_usedPkgs.entrySet())
{
System.out.println(" " + entry.getKey() + " - " + entry.getValue().values());
}
}
private static final class WireCandidate
{
public final Requirement requirement;
public final Capability capability;
public WireCandidate(Requirement requirement, Capability capability)
{
this.requirement = requirement;
this.capability = capability;
}
}
public static class Packages
{
public final OpenHashMap<String, Blame> m_exportedPkgs;
public final OpenHashMap<String, Blame> m_substitePkgs;
public final OpenHashMap<String, List<Blame>> m_importedPkgs;
public final OpenHashMap<String, List<Blame>> m_requiredPkgs;
public final OpenHashMap<String, ArrayMap<Capability, UsedBlames>> m_usedPkgs;
public final OpenHashMap<Capability, Set<Capability>> m_sources;
@SuppressWarnings("serial")
public Packages(Resource resource)
{
int nbCaps = resource.getCapabilities(null).size();
int nbReqs = resource.getRequirements(null).size();
m_exportedPkgs = new OpenHashMap<String, Blame>(nbCaps);
m_substitePkgs = new OpenHashMap<String, Blame>(nbCaps);
m_importedPkgs = new OpenHashMap<String, List<Blame>>(nbReqs) {
public List<Blame> compute(String s) {
return new ArrayList<Blame>();
}
};
m_requiredPkgs = new OpenHashMap<String, List<Blame>>(nbReqs) {
public List<Blame> compute(String s) {
return new ArrayList<Blame>();
}
};
m_usedPkgs = new OpenHashMap<String, ArrayMap<Capability, UsedBlames>>(128) {
@Override
protected ArrayMap<Capability, UsedBlames> compute(String s) {
return new ArrayMap<Capability, UsedBlames>() {
@Override
protected UsedBlames compute(Capability key) {
return new UsedBlames(key);
}
};
}
};
m_sources = new OpenHashMap<Capability, Set<Capability>>(nbCaps);
}
}
private static class Blame
{
public final Capability m_cap;
public final List<Requirement> m_reqs;
public Blame(Capability cap, List<Requirement> reqs)
{
m_cap = cap;
m_reqs = reqs;
}
@Override
public String toString()
{
return m_cap.getResource()
+ "." + m_cap.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE)
+ (((m_reqs == null) || m_reqs.isEmpty())
? " NO BLAME"
: " BLAMED ON " + m_reqs);
}
@Override
public boolean equals(Object o)
{
return (o instanceof Blame) && m_reqs.equals(((Blame) o).m_reqs)
&& m_cap.equals(((Blame) o).m_cap);
}
}
private static class UsedBlames
{
public final Capability m_cap;
public final List<Blame> m_blames = new ArrayList<ResolverImpl.Blame>();
private Map<Requirement, Set<Capability>> m_rootCauses;
public UsedBlames(Capability cap)
{
m_cap = cap;
}
public void addBlame(Blame blame, Capability matchingRootCause)
{
if (!m_cap.equals(blame.m_cap))
{
throw new IllegalArgumentException(
"Attempt to add a blame with a different used capability: "
+ blame.m_cap);
}
m_blames.add(blame);
if (matchingRootCause != null)
{
Requirement req = blame.m_reqs.get(0);
if (Util.isMultiple(req))
{
if (m_rootCauses == null)
{
m_rootCauses = new HashMap<Requirement, Set<Capability>>();
}
Set<Capability> rootCauses = m_rootCauses.get(req);
if (rootCauses == null)
{
rootCauses = new HashSet<Capability>();
m_rootCauses.put(req, rootCauses);
}
rootCauses.add(matchingRootCause);
}
}
}
public Set<Capability> getRootCauses(Requirement req)
{
if (m_rootCauses == null)
{
return Collections.emptySet();
}
Set<Capability> result = m_rootCauses.get(req);
return result == null ? Collections.<Capability>emptySet() : result;
}
@Override
public String toString()
{
return m_blames.toString();
}
}
private static final class UseConstraintError extends ResolutionError {
private final ResolveContext m_context;
private final Candidates m_allCandidates;
private final Resource m_resource;
private final String m_pkgName;
private final Blame m_blame1;
private final Blame m_blame2;
public UseConstraintError(ResolveContext context, Candidates allCandidates, Resource resource, String pkgName, Blame blame) {
this(context, allCandidates, resource, pkgName, blame, null);
}
public UseConstraintError(ResolveContext context, Candidates allCandidates, Resource resource, String pkgName, Blame blame1, Blame blame2) {
this.m_context = context;
this.m_allCandidates = allCandidates;
this.m_resource = resource;
this.m_pkgName = pkgName;
if (blame1 == null)
{
throw new NullPointerException("First blame cannot be null.");
}
this.m_blame1 = blame1;
this.m_blame2 = blame2;
}
public String getMessage() {
if (m_blame2 == null)
{
return "Uses constraint violation. Unable to resolve resource "
+ Util.getSymbolicName(m_resource)
+ " [" + m_resource
+ "] because it exports package '"
+ m_pkgName
+ "' and is also exposed to it from resource "
+ Util.getSymbolicName(m_blame1.m_cap.getResource())
+ " [" + m_blame1.m_cap.getResource()
+ "] via the following dependency chain:\n\n"
+ toStringBlame(m_blame1);
}
else
{
return "Uses constraint violation. Unable to resolve resource "
+ Util.getSymbolicName(m_resource)
+ " [" + m_resource
+ "] because it is exposed to package '"
+ m_pkgName
+ "' from resources "
+ Util.getSymbolicName(m_blame1.m_cap.getResource())
+ " [" + m_blame1.m_cap.getResource()
+ "] and "
+ Util.getSymbolicName(m_blame2.m_cap.getResource())
+ " [" + m_blame2.m_cap.getResource()
+ "] via two dependency chains.\n\nChain 1:\n"
+ toStringBlame(m_blame1)
+ "\n\nChain 2:\n"
+ toStringBlame(m_blame2);
}
}
public Collection<Requirement> getUnresolvedRequirements() {
if (m_blame2 == null)
{
return Collections.singleton(m_blame1.m_reqs.get(0));
}
else
{
return Collections.singleton(m_blame2.m_reqs.get(0));
}
}
private String toStringBlame(Blame blame)
{
StringBuilder sb = new StringBuilder();
if ((blame.m_reqs != null) && !blame.m_reqs.isEmpty())
{
for (int i = 0; i < blame.m_reqs.size(); i++)
{
Requirement req = blame.m_reqs.get(i);
sb.append(" ");
sb.append(Util.getSymbolicName(req.getResource()));
sb.append(" [");
sb.append(req.getResource().toString());
sb.append("]\n");
if (req.getNamespace().equals(PackageNamespace.PACKAGE_NAMESPACE))
{
sb.append(" import: ");
}
else
{
sb.append(" require: ");
}
sb.append(req.getDirectives().get(Namespace.REQUIREMENT_FILTER_DIRECTIVE));
sb.append("\n |");
if (req.getNamespace().equals(PackageNamespace.PACKAGE_NAMESPACE))
{
sb.append("\n export: ");
}
else
{
sb.append("\n provide: ");
}
if ((i + 1) < blame.m_reqs.size())
{
Capability cap = getSatisfyingCapability(blame.m_reqs.get(i));
if (cap.getNamespace().equals(PackageNamespace.PACKAGE_NAMESPACE))
{
sb.append(PackageNamespace.PACKAGE_NAMESPACE);
sb.append("=");
sb.append(cap.getAttributes()
.get(PackageNamespace.PACKAGE_NAMESPACE));
Capability usedCap =
getSatisfyingCapability(blame.m_reqs.get(i + 1));
sb.append("; uses:=");
sb.append(usedCap.getAttributes()
.get(PackageNamespace.PACKAGE_NAMESPACE));
}
else
{
sb.append(cap);
}
sb.append("\n");
}
else
{
Capability export = getSatisfyingCapability(blame.m_reqs.get(i));
sb.append(export.getNamespace());
sb.append(": ");
Object namespaceVal = export.getAttributes().get(export.getNamespace());
if (namespaceVal != null)
{
sb.append(namespaceVal.toString());
}
else
{
for (Entry<String, Object> attrEntry : export.getAttributes().entrySet())
{
sb.append(attrEntry.getKey()).append('=')
.append(attrEntry.getValue()).append(';');
}
}
if (export.getNamespace().equals(PackageNamespace.PACKAGE_NAMESPACE)
&& !export.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE)
.equals(blame.m_cap.getAttributes().get(
PackageNamespace.PACKAGE_NAMESPACE)))
{
sb.append("; uses:=");
sb.append(blame.m_cap.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE));
sb.append("\n export: ");
sb.append(PackageNamespace.PACKAGE_NAMESPACE);
sb.append("=");
sb.append(blame.m_cap.getAttributes()
.get(PackageNamespace.PACKAGE_NAMESPACE));
}
sb.append("\n ");
sb.append(Util.getSymbolicName(blame.m_cap.getResource()));
sb.append(" [");
sb.append(blame.m_cap.getResource().toString());
sb.append("]");
}
}
}
else
{
sb.append(blame.m_cap.getResource().toString());
}
return sb.toString();
}
private Capability getSatisfyingCapability(Requirement req)
{
Capability cap = m_allCandidates.getFirstCandidate(req);
if (cap == null && m_context.getWirings().containsKey(req.getResource()))
{
List<Wire> wires =
m_context.getWirings().get(req.getResource()).getRequiredResourceWires(null);
req = getDeclaredRequirement(req);
for (Wire w : wires)
{
if (w.getRequirement().equals(req))
{
cap = w.getCapability();
break;
}
}
}
return cap;
}
@Override
public ResolutionException toException()
{
return new ReasonException(ReasonException.Reason.UseConstraint, getMessage(), null, getUnresolvedRequirements());
}
}
private static class EnhancedExecutor
{
private final Executor executor;
private final Queue<Future<Void>> awaiting = new ConcurrentLinkedQueue<Future<Void>>();
private final AtomicReference<Throwable> throwable = new AtomicReference<Throwable>();
public EnhancedExecutor(Executor executor)
{
this.executor = executor;
}
public void execute(final Runnable runnable)
{
FutureTask<Void> task = new FutureTask<Void>(new Runnable()
{
public void run()
{
try
{
runnable.run();
}
catch (Throwable t)
{
throwable.compareAndSet(null, t);
}
}
}, (Void) null);
awaiting.add(task);
try
{
executor.execute(task);
}
catch (Throwable t)
{
task.cancel(false);
throwable.compareAndSet(null, t);
}
}
public void await()
{
Future<Void> awaitTask;
while (throwable.get() == null && (awaitTask = awaiting.poll()) != null)
{
if (!awaitTask.isDone() && !awaitTask.isCancelled())
{
try
{
awaitTask.get();
}
catch (CancellationException e)
{
}
catch (InterruptedException e)
{
throw new IllegalStateException(e);
}
catch (ExecutionException e)
{
throw new RuntimeException(e.getCause());
}
}
}
Throwable t = throwable.get();
if (t != null)
{
if (t instanceof Runnable)
{
throw (RuntimeException) t;
}
else if (t instanceof Error)
{
throw (Error) t;
}
throw new RuntimeException(t);
}
}
}
static class DumbExecutor implements Executor
{
public void execute(Runnable command)
{
command.run();
}
}
}