Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 887cb785c112f51b400164044b822c291d081bdb (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
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
/*
 * Copyright (c) OSGi Alliance (2011, 2016). All Rights Reserved.
 *
 * Licensed 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.osgi.service.resolver;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CancellationException;

import org.osgi.annotation.versioning.ConsumerType;
import org.osgi.framework.namespace.BundleNamespace;
import org.osgi.framework.namespace.HostNamespace;
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;

/**
 * A resolve context provides resources, options and constraints to the
 * potential solution of a {@link Resolver#resolve(ResolveContext) resolve}
 * operation.
 * <p>
 * Resolve Contexts:
 * <ul>
 * <li>Specify the mandatory and optional resources to resolve. The mandatory
 * and optional resources must be consistent and correct. For example, they must
 * not violate the singleton policy of the implementer.</li>
 * <li>Provide {@link Capability capabilities} that the Resolver can use to
 * satisfy {@link Requirement requirements} via the
 * {@link #findProviders(Requirement)} method</li>
 * <li>Constrain solutions via the {@link #getWirings()} method. A wiring
 * consists of a map of existing {@link Resource resources} to {@link Wiring
 * wiring}.</li>
 * <li>Filter requirements that are part of a resolve operation via the
 * {@link #isEffective(Requirement)}.</li>
 * </ul>
 * <p>
 * A resolver may call the methods on the resolve context any number of times
 * during a resolve operation using any thread. Implementors should ensure that
 * this class is properly thread safe.
 * <p>
 * Except for {@link #insertHostedCapability(List, HostedCapability)} and
 * {@link #onCancel(Runnable)}, the resolve context methods must be
 * <i>idempotent</i>. This means that resources must have constant capabilities
 * and requirements and the resolve context must return a consistent set of
 * capabilities, wires and effective requirements.
 * 
 * @ThreadSafe
 * @author $Id$
 */
@ConsumerType
public abstract class ResolveContext {
	/**
	 * Return the resources that must be resolved for this resolve context.
	 * 
	 * <p>
	 * The default implementation returns an empty collection.
	 * 
	 * @return A collection of the resources that must be resolved for this
	 *         resolve context. May be empty if there are no mandatory
	 *         resources. The returned collection may be unmodifiable.
	 */
	public Collection<Resource> getMandatoryResources() {
		return emptyCollection();
	}

	/**
	 * Return the resources that the resolver should attempt to resolve for this
	 * resolve context. Inability to resolve one of the specified resources will
	 * not result in a resolution exception.
	 * 
	 * <p>
	 * The default implementation returns an empty collection.
	 * 
	 * @return A collection of the resources that the resolver should attempt to
	 *         resolve for this resolve context. May be empty if there are no
	 *         optional resources. The returned collection may be unmodifiable.
	 */
	public Collection<Resource> getOptionalResources() {
		return emptyCollection();
	}

	@SuppressWarnings("unchecked")
	private static <T> Collection<T> emptyCollection() {
		return Collections.EMPTY_LIST;
	}

	/**
	 * Find Capabilities that match the given Requirement.
	 * <p>
	 * The returned list contains {@link Capability} objects where the Resource
	 * must be the declared Resource of the Capability. The Resolver can then
	 * add additional {@link HostedCapability} objects with the
	 * {@link #insertHostedCapability(List, HostedCapability)} method when it,
	 * for example, attaches fragments. Those {@link HostedCapability} objects
	 * will then use the host's Resource which likely differs from the declared
	 * Resource of the corresponding Capability.
	 * 
	 * <p>
	 * The returned list is in priority order such that the Capabilities with a
	 * lower index have a preference over those with a higher index. The
	 * resolver must use the
	 * {@link #insertHostedCapability(List, HostedCapability)} method to add
	 * additional Capabilities to maintain priority order. In general, this is
	 * necessary when the Resolver uses Capabilities declared in a Resource but
	 * that must originate from an attached host.
	 * 
	 * <p>
	 * Each returned Capability must match the given Requirement. This means
	 * that the filter in the Requirement must match as well as any namespace
	 * specific directives. For example, the mandatory attributes for the
	 * {@code osgi.wiring.package} namespace.
	 * 
	 * @param requirement The requirement that a resolver is attempting to
	 *        satisfy. Must not be {@code null}.
	 * @return A list of {@link Capability} objects that match the specified
	 *         requirement.
	 */
	public abstract List<Capability> findProviders(Requirement requirement);

	/**
	 * Add a {@link HostedCapability} to the list of capabilities returned from
	 * {@link #findProviders(Requirement)}.
	 * 
	 * <p>
	 * This method is used by the {@link Resolver} to add Capabilities that are
	 * hosted by another Resource to the list of Capabilities returned from
	 * {@link #findProviders(Requirement)}. This function is necessary to allow
	 * fragments to attach to hosts, thereby changing the origin of a
	 * Capability. This method must insert the specified HostedCapability in a
	 * place that makes the list maintain the preference order. It must return
	 * the index in the list of the inserted {@link HostedCapability}.
	 * 
	 * @param capabilities The list returned from
	 *        {@link #findProviders(Requirement)}. Must not be {@code null}.
	 * @param hostedCapability The HostedCapability to insert in the specified
	 *        list. Must not be {@code null}.
	 * @return The index in the list of the inserted HostedCapability.
	 * 
	 */
	public abstract int insertHostedCapability(List<Capability> capabilities, HostedCapability hostedCapability);

	/**
	 * Test if a given requirement should be wired in the resolve operation. If
	 * this method returns {@code false}, then the resolver should ignore this
	 * requirement during the resolve operation.
	 * 
	 * <p>
	 * The primary use case for this is to test the {@code effective} directive
	 * on the requirement, though implementations are free to use any effective
	 * test.
	 * 
	 * @param requirement The Requirement to test. Must not be {@code null}.
	 * @return {@code true} if the requirement should be considered as part of
	 *         the resolve operation.
	 */
	public abstract boolean isEffective(Requirement requirement);

	/**
	 * Returns the wirings for existing resolved resources.
	 * 
	 * <p>
	 * For example, if this resolve context is for an OSGi framework, then the
	 * result would contain all the currently resolved bundles with each
	 * bundle's current wiring.
	 * 
	 * <p>
	 * Multiple calls to this method for this resolve context must return the
	 * same result.
	 * 
	 * @return The wirings for existing resolved resources. The returned map is
	 *         unmodifiable.
	 */
	public abstract Map<Resource, Wiring> getWirings();

	/**
	 * Find resources that are related to the given resource.
	 * <p>
	 * The resolver attempts to resolve related resources during the current
	 * resolve operation. Failing to resolve one of the related resources will
	 * not result in a resolution exception unless the related resource is also
	 * a {@link #getMandatoryResources() mandatory} resource.
	 * <p>
	 * The resolve context is asked to return related resources for each
	 * resource that is pulled into a resolve operation. This includes the
	 * {@link #getMandatoryResources() mandatory} and
	 * {@link #getOptionalResources() optional} resources and each related
	 * resource returned by this method.
	 * <p>
	 * For example, a fragment can be considered a related resource for a host
	 * bundle. When a host is being resolved the resolve context will be asked
	 * if any related resources should be added to the resolve operation. The
	 * resolve context may decide that the potential fragments of the host
	 * should be resolved along with the host.
	 * 
	 * @param resource The Resource that a resolver is attempting to find
	 *            related resources for. Must not be {@code null}.
	 * @return A collection of the resources that the resolver should attempt to
	 *         resolve for this resolve context. May be empty if there are no
	 *         related resources. The returned collection may be unmodifiable.
	 * @since 1.1
	 */
	public Collection<Resource> findRelatedResources(Resource resource) {
		return Collections.emptyList();
	}

	/**
	 * Registers a callback with the resolve context that is associated with the
	 * currently running resolve operation. The callback can be executed in
	 * order to cancel the currently running resolve operation.
	 * <p>
	 * When a resolve operation begins, the resolver must call this method once
	 * and only once for the duration of the resolve operation and that call
	 * must happen before calling any other method on this resolve context. If
	 * the specified callback is executed then the resolver must cancel the
	 * currently running resolve operation and throw a
	 * {@link ResolutionException} with a cause of type
	 * {@link CancellationException}.
	 * <p>
	 * The callback allows a resolve context to cancel a long running resolve
	 * operation that appears to be running endlessly or at risk of running out
	 * of resources. The resolve context may then decide to give up on resolve
	 * operation or attempt to try another resolve operation with a smaller set
	 * of resources which may allow the resolve operation to complete normally.
	 * 
	 * @param callback the callback to execute in order to cancel the resolve
	 *            operation. Must not be {@code null}.
	 * @throws IllegalStateException if the resolver attempts to register more
	 *             than one callback for a resolve operation
	 * @since 1.1
	 */
	public void onCancel(Runnable callback) {
		// do nothing by default
	}

	/**
	 * Returns the subset of {@link Wiring#getRequiredResourceWires(String)
	 * required} wires that provide wires to {@link Capability capabilities}
	 * which substitute capabilities of the wiring. For example, when a
	 * {@link PackageNamespace package} name is both provided and required by
	 * the same resource. If the package requirement is resolved to a capability
	 * provided by a different wiring then the package capability is considered
	 * to be substituted.
	 * <p>
	 * The resolver asks the resolve context to return substitution wires for
	 * each wiring that {@link Wiring#getResourceCapabilities(String) provides}
	 * a {@link BundleNamespace bundle} namespace capability that is used to
	 * resolve one or more bundle requirements.
	 * <p>
	 * Note that this method searches all the {@link PackageNamespace package}
	 * capabilities declared as {@link Resource#getCapabilities(String)
	 * provided} by the resource associated with the wiring and fragment
	 * resources wired to the wiring with the {@link HostNamespace host}
	 * namespace. The provided package names are compared against the
	 * {@link Wiring#getRequiredResourceWires(String) required} package wires to
	 * determine which wires are substitution wires. Subclasses of
	 * <code>ResolveContext</code> should provide a more efficient
	 * implementation of this method.
	 *
	 * @param wiring the wiring to get the substitution wires for. Must not be
	 *            {@code null}.
	 * @return A list containing a snapshot of the substitution {@link Wire}s
	 *         for the {@link Requirement requirements} of the wiring, or an
	 *         empty list if the wiring has no substitution wires. The list
	 *         contains the wires in the order they are found in the
	 *         {@link Wiring#getRequiredResourceWires(String) required} wires of
	 *         the wiring.
	 * @since 1.1
	 */
	public List<Wire> getSubstitutionWires(Wiring wiring) {
		// Keep track of the declared capability package names
		Set<String> exportNames = new HashSet<String>();

		// Add packages declared as provided by the wiring host
		for (Capability cap : wiring.getResource().getCapabilities(null)) {
			if (PackageNamespace.PACKAGE_NAMESPACE.equals(cap.getNamespace())) {
				exportNames.add((String) cap.getAttributes()
						.get(PackageNamespace.PACKAGE_NAMESPACE));
			}
		}

		// Add packages declared as provided by the attached fragments
		for (Wire wire : wiring.getProvidedResourceWires(null)) {
			if (HostNamespace.HOST_NAMESPACE
					.equals(wire.getCapability().getNamespace())) {
				Resource fragment = wire.getRequirement().getResource();
				for (Capability cap : fragment.getCapabilities(null)) {
					if (PackageNamespace.PACKAGE_NAMESPACE
							.equals(cap.getNamespace())) {
						exportNames.add((String) cap.getAttributes()
								.get(PackageNamespace.PACKAGE_NAMESPACE));
					}
				}
			}
		}

		// collect the package wires that substitute one of the declared
		// export package names
		List<Wire> substitutionWires = new ArrayList<Wire>();
		for (Wire wire : wiring.getRequiredResourceWires(null)) {
			if (PackageNamespace.PACKAGE_NAMESPACE
					.equals(wire.getCapability().getNamespace())) {
				if (exportNames
						.contains(wire.getCapability().getAttributes().get(
								PackageNamespace.PACKAGE_NAMESPACE))) {
					substitutionWires.add(wire);
				}
			}
		}
		return substitutionWires;
	}
}

Back to the top