Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: b54a40ba22ea6d8e64264528a6611add07eacc32 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
/*******************************************************************************
 * Copyright (c) 2003, 2014 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.osgi.internal.loader.sources;

import java.io.IOException;
import java.net.URL;
import java.util.Collection;
import java.util.Enumeration;
import org.eclipse.osgi.container.ModuleRevision;
import org.eclipse.osgi.container.ModuleWiring;
import org.eclipse.osgi.framework.util.KeyedElement;
import org.eclipse.osgi.internal.framework.EquinoxBundle;
import org.eclipse.osgi.internal.framework.EquinoxContainer;
import org.eclipse.osgi.internal.loader.BundleLoader;
import org.eclipse.osgi.internal.loader.SystemBundleLoader;
import org.osgi.framework.Bundle;
import org.osgi.framework.ServiceFactory;
import org.osgi.service.packageadmin.PackageAdmin;

public abstract class PackageSource implements KeyedElement {
	protected final String id;

	public PackageSource(String id) {
		// others depend on the id being interned; see SingleSourcePackage.equals
		this.id = id.intern();
	}

	public String getId() {
		return id;
	}

	public abstract SingleSourcePackage[] getSuppliers();

	public boolean compare(KeyedElement other) {
		return id.equals(((PackageSource) other).getId());
	}

	public int getKeyHashCode() {
		return id.hashCode();
	}

	public Object getKey() {
		return id;
	}

	public boolean isNullSource() {
		return false;
	}

	public abstract Class<?> loadClass(String name) throws ClassNotFoundException;

	public abstract URL getResource(String name);

	public abstract Enumeration<URL> getResources(String name) throws IOException;

	//TODO See how this relates with FilteredSourcePackage. Overwriting or doing a double dispatch might be good.
	// This is intentionally lenient; we don't force all suppliers to match (only one)
	// it is better to get class cast exceptions in split package cases than miss an event
	public boolean hasCommonSource(PackageSource other) {
		if (other == null)
			return false;
		if (this == other)
			return true;
		SingleSourcePackage[] suppliers1 = getSuppliers();
		SingleSourcePackage[] suppliers2 = other.getSuppliers();
		if (suppliers1 == null || suppliers2 == null)
			return false;
		// This will return true if the specified source has at least one
		// of the suppliers of this source.
		for (int i = 0; i < suppliers1.length; i++)
			for (int j = 0; j < suppliers2.length; j++)
				if (suppliers2[j].equals(suppliers1[i]))
					return true;
		return false;
	}

	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();
		builder.append(id).append(" -> "); //$NON-NLS-1$
		SingleSourcePackage[] suppliers = getSuppliers();
		if (suppliers == null) {
			return builder.append(String.valueOf(null)).toString();
		}
		builder.append('[');
		for (int i = 0; i < suppliers.length; i++) {
			if (i > 0) {
				builder.append(',');
			}
			builder.append(suppliers[i].getLoader());
		}
		builder.append(']');
		return builder.toString();
	}

	public abstract Collection<String> listResources(String path, String filePattern);

	/**
	 * Used by ServiceReferenceImpl for isAssignableTo
	 * @param registrant Bundle registering service
	 * @param client Bundle desiring to use service
	 * @param className class name to use
	 * @param serviceClass class of original service object
	 * @param container the equinox container
	 * @return true if assignable given package wiring
	 */
	public static boolean isServiceAssignableTo(Bundle registrant, Bundle client, String className, Class<?> serviceClass, EquinoxContainer container) {
		// 1) if the registrant == client always return true
		if (registrant == client) {
			return true;
		}
		// 2) get the package name from the specified className
		String pkgName = BundleLoader.getPackageName(className);
		if (pkgName.startsWith("java.")) //$NON-NLS-1$
			return true;

		BundleLoader producerBL = getBundleLoader(registrant);
		if (producerBL == null)
			return false;
		BundleLoader consumerBL = getBundleLoader(client);
		if (consumerBL == null)
			return false;
		// 3) for the specified bundle, find the wiring for the package.  If no wiring is found return true
		PackageSource consumerSource = consumerBL.getPackageSource(pkgName);
		if (consumerSource == null)
			return true;
		// work around the issue when the package is in the EE and we delegate to boot for that package
		if (container.isBootDelegationPackage(pkgName)) {
			Bundle systemBundle = container.getStorage().getModuleContainer().getModule(0).getBundle();
			SystemBundleLoader systemLoader = (SystemBundleLoader) getBundleLoader(systemBundle);
			if (systemLoader.isExportedPackage(pkgName)) {
				return true; // in this case we have a common source from the EE
			}
		}
		// 4) For the registrant bundle, find the wiring for the package.
		PackageSource producerSource = producerBL.getPackageSource(pkgName);
		if (producerSource == null) {
			if (serviceClass != null && ServiceFactory.class.isAssignableFrom(serviceClass)) {
				@SuppressWarnings("deprecation")
				Bundle bundle = container.getPackageAdmin().getBundle(serviceClass);
				if (bundle != null && bundle != registrant)
					// in this case we have a wacky ServiceFactory that is doing something we cannot 
					// verify if it is correct.  Instead of failing we allow the assignment and hope for the best
					// bug 326918
					return true;
			}
			// 5) If no wiring is found for the registrant bundle then find the wiring for the classloader of the service object.  If no wiring is found return false.
			producerSource = getPackageSource(serviceClass, pkgName, container.getPackageAdmin());
			if (producerSource == null)
				return false;
		}
		// 6) If the two wirings found are equal then return true; otherwise return false.
		return producerSource.hasCommonSource(consumerSource);
	}

	private static PackageSource getPackageSource(Class<?> serviceClass, String pkgName, @SuppressWarnings("deprecation") PackageAdmin packageAdmin) {
		if (serviceClass == null)
			return null;
		@SuppressWarnings("deprecation")
		Bundle serviceBundle = packageAdmin.getBundle(serviceClass);
		if (serviceBundle == null)
			return null;
		BundleLoader producerBL = getBundleLoader(serviceBundle);
		if (producerBL == null)
			return null;
		PackageSource producerSource = producerBL.getPackageSource(pkgName);
		if (producerSource != null)
			return producerSource;
		// try the interfaces
		Class<?>[] interfaces = serviceClass.getInterfaces();
		// note that getInterfaces never returns null
		for (int i = 0; i < interfaces.length; i++) {
			producerSource = getPackageSource(interfaces[i], pkgName, packageAdmin);
			if (producerSource != null)
				return producerSource;
		}
		// try super class
		return getPackageSource(serviceClass.getSuperclass(), pkgName, packageAdmin);
	}

	private static BundleLoader getBundleLoader(Bundle bundle) {
		ModuleRevision producer = ((EquinoxBundle) bundle).getModule().getCurrentRevision();
		ModuleWiring producerWiring = producer.getWiring();
		return producerWiring == null ? null : (BundleLoader) producerWiring.getModuleLoader();
	}
}

Back to the top