Details
-
Bug
-
Status: Resolved
-
Major
-
Resolution: Fixed
-
resolver-1.10.1
-
None
Description
The fix in FELIX-5389 for ShadowList is not complete. There are still cases where the underlying ArrayList from the CandidateSelector is reused in the ShadowList when it is not intended. This leads to exceptions like the following:
java.lang.ArrayIndexOutOfBoundsException: -1
at java.util.ArrayList.elementData(ArrayList.java:418)
at java.util.ArrayList.set(ArrayList.java:446)
at org.apache.felix.resolver.util.ShadowList.replace(ShadowList.java:81)
at org.apache.felix.resolver.Candidates.prepare(Candidates.java:915)
at org.apache.felix.resolver.ResolverImpl.getInitialCandidates(ResolverImpl.java:502)
at org.apache.felix.resolver.ResolverImpl.doResolve(ResolverImpl.java:387)
at org.apache.felix.resolver.ResolverImpl.resolve(ResolverImpl.java:375)
at org.apache.felix.resolver.ResolverImpl.resolve(ResolverImpl.java:368)
This is happening when there are multiple hosts for a single fragment and the fragment has a requirement that is getting resolved to a candidate from another host resource that also has at least one fragment.
The issue comes up when replacing the capability from the host with a wrapped capability for the fragment requirement which is a payload requirement for more than one host. It should do this for each WrappedRequirement that is wrapping the payload requirement from the fragment to each host. The first WrappedRequirement has its candidates modified, but that changes the shared ArrayList of the candidates for each of the other WrappedRequirements for the other hosts. The fix for this is to ensure the ArrayList from a CandidateSelector is never shared with a ShadowList.
But once I fixed this I ran into another strange issue that ended up causing Uses constraint errors like this:
org.osgi.service.resolver.ResolutionException: Uses constraint violation. Unable to resolve resource host [osgi.identity; host] because it is exposed to package 'exporter' from resources exporter [osgi.identity; exporter] and exporter [osgi.identity; exporter] via two dependency chains.
Chain 1:
host [osgi.identity; host]
import: (osgi.wiring.package=exporter)
export: osgi.wiring.package: exporter
exporter [osgi.identity; exporter]
Chain 2:
host [osgi.identity; host]
import: (osgi.wiring.package=exporter)
export: osgi.wiring.package: exporter
exporter [osgi.identity; exporter]
This only happens if a fragment has more than one host and the fragment and host have a requirement for a the same package. Additionally the capability that satisfies the payload requirement must come from a host that also has at least one fragment. The issue here is how we are finding CandidateSelectors of depending requirements when we need to replace a capability with a wrapped capability. There is a loop in the Candidates.prepare method which is says "Copy candidates for fragment requirements to the host.". This loop basically copies the candidates from the original requirement from the fragment to a CandidateSelector for the WrappedRequirement for the host. But it also removes the original requirement from the dependents for the capability. If there are multiple hosts this becomes problematic because this dependency set is used to find CandidateSelectors that need to be modified when wrapping a host. But since we remove the original requirement from the fragment that means any other hosts the fragment is attached to will not be discovered and therefore will not have the original capability properly replaced with the WrappedCapability. That makes the consistency check fail and give this bogus message that make it look like there are 2 identical chains leading to the identical capability. But the two capabilities are different because one is wrapped and one is not.
The fix is to separate out the inner loop that "Copy candidates for fragment requirements to the host." to a outer loop that is done before the main loop over the hostResources. This way we properly setup all the CandidateSelector for the payload WrappedRequirements before actually modifying the CandidateSelector capabilities with the WrappedCapabilities.
Super long story short: Fragments are just horribly complex.