1 /*******************************************************************************
2  * Copyright (c) 2011, 2015 VMware Inc.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *   VMware Inc. - initial contribution
13  *******************************************************************************/
14 
15 package org.eclipse.equinox.internal.region.hook;
16 
17 import java.util.Collection;
18 import java.util.Iterator;
19 import org.eclipse.equinox.internal.region.EquinoxStateHelper;
20 import org.eclipse.equinox.region.*;
21 import org.osgi.framework.Bundle;
22 import org.osgi.framework.hooks.resolver.ResolverHook;
23 import org.osgi.framework.wiring.*;
24 
25 /**
26  * {@link RegionResolverHook} manages the visibility of bundles across regions according to the {@link RegionDigraph}.
27  * <p />
28  *
29  * <strong>Concurrent Semantics</strong><br />
30  * Thread safe.
31  */
32 public final class RegionResolverHook implements ResolverHook {
33 
34 	private static final Boolean DEBUG = false;
35 
36 	private final RegionDigraph regionDigraph;
37 
RegionResolverHook(RegionDigraph regionDigraph)38 	public RegionResolverHook(RegionDigraph regionDigraph) {
39 		this.regionDigraph = regionDigraph;
40 	}
41 
42 	@Override
filterMatches(BundleRequirement requirement, Collection<BundleCapability> candidates)43 	public void filterMatches(BundleRequirement requirement, Collection<BundleCapability> candidates) {
44 		filterCandidates(requirement.getRevision(), candidates, false);
45 	}
46 
filterCandidates(BundleRevision requirer, Collection<BundleCapability> candidates, boolean singleton)47 	private void filterCandidates(BundleRevision requirer, Collection<BundleCapability> candidates, boolean singleton) {
48 		try {
49 			if (DEBUG) {
50 				debugEntry(requirer, candidates, singleton);
51 			}
52 
53 			if (getBundleId(requirer) == 0L) {
54 				return;
55 			}
56 
57 			Region requirerRegion = getRegion(requirer);
58 			if (requirerRegion == null) {
59 				// for singleton check; keep all collisions
60 				if (!singleton) {
61 					candidates.clear();
62 				}
63 				return;
64 			}
65 
66 			Visitor visitor = new Visitor(candidates);
67 			requirerRegion.visitSubgraph(visitor);
68 			Collection<BundleCapability> allowed = visitor.getAllowed();
69 
70 			candidates.retainAll(allowed);
71 		} finally {
72 			if (DEBUG) {
73 				debugExit(requirer, candidates);
74 			}
75 		}
76 	}
77 
78 	class Visitor extends RegionDigraphVisitorBase<BundleCapability> {
79 
Visitor(Collection<BundleCapability> candidates)80 		Visitor(Collection<BundleCapability> candidates) {
81 			super(candidates);
82 		}
83 
84 		@Override
contains(Region region, BundleCapability candidate)85 		protected boolean contains(Region region, BundleCapability candidate) {
86 			return region.equals(getRegion(candidate.getRevision()));
87 		}
88 
89 		@Override
isAllowed(BundleCapability candidate, RegionFilter filter)90 		protected boolean isAllowed(BundleCapability candidate, RegionFilter filter) {
91 			return filter.isAllowed(candidate) || filter.isAllowed(candidate.getRevision());
92 		}
93 
94 	}
95 
getRegion(BundleRevision bundleRevision)96 	Region getRegion(BundleRevision bundleRevision) {
97 		Bundle bundle = bundleRevision.getBundle();
98 		if (bundle != null) {
99 			return getRegion(bundle);
100 		}
101 		Long bundleId = getBundleId(bundleRevision);
102 		return getRegion(bundleId);
103 	}
104 
getRegion(Long bundleId)105 	private Region getRegion(Long bundleId) {
106 		return this.regionDigraph.getRegion(bundleId);
107 	}
108 
getBundleId(BundleRevision bundleRevision)109 	private Long getBundleId(BundleRevision bundleRevision) {
110 		return EquinoxStateHelper.getBundleId(bundleRevision);
111 	}
112 
getRegion(Bundle bundle)113 	private Region getRegion(Bundle bundle) {
114 		return this.regionDigraph.getRegion(bundle);
115 	}
116 
117 	@Override
end()118 	public void end() {
119 		// do nothing
120 	}
121 
122 	@Override
filterResolvable(Collection<BundleRevision> candidates)123 	public void filterResolvable(Collection<BundleRevision> candidates) {
124 		// filter any revisions that have no region
125 		for (Iterator<BundleRevision> iCandidates = candidates.iterator(); iCandidates.hasNext();) {
126 			if (getRegion(iCandidates.next()) == null) {
127 				iCandidates.remove();
128 			}
129 		}
130 	}
131 
132 	@Override
filterSingletonCollisions(BundleCapability singleton, Collection<BundleCapability> collisionCandidates)133 	public void filterSingletonCollisions(BundleCapability singleton, Collection<BundleCapability> collisionCandidates) {
134 		filterCandidates(singleton.getRevision(), collisionCandidates, true);
135 	}
136 
debugEntry(BundleRevision requirer, Collection<BundleCapability> candidates, boolean singleton)137 	private void debugEntry(BundleRevision requirer, Collection<BundleCapability> candidates, boolean singleton) {
138 		System.out.println((singleton ? "Singleton" : "Requirer: ") + requirer.getSymbolicName() + "_" + requirer.getVersion() + "[" + getBundleId(requirer) + "]"); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
139 		System.out.println("  Candidates: "); //$NON-NLS-1$
140 		Iterator<BundleCapability> i = candidates.iterator();
141 		while (i.hasNext()) {
142 			BundleCapability c = i.next();
143 			String namespace = c.getNamespace();
144 			if (BundleRevision.PACKAGE_NAMESPACE.equals(namespace)) {
145 				BundleRevision providerRevision = c.getRevision();
146 				String pkg = (String) c.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE);
147 				System.out.println("    Package " + pkg + " from provider " + providerRevision.getSymbolicName() + "_" + providerRevision.getVersion() + "[" + getBundleId(providerRevision) + "]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
148 				if (pkg.equals("slow")) { //$NON-NLS-1$
149 					System.out.println(">>> put breakpoint here <<<"); //$NON-NLS-1$
150 				}
151 			} else {
152 				BundleRevision providerRevision = c.getRevision();
153 				System.out.println("    Bundle from provider " + providerRevision.getSymbolicName() + "_" + providerRevision.getVersion() + "[" + getBundleId(providerRevision) + "]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
154 			}
155 		}
156 	}
157 
debugExit(BundleRevision requirer, Collection<BundleCapability> candidates)158 	private void debugExit(BundleRevision requirer, Collection<BundleCapability> candidates) {
159 		System.out.println("  Filtered candidates: "); //$NON-NLS-1$
160 		Iterator<BundleCapability> i = candidates.iterator();
161 		while (i.hasNext()) {
162 			BundleCapability c = i.next();
163 			String namespace = c.getNamespace();
164 			if (BundleRevision.PACKAGE_NAMESPACE.equals(namespace)) {
165 				BundleRevision providerRevision = c.getRevision();
166 				String pkg = (String) c.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE);
167 				System.out.println("    Package " + pkg + " from provider " + providerRevision.getSymbolicName() + "_" + providerRevision.getVersion() + "[" + getBundleId(providerRevision) + "]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
168 				if (pkg.equals("slow")) { //$NON-NLS-1$
169 					System.out.println(">>> put breakpoint here <<<"); //$NON-NLS-1$
170 				}
171 			} else {
172 				BundleRevision providerRevision = c.getRevision();
173 				System.out.println("    Bundle from provider " + providerRevision.getSymbolicName() + "_" + providerRevision.getVersion() + "[" + getBundleId(providerRevision) + "]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
174 			}
175 		}
176 	}
177 }
178