diff --git a/bundles/org.eclipse.osgi/felix/src/org/apache/felix/resolver/Candidates.java b/bundles/org.eclipse.osgi/felix/src/org/apache/felix/resolver/Candidates.java index 8750706f0a8..e7de4889cab 100644 --- a/bundles/org.eclipse.osgi/felix/src/org/apache/felix/resolver/Candidates.java +++ b/bundles/org.eclipse.osgi/felix/src/org/apache/felix/resolver/Candidates.java @@ -18,17 +18,38 @@ */ package org.apache.felix.resolver; -import java.util.*; +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.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; import java.util.Map.Entry; +import java.util.Set; +import java.util.TreeMap; import java.util.concurrent.atomic.AtomicBoolean; - import org.apache.felix.resolver.ResolverImpl.PermutationType; import org.apache.felix.resolver.ResolverImpl.ResolveSession; import org.apache.felix.resolver.reason.ReasonException; -import org.apache.felix.resolver.util.*; +import org.apache.felix.resolver.util.CandidateSelector; +import org.apache.felix.resolver.util.CopyOnWriteSet; +import org.apache.felix.resolver.util.OpenHashMap; +import org.apache.felix.resolver.util.OpenHashMapList; +import org.apache.felix.resolver.util.OpenHashMapSet; +import org.apache.felix.resolver.util.ShadowList; import org.osgi.framework.Version; -import org.osgi.framework.namespace.*; -import org.osgi.resource.*; +import org.osgi.framework.namespace.HostNamespace; +import org.osgi.framework.namespace.IdentityNamespace; +import org.osgi.framework.namespace.PackageNamespace; +import org.osgi.resource.Capability; +import org.osgi.resource.Requirement; +import org.osgi.resource.Resource; +import org.osgi.resource.Wire; +import org.osgi.resource.Wiring; import org.osgi.service.resolver.HostedCapability; import org.osgi.service.resolver.ResolutionException; import org.osgi.service.resolver.ResolveContext; @@ -868,7 +889,8 @@ public ResolutionError prepare() // really be attached to the original host, not the wrapper. if (!c.getNamespace().equals(HostNamespace.HOST_NAMESPACE)) { - Capability origCap = ((HostedCapability) c).getDeclaredCapability(); + HostedCapability hostedCapability = (HostedCapability) c; + Capability origCap = hostedCapability.getDeclaredCapability(); // Note that you might think we could remove the original cap // from the dependent map, but you can't since it may come from // a fragment that is attached to multiple hosts, so each host @@ -912,7 +934,7 @@ public ResolutionError prepare() { // If the original capability is from the host, then // we just need to replace it in the shadow list. - getShadowList(r).replace(origCap, c); + m_candidateMap.get(r).replaceHostedCapability(hostedCapability); } else { @@ -921,7 +943,7 @@ public ResolutionError prepare() // shadow copy of the list accordingly. getShadowList(r).insertHostedCapability( m_session.getContext(), - (HostedCapability) c, + hostedCapability, new SimpleHostedCapability( hostResource.getDeclaredResource(), origCap)); diff --git a/bundles/org.eclipse.osgi/felix/src/org/apache/felix/resolver/util/CandidateSelector.java b/bundles/org.eclipse.osgi/felix/src/org/apache/felix/resolver/util/CandidateSelector.java index de8a5f502d2..139c245ef59 100644 --- a/bundles/org.eclipse.osgi/felix/src/org/apache/felix/resolver/util/CandidateSelector.java +++ b/bundles/org.eclipse.osgi/felix/src/org/apache/felix/resolver/util/CandidateSelector.java @@ -22,8 +22,8 @@ import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; - import org.osgi.resource.Capability; +import org.osgi.service.resolver.HostedCapability; public class CandidateSelector { protected final AtomicBoolean isUnmodifiable; @@ -82,9 +82,22 @@ public int remove(Capability cap) { return index; } + public void replaceHostedCapability(HostedCapability c) { + checkModifiable(); + Capability origCap = c.getDeclaredCapability(); + int idx = unmodifiable.indexOf(origCap); + if (idx < 0) { + return; + } + unmodifiable.set(idx, new ShadowedCapability(c, origCap)); + } + protected void checkModifiable() { if (isUnmodifiable.get()) { throw new IllegalStateException("Trying to mutate after candidates have been prepared."); } + if (currentIndex > 0) { + throw new IllegalStateException("Trying to mutate after candidates have been removed already."); + } } } diff --git a/bundles/org.eclipse.osgi/felix/src/org/apache/felix/resolver/util/ShadowList.java b/bundles/org.eclipse.osgi/felix/src/org/apache/felix/resolver/util/ShadowList.java index a91ba30ee93..b742bb4ae78 100644 --- a/bundles/org.eclipse.osgi/felix/src/org/apache/felix/resolver/util/ShadowList.java +++ b/bundles/org.eclipse.osgi/felix/src/org/apache/felix/resolver/util/ShadowList.java @@ -64,13 +64,19 @@ public void insertHostedCapability(ResolveContext context, HostedCapability wrap m_original.remove(removeIdx); unmodifiable.remove(removeIdx); } - int insertIdx = context.insertHostedCapability(m_original, toInsertCapability); + List copy = new ArrayList<>(m_original); // make a copy here + for (int i = 0; i < copy.size(); i++) { + Capability capability = copy.get(i); + if (capability instanceof ShadowedCapability) { + // we unwrap the ShadowedCapability here as we always must pass only what we + // have got from the ResolveContext#findProviders. + copy.set(i, ((ShadowedCapability) capability).getShadowed()); + } + } + int insertIdx = context.insertHostedCapability(copy, toInsertCapability); + // now insert at the given position into our internal data structure unmodifiable.add(insertIdx, wrappedCapability); + m_original.add(insertIdx, toInsertCapability); } - public void replace(Capability origCap, Capability c) { - checkModifiable(); - int idx = unmodifiable.indexOf(origCap); - unmodifiable.set(idx, c); - } } \ No newline at end of file diff --git a/bundles/org.eclipse.osgi/felix/src/org/apache/felix/resolver/util/ShadowedCapability.java b/bundles/org.eclipse.osgi/felix/src/org/apache/felix/resolver/util/ShadowedCapability.java new file mode 100644 index 00000000000..07f7bccf3fb --- /dev/null +++ b/bundles/org.eclipse.osgi/felix/src/org/apache/felix/resolver/util/ShadowedCapability.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.felix.resolver.util; + +import java.util.Map; +import java.util.Objects; +import org.osgi.resource.Capability; +import org.osgi.resource.Resource; +import org.osgi.service.resolver.HostedCapability; + +/** + * A {@link Capability} that is shadowed by a {@link HostedCapability}, this is + * done when we merge the fragments capabilities with its host, but to insert + * {@link HostedCapability} we need to make sure we always pass the original so + * this class keeps track of the fact that we have replaced it with something + * else. + */ +class ShadowedCapability implements HostedCapability { + + private HostedCapability hosted; + private int hashCode; + private Capability shadowed; + + public ShadowedCapability(HostedCapability hosted, Capability shadowed) { + this.hosted = hosted; + this.shadowed = shadowed; + } + + @Override + public String getNamespace() { + return hosted.getNamespace(); + } + + @Override + public Map getDirectives() { + return hosted.getDirectives(); + } + + @Override + public Map getAttributes() { + return hosted.getAttributes(); + } + + @Override + public Resource getResource() { + return hosted.getResource(); + } + + @Override + public Capability getDeclaredCapability() { + return hosted.getDeclaredCapability(); + } + + public Capability getShadowed() { + return shadowed; + } + + @Override + public int hashCode() { + if (hashCode != 0) { + return hashCode; + } + return hashCode = Objects.hash(getNamespace(), getDirectives(), getAttributes(), getResource()); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof Capability) { + Capability other = (Capability) obj; + return Objects.equals(getNamespace(), other.getNamespace()) + && Objects.equals(getDirectives(), other.getDirectives()) + && Objects.equals(getAttributes(), other.getAttributes()) + && Objects.equals(getResource(), other.getResource()); + } + return false; + } + +}