Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: bc731a1a6ce783a5508009f2d2ef14444c6a54cb (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
/*******************************************************************************
 * Copyright (c) 2012, 2013 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.osgi.report.resolution;

import java.util.List;
import java.util.Map;
import org.osgi.framework.hooks.resolver.ResolverHook;
import org.osgi.resource.Resource;
import org.osgi.service.resolver.ResolutionException;

/**
 * A resolution report is associated with a single resolve process.  Entries
 * contained in a resolution report will contain entries for all resources
 * that were attempted to be resolved in a single resolve process.
 * Resolution reports are gathered by a special type of {@link ResolverHook}
 * which implements the report {@link Listener} interface.  The following
 * example demonstrates how to gather a resolution report for a list of bundles
 * <pre>

	public static ResolutionReport getResolutionReport(Bundle[] bundles, BundleContext context) {
		DiagReportListener reportListener = new DiagReportListener(bundles);
		ServiceRegistration<ResolverHookFactory> hookReg = context.registerService(ResolverHookFactory.class, reportListener, null);
		try {
			Bundle systemBundle = context.getBundle(Constants.SYSTEM_BUNDLE_LOCATION);
			FrameworkWiring frameworkWiring = systemBundle.adapt(FrameworkWiring.class);
			frameworkWiring.resolveBundles(Arrays.asList(bundles));
			return reportListener.getReport();
		} finally {
			hookReg.unregister();
		}
	}

	private static class DiagReportListener implements ResolverHookFactory {
		private final Collection<BundleRevision> targetTriggers = new ArrayList<BundleRevision>();
		volatile ResolutionReport report = null;

		public DiagReportListener(Bundle[] bundles) {
			for (Bundle bundle : bundles) {
				BundleRevision revision = bundle.adapt(BundleRevision.class);
				if (revision != null && revision.getWiring() == null) {
					targetTriggers.add(revision);
				}
			}
			
		}

		class DiagResolverHook implements ResolverHook, ResolutionReport.Listener {

			public void handleResolutionReport(ResolutionReport report) {
				DiagReportListener.this.report = report;
			}

			public void filterResolvable(Collection<BundleRevision> candidates) { }

			public void filterSingletonCollisions(BundleCapability singleton,
					Collection<BundleCapability> collisionCandidates) { }

			public void filterMatches(BundleRequirement requirement,
					Collection<BundleCapability> candidates) { }

			public void end() { }
			
		}
		public ResolverHook begin(Collection<BundleRevision> triggers) {
			if (triggers.containsAll(targetTriggers)) {
				// this is the triggers we are looking for
				return new DiagResolverHook();
			}
			// did not find the expected triggers do not participate
			// in the resolve process to gather the report
			return null;
		}
		ResolutionReport getReport() {
			return report;
		}
	}
 * </pre> 
 * @since 3.10
 */
public interface ResolutionReport {
	public interface Entry {
		enum Type {
			/**
			 * Indicates a resource failed to resolve because a resolver hook
			 * filtered it out. The structure of the data is <code>null</code>.
			 */
			// TODO This could possibly be improved by adding a reference to the
			// hook that filtered it out.
			FILTERED_BY_RESOLVER_HOOK,
			/**
			 * Indicates a resource failed to resolve because no capabilities
			 * matching one of the resource's requirements could not be found.
			 * The structure of the data is <code>Requirement</code>, which
			 * represents the missing requirement.
			 */
			MISSING_CAPABILITY,
			/**
			 * Indicates a resource failed to resolve because (1) it's a
			 * singleton, (2) there was at least one collision, and (3) it was
			 * not the selected singleton. The structure of the data is <code>
			 * Resource</code>, which represents the singleton with which the
			 * resource collided.
			 */
			SINGLETON_SELECTION,
			/**
			 * Indicates a resource failed to resolve because one or more
			 * providers of capabilities matching the resource's requirements
			 * were not resolved. The structure of the data is <code>
			 * Map&lt;Requirement, Set&lt;Capability&gt;&gt;</code>.
			 */
			UNRESOLVED_PROVIDER,
			/**
			 * Indicates a resource failed to resolve because of a uses
			 * constraint violation.  The structure of the data is
			 * <code>ResolutionException</code>.
			 */
			USES_CONSTRAINT_VIOLATION
		}

		// TODO Can this make use of generics? Or should this be Map<String, Object>
		// and each enum would define the key constants?
		/**
		 * Returns the data associated with this resolution report entry.  The 
		 * structure of the data is determined by the <code>Type</code>
		 * of the entry and may by <code>null</code>.
		 * 
		 * @return the data associated with this resolution report entry.
		 * @see Type
		 */
		Object getData();

		/**
		 * Returns the type for this resolution report entry.
		 * @return the type for this resolution report entry.
		 */
		Type getType();
	}

	/**
	 * A resolution report listener gets called an the end of resolve process
	 * in order to receive a resolution report.  All {@link ResolverHook resolver hooks}
	 * that also implement the {@link Listener listener} interface will be called
	 * to receive the resolution report associated with the resolve process.
	 */
	public interface Listener {
		void handleResolutionReport(ResolutionReport report);
	}

	/**
	 * Returns all resolution report entries associated with this report.
	 * The key is the unresolved resource and the value is a list of 
	 * report entries that caused the associated resource  to not be able to resolve.
	 * @return all resolution report entries associated with this report.
	 */
	Map<Resource, List<Entry>> getEntries();

	/**
	 * Returns the resolution exception associated with the resolve process
	 * or {@code null} if there is no resolution exception.  For some resolve
	 * operations a resolution exception may not be thrown even if the 
	 * resolve process could not resolve some resources.  For example, if
	 * the resources are optional resources to resolve.
	 * @return the resolution exception or {@code null} if there is 
	 * no resolution exception.
	 */
	ResolutionException getResolutionException();

	/**
	 * Returns a resolution report message for the given resource.
	 * The resource must be included as an {@link #getEntries entry} for this resolution report.
	 * This is a convenience method intended to help display messaged for resolution
	 * errors.
	 * @param resource the resource to get the resolution report message for.
	 * @return a resolution report message.
	 */
	String getResolutionReportMessage(Resource resource);
}

Back to the top