1 /*******************************************************************************
2  * Copyright (c) 2003, 2017 IBM Corporation and others.
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  *     IBM Corporation - initial API and implementation
13  *******************************************************************************/
14 
15 package org.eclipse.osgi.internal.framework.legacy;
16 
17 import java.security.AccessController;
18 import java.security.PrivilegedAction;
19 import java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.Collection;
22 import java.util.Collections;
23 import java.util.Comparator;
24 import java.util.HashSet;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Set;
28 import org.eclipse.osgi.container.Module;
29 import org.eclipse.osgi.container.ModuleCapability;
30 import org.eclipse.osgi.container.ModuleContainer;
31 import org.eclipse.osgi.container.ModuleRevision;
32 import org.eclipse.osgi.container.ModuleWire;
33 import org.eclipse.osgi.container.ModuleWiring;
34 import org.eclipse.osgi.internal.container.Capabilities;
35 import org.eclipse.osgi.internal.container.InternalUtils;
36 import org.eclipse.osgi.internal.framework.EquinoxContainer;
37 import org.osgi.framework.Bundle;
38 import org.osgi.framework.Constants;
39 import org.osgi.framework.FrameworkUtil;
40 import org.osgi.framework.Version;
41 import org.osgi.framework.VersionRange;
42 import org.osgi.framework.namespace.BundleNamespace;
43 import org.osgi.framework.namespace.HostNamespace;
44 import org.osgi.framework.namespace.IdentityNamespace;
45 import org.osgi.framework.namespace.PackageNamespace;
46 import org.osgi.framework.wiring.BundleCapability;
47 import org.osgi.framework.wiring.BundleRevision;
48 import org.osgi.framework.wiring.BundleWire;
49 import org.osgi.framework.wiring.BundleWiring;
50 import org.osgi.resource.Namespace;
51 import org.osgi.resource.Requirement;
52 import org.osgi.service.packageadmin.ExportedPackage;
53 import org.osgi.service.packageadmin.PackageAdmin;
54 import org.osgi.service.packageadmin.RequiredBundle;
55 
56 @Deprecated
57 public class PackageAdminImpl implements PackageAdmin {
58 	private final ModuleContainer container;
59 
60 	/*
61 	 * We need to make sure that the GetBundleAction class loads early to prevent a ClassCircularityError when checking permissions.
62 	 * See bug 161561
63 	 */
64 	static {
65 		Class<?> c;
66 		c = GetBundleAction.class;
c.getName()67 		c.getName(); // to prevent compiler warnings
68 	}
69 
70 	static class GetBundleAction implements PrivilegedAction<Bundle> {
71 		private Class<?> clazz;
72 		private PackageAdminImpl impl;
73 
GetBundleAction(PackageAdminImpl impl, Class<?> clazz)74 		public GetBundleAction(PackageAdminImpl impl, Class<?> clazz) {
75 			this.impl = impl;
76 			this.clazz = clazz;
77 		}
78 
79 		@Override
run()80 		public Bundle run() {
81 			return impl.getBundlePriv(clazz);
82 		}
83 	}
84 
85 	/**
86 	 * Constructor.
87 	 *
88 	 * @param container the container
89 	 */
PackageAdminImpl(ModuleContainer container)90 	public PackageAdminImpl(ModuleContainer container) {
91 		this.container = container;
92 	}
93 
94 	@Override
getExportedPackages(Bundle bundle)95 	public ExportedPackage[] getExportedPackages(Bundle bundle) {
96 		if (bundle == null) {
97 			return getExportedPackages((String) null);
98 		}
99 		Module module = StartLevelImpl.getModule(bundle);
100 		Collection<ModuleRevision> revisions = module == null ? Collections.<ModuleRevision> emptyList() : module.getRevisions().getModuleRevisions();
101 
102 		Collection<ExportedPackage> allExports = new ArrayList<>();
103 		for (ModuleRevision revision : revisions) {
104 			ModuleWiring wiring = revision.getWiring();
105 			if (wiring != null) {
106 				List<ModuleCapability> providedPackages = wiring.getModuleCapabilities(PackageNamespace.PACKAGE_NAMESPACE);
107 				if (providedPackages != null) {
108 					for (ModuleCapability providedPackage : providedPackages) {
109 						allExports.add(new ExportedPackageImpl(providedPackage, wiring));
110 					}
111 				}
112 			}
113 		}
114 		return allExports.isEmpty() ? null : allExports.toArray(new ExportedPackage[allExports.size()]);
115 	}
116 
117 	@Override
getExportedPackage(String name)118 	public ExportedPackage getExportedPackage(String name) {
119 		ExportedPackage[] allExports = getExportedPackages(name);
120 		if (allExports == null)
121 			return null;
122 		ExportedPackage result = null;
123 		for (ExportedPackage allExport : allExports) {
124 			if (name.equals(allExport.getName())) {
125 				if (result == null) {
126 					result = allExport;
127 				} else {
128 					Version curVersion = result.getVersion();
129 					Version newVersion = allExport.getVersion();
130 					if (newVersion.compareTo(curVersion) >= 0) {
131 						result = allExport;
132 					}
133 				}
134 			}
135 		}
136 		return result;
137 	}
138 
139 	@Override
getExportedPackages(String name)140 	public ExportedPackage[] getExportedPackages(String name) {
141 		String filter = "(" + PackageNamespace.PACKAGE_NAMESPACE + "=" + (name == null ? "*" : name) + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$//$NON-NLS-4$
142 		Map<String, String> directives = Collections.<String, String> singletonMap(Namespace.REQUIREMENT_FILTER_DIRECTIVE, filter);
143 		Map<String, Boolean> attributes = Collections.singletonMap(Capabilities.SYNTHETIC_REQUIREMENT, Boolean.TRUE);
144 		Requirement packageReq = ModuleContainer.createRequirement(PackageNamespace.PACKAGE_NAMESPACE, directives, attributes);
145 		Collection<BundleCapability> packageCaps = container.getFrameworkWiring().findProviders(packageReq);
146 		InternalUtils.filterCapabilityPermissions(packageCaps);
147 		List<ExportedPackage> result = new ArrayList<>();
148 		for (BundleCapability capability : packageCaps) {
149 			ModuleWiring wiring = (ModuleWiring) capability.getRevision().getWiring();
150 			if (wiring != null) {
151 				Collection<ModuleWiring> wirings = Collections.emptyList();
152 				if ((capability.getRevision().getTypes() & BundleRevision.TYPE_FRAGMENT) != 0) {
153 					// This is a fragment, just get all the host wirings
154 					List<ModuleWire> hostWires = wiring.getRequiredModuleWires(HostNamespace.HOST_NAMESPACE);
155 					if (hostWires != null && !hostWires.isEmpty()) {
156 						wirings = new ArrayList<>(hostWires.size());
157 						for (ModuleWire hostWire : hostWires) {
158 							ModuleWiring hostWiring = hostWire.getProviderWiring();
159 							if (hostWiring != null) {
160 								wirings.add(hostWiring);
161 							}
162 						}
163 					}
164 				} else {
165 					// just a single host wiring
166 					wirings = Collections.singletonList(wiring);
167 				}
168 				for (ModuleWiring moduleWiring : wirings) {
169 					if (!moduleWiring.getSubstitutedNames().contains(capability.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE))) {
170 						result.add(new ExportedPackageImpl((ModuleCapability) capability, moduleWiring));
171 					}
172 				}
173 			}
174 		}
175 		return (result.size() == 0 ? null : result.toArray(new ExportedPackage[result.size()]));
176 	}
177 
178 	@Override
refreshPackages(Bundle[] input)179 	public void refreshPackages(Bundle[] input) {
180 		container.getFrameworkWiring().refreshBundles(input == null ? null : Arrays.asList(input));
181 	}
182 
183 	@Override
resolveBundles(Bundle[] input)184 	public boolean resolveBundles(Bundle[] input) {
185 		return container.getFrameworkWiring().resolveBundles(input == null ? null : Arrays.asList(input));
186 	}
187 
188 	@Override
getRequiredBundles(String symbolicName)189 	public RequiredBundle[] getRequiredBundles(String symbolicName) {
190 		String filter = "(" + BundleNamespace.BUNDLE_NAMESPACE + "=" + (symbolicName == null ? "*" : symbolicName) + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$//$NON-NLS-4$
191 		Map<String, String> directives = Collections.<String, String> singletonMap(Namespace.REQUIREMENT_FILTER_DIRECTIVE, filter);
192 		Map<String, Boolean> attributes = Collections.singletonMap(Capabilities.SYNTHETIC_REQUIREMENT, Boolean.TRUE);
193 		Requirement bundleReq = ModuleContainer.createRequirement(BundleNamespace.BUNDLE_NAMESPACE, directives, attributes);
194 		Collection<BundleCapability> bundleCaps = container.getFrameworkWiring().findProviders(bundleReq);
195 		InternalUtils.filterCapabilityPermissions(bundleCaps);
196 		Collection<RequiredBundle> result = new ArrayList<>();
197 		for (BundleCapability capability : bundleCaps) {
198 			BundleWiring wiring = capability.getRevision().getWiring();
199 			if (wiring != null) {
200 				result.add(new RequiredBundleImpl(capability, wiring));
201 			}
202 		}
203 		return result.isEmpty() ? null : result.toArray(new RequiredBundle[result.size()]);
204 	}
205 
206 	@Override
getBundles(String symbolicName, String versionRange)207 	public Bundle[] getBundles(String symbolicName, String versionRange) {
208 		if (symbolicName == null) {
209 			throw new IllegalArgumentException();
210 		}
211 		if (Constants.SYSTEM_BUNDLE_SYMBOLICNAME.equals(symbolicName)) {
212 			// need to alias system.bundle to the implementation BSN
213 			symbolicName = EquinoxContainer.NAME;
214 		}
215 		VersionRange range = versionRange == null ? null : new VersionRange(versionRange);
216 		String filter = (range != null ? "(&" : "") + "(" + IdentityNamespace.IDENTITY_NAMESPACE + "=" + symbolicName + ")" + (range != null ? range.toFilterString(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE) + ")" : ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$
217 		Requirement identityReq = ModuleContainer.createRequirement(IdentityNamespace.IDENTITY_NAMESPACE, Collections.<String, String> singletonMap(Namespace.REQUIREMENT_FILTER_DIRECTIVE, filter), Collections.<String, Object> emptyMap());
218 		Collection<BundleCapability> identityCaps = container.getFrameworkWiring().findProviders(identityReq);
219 
220 		if (identityCaps.isEmpty()) {
221 			return null;
222 		}
223 		List<Bundle> sorted = new ArrayList<>(identityCaps.size());
224 		for (BundleCapability capability : identityCaps) {
225 			Bundle b = capability.getRevision().getBundle();
226 			// a sanity check incase this is an old revision
227 			if (symbolicName.equals(b.getSymbolicName()) && !sorted.contains(b)) {
228 				sorted.add(b);
229 			}
230 		}
231 		Collections.sort(sorted, new Comparator<Bundle>() {
232 			@Override
233 			public int compare(Bundle b1, Bundle b2) {
234 				return b2.getVersion().compareTo(b1.getVersion());
235 			}
236 		});
237 
238 		if (sorted.isEmpty()) {
239 			return null;
240 		}
241 
242 		return sorted.toArray(new Bundle[sorted.size()]);
243 	}
244 
245 	@Override
getFragments(Bundle bundle)246 	public Bundle[] getFragments(Bundle bundle) {
247 		ModuleWiring wiring = getWiring(bundle);
248 		if (wiring == null) {
249 			return null;
250 		}
251 		List<ModuleWire> hostWires = wiring.getProvidedModuleWires(HostNamespace.HOST_NAMESPACE);
252 		if (hostWires == null) {
253 			// we don't hold locks while checking the graph, just return if no longer valid
254 			return null;
255 		}
256 		Collection<Bundle> fragments = new ArrayList<>(hostWires.size());
257 		for (ModuleWire wire : hostWires) {
258 			Bundle fragment = wire.getRequirer().getBundle();
259 			if (fragment != null) {
260 				fragments.add(fragment);
261 			}
262 		}
263 		return fragments.isEmpty() ? null : fragments.toArray(new Bundle[fragments.size()]);
264 	}
265 
266 	@Override
getHosts(Bundle bundle)267 	public Bundle[] getHosts(Bundle bundle) {
268 		ModuleWiring wiring = getWiring(bundle);
269 		if (wiring == null) {
270 			return null;
271 		}
272 		List<ModuleWire> hostWires = wiring.getRequiredModuleWires(HostNamespace.HOST_NAMESPACE);
273 		if (hostWires == null) {
274 			// we don't hold locks while checking the graph, just return if no longer valid
275 			return null;
276 		}
277 		Collection<Bundle> hosts = new ArrayList<>(hostWires.size());
278 		for (ModuleWire wire : hostWires) {
279 			Bundle host = wire.getProvider().getBundle();
280 			if (host != null) {
281 				hosts.add(host);
282 			}
283 		}
284 		return hosts.isEmpty() ? null : hosts.toArray(new Bundle[hosts.size()]);
285 	}
286 
getWiring(Bundle bundle)287 	private ModuleWiring getWiring(Bundle bundle) {
288 		Module module = StartLevelImpl.getModule(bundle);
289 		if (module == null) {
290 			return null;
291 		}
292 
293 		List<ModuleRevision> revisions = module.getRevisions().getModuleRevisions();
294 		if (revisions.isEmpty()) {
295 			return null;
296 		}
297 
298 		return revisions.get(0).getWiring();
299 	}
300 
getBundlePriv(Class<?> clazz)301 	Bundle getBundlePriv(Class<?> clazz) {
302 		Bundle b = FrameworkUtil.getBundle(clazz);
303 		if (b == null && clazz.getClassLoader() == getClass().getClassLoader()) {
304 			return container.getModule(0).getBundle();
305 		}
306 		return b;
307 	}
308 
309 	@Override
getBundle(final Class<?> clazz)310 	public Bundle getBundle(final Class<?> clazz) {
311 		if (System.getSecurityManager() == null)
312 			return getBundlePriv(clazz);
313 		return AccessController.doPrivileged(new GetBundleAction(this, clazz));
314 	}
315 
316 	@Override
getBundleType(Bundle bundle)317 	public int getBundleType(Bundle bundle) {
318 		Module module = StartLevelImpl.getModule(bundle);
319 		if (module == null) {
320 			return 0;
321 		}
322 		List<BundleRevision> revisions = module.getRevisions().getRevisions();
323 		if (revisions.isEmpty()) {
324 			return 0;
325 		}
326 		return (revisions.get(0).getTypes() & BundleRevision.TYPE_FRAGMENT) != 0 ? PackageAdmin.BUNDLE_TYPE_FRAGMENT : 0;
327 	}
328 
getRemovalPendingBundles()329 	public Collection<Bundle> getRemovalPendingBundles() {
330 		return container.getFrameworkWiring().getRemovalPendingBundles();
331 	}
332 
getDependencyClosure(Collection<Bundle> bundles)333 	public Collection<Bundle> getDependencyClosure(Collection<Bundle> bundles) {
334 		return container.getFrameworkWiring().getDependencyClosure(bundles);
335 	}
336 
337 	static class ExportedPackageImpl implements ExportedPackage {
338 
339 		private final ModuleCapability packageCapability;
340 		private final ModuleWiring providerWiring;
341 
ExportedPackageImpl(ModuleCapability packageCapability, ModuleWiring providerWiring)342 		public ExportedPackageImpl(ModuleCapability packageCapability, ModuleWiring providerWiring) {
343 			this.packageCapability = packageCapability;
344 			this.providerWiring = providerWiring;
345 		}
346 
347 		@Override
getName()348 		public String getName() {
349 			return (String) packageCapability.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE);
350 		}
351 
352 		@Override
getExportingBundle()353 		public Bundle getExportingBundle() {
354 			if (!providerWiring.isInUse())
355 				return null;
356 			return providerWiring.getBundle();
357 		}
358 
359 		@Override
getImportingBundles()360 		public Bundle[] getImportingBundles() {
361 			if (!providerWiring.isInUse()) {
362 				return null;
363 			}
364 			Set<Bundle> importing = new HashSet<>();
365 
366 			String packageName = getName();
367 			addRequirers(importing, providerWiring, packageName);
368 
369 			List<ModuleWire> providedPackages = providerWiring.getProvidedModuleWires(PackageNamespace.PACKAGE_NAMESPACE);
370 			if (providedPackages == null) {
371 				// we don't hold locks while checking the graph, just return if no longer valid
372 				return null;
373 			}
374 			for (ModuleWire packageWire : providedPackages) {
375 				if (packageCapability.equals(packageWire.getCapability())) {
376 					importing.add(packageWire.getRequirer().getBundle());
377 					if (packageWire.getRequirerWiring().isSubstitutedPackage(packageName)) {
378 						addRequirers(importing, packageWire.getRequirerWiring(), packageName);
379 					}
380 				}
381 			}
382 			return importing.toArray(new Bundle[importing.size()]);
383 		}
384 
addRequirers(Set<Bundle> importing, ModuleWiring wiring, String packageName)385 		private static void addRequirers(Set<Bundle> importing, ModuleWiring wiring, String packageName) {
386 			List<ModuleWire> requirerWires = wiring.getProvidedModuleWires(BundleNamespace.BUNDLE_NAMESPACE);
387 			if (requirerWires == null) {
388 				// we don't hold locks while checking the graph, just return if no longer isInUse
389 				return;
390 			}
391 			for (ModuleWire requireBundleWire : requirerWires) {
392 				Bundle requirer = requireBundleWire.getRequirer().getBundle();
393 				if (importing.contains(requirer)) {
394 					continue;
395 				}
396 				importing.add(requirer);
397 
398 				// if reexported then need to add any requirers of the reexporter
399 				String reExport = requireBundleWire.getRequirement().getDirectives().get(BundleNamespace.REQUIREMENT_VISIBILITY_DIRECTIVE);
400 				ModuleWiring requirerWiring = requireBundleWire.getRequirerWiring();
401 				if (BundleNamespace.VISIBILITY_REEXPORT.equals(reExport)) {
402 					addRequirers(importing, requirerWiring, packageName);
403 				}
404 				// also need to add any importers of the same package as the wiring exports; case of aggregations
405 				if (!requirerWiring.equals(wiring)) {
406 					List<ModuleWire> providedPackages = requirerWiring.getProvidedModuleWires(PackageNamespace.PACKAGE_NAMESPACE);
407 					if (providedPackages != null) {
408 						for (ModuleWire packageWire : providedPackages) {
409 							if (packageName.equals(packageWire.getCapability().getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE))) {
410 								importing.add(packageWire.getRequirer().getBundle());
411 								if (packageWire.getRequirerWiring().isSubstitutedPackage(packageName)) {
412 									addRequirers(importing, packageWire.getRequirerWiring(), packageName);
413 								}
414 							}
415 						}
416 					}
417 				}
418 			}
419 		}
420 
421 		/**
422 		 * @deprecated
423 		 */
424 		@Override
getSpecificationVersion()425 		public String getSpecificationVersion() {
426 			return getVersion().toString();
427 		}
428 
429 		@Override
getVersion()430 		public Version getVersion() {
431 			Version version = (Version) packageCapability.getAttributes().get(PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE);
432 			return version == null ? Version.emptyVersion : version;
433 		}
434 
435 		@Override
isRemovalPending()436 		public boolean isRemovalPending() {
437 			return !providerWiring.isCurrent();
438 		}
439 
440 		@Override
toString()441 		public String toString() {
442 			return packageCapability.toString();
443 		}
444 	}
445 
446 	private static class RequiredBundleImpl implements RequiredBundle {
447 		private final BundleCapability bundleCapability;
448 		private final BundleWiring providerWiring;
449 
RequiredBundleImpl(BundleCapability bundleCapability, BundleWiring providerWiring)450 		public RequiredBundleImpl(BundleCapability bundleCapability, BundleWiring providerWiring) {
451 			this.bundleCapability = bundleCapability;
452 			this.providerWiring = providerWiring;
453 		}
454 
455 		@Override
getSymbolicName()456 		public String getSymbolicName() {
457 			return (String) bundleCapability.getAttributes().get(BundleNamespace.BUNDLE_NAMESPACE);
458 		}
459 
460 		@Override
getBundle()461 		public Bundle getBundle() {
462 			if (!providerWiring.isInUse())
463 				return null;
464 			return providerWiring.getBundle();
465 		}
466 
467 		@Override
getRequiringBundles()468 		public Bundle[] getRequiringBundles() {
469 			if (!providerWiring.isInUse()) {
470 				return null;
471 			}
472 			Set<Bundle> requiring = new HashSet<>();
473 
474 			addRequirers(requiring, providerWiring);
475 
476 			return requiring.toArray(new Bundle[requiring.size()]);
477 		}
478 
addRequirers(Set<Bundle> requiring, BundleWiring providerWiring)479 		private static void addRequirers(Set<Bundle> requiring, BundleWiring providerWiring) {
480 			List<BundleWire> requirerWires = providerWiring.getProvidedWires(BundleNamespace.BUNDLE_NAMESPACE);
481 			if (requirerWires == null) {
482 				// we don't hold locks while checking the graph, just return if no longer isInUse
483 				return;
484 			}
485 			for (BundleWire requireBundleWire : requirerWires) {
486 				Bundle requirer = requireBundleWire.getRequirer().getBundle();
487 				if (requiring.contains(requirer)) {
488 					continue;
489 				}
490 				requiring.add(requirer);
491 				String reExport = requireBundleWire.getRequirement().getDirectives().get(BundleNamespace.REQUIREMENT_VISIBILITY_DIRECTIVE);
492 				if (BundleNamespace.VISIBILITY_REEXPORT.equals(reExport)) {
493 					addRequirers(requiring, requireBundleWire.getRequirerWiring());
494 				}
495 			}
496 		}
497 
498 		@Override
getVersion()499 		public Version getVersion() {
500 			Version version = (Version) bundleCapability.getAttributes().get(PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE);
501 			return version == null ? Version.emptyVersion : version;
502 		}
503 
504 		@Override
isRemovalPending()505 		public boolean isRemovalPending() {
506 			return !providerWiring.isCurrent();
507 		}
508 
509 		@Override
toString()510 		public String toString() {
511 			return bundleCapability.toString();
512 		}
513 	}
514 }
515