1 /*******************************************************************************
2  * Copyright (c) 2008, 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 package org.eclipse.pde.internal.runtime.registry.model;
15 
16 import java.util.*;
17 import org.eclipse.core.runtime.IProgressMonitor;
18 import org.eclipse.osgi.service.resolver.VersionRange;
19 import org.osgi.framework.Version;
20 
21 /**
22  * Model entry point for Eclipse runtime. Provides information about runtime bundles, services and extension points.
23  */
24 public class RegistryModel {
25 
26 	private BackendChangeListener backendListener = new BackendChangeListener() {
27 		@Override
28 		public void addBundle(Bundle adapter) {
29 			adapter.setModel(RegistryModel.this);
30 			ModelChangeDelta delta = new ModelChangeDelta(adapter, ModelChangeDelta.ADDED);
31 
32 			bundles.put(Long.valueOf(adapter.getId()), adapter);
33 
34 			if (adapter.getFragmentHost() != null) {
35 				addFragment(adapter);
36 
37 				Bundle host = getBundle(adapter.getFragmentHost(), adapter.getFragmentHostVersion());
38 				if (host != null) {
39 					ModelChangeDelta d2 = new ModelChangeDelta(host, ModelChangeDelta.UPDATED);
40 					fireModelChangeEvent(new ModelChangeDelta[] {delta, d2});
41 					return;
42 				}
43 			}
44 
45 			fireModelChangeEvent(new ModelChangeDelta[] {delta});
46 		}
47 
48 		@Override
49 		public void removeBundle(Bundle adapter) {
50 			ModelChangeDelta delta = new ModelChangeDelta(adapter, ModelChangeDelta.REMOVED);
51 
52 			bundles.remove(Long.valueOf(adapter.getId()));
53 
54 			if (adapter.getFragmentHost() != null) {
55 				removeFragment(adapter);
56 
57 				Bundle host = getBundle(adapter.getFragmentHost(), adapter.getFragmentHostVersion());
58 				if (host != null) {
59 					ModelChangeDelta d2 = new ModelChangeDelta(host, ModelChangeDelta.UPDATED);
60 					fireModelChangeEvent(new ModelChangeDelta[] {delta, d2});
61 					return;
62 				}
63 			}
64 
65 			fireModelChangeEvent(new ModelChangeDelta[] {delta});
66 			adapter.setModel(null);
67 		}
68 
69 		@Override
70 		public void updateBundle(Bundle adapter, int updated) {
71 			adapter.setModel(RegistryModel.this);
72 			ModelChangeDelta delta = new ModelChangeDelta(adapter, updated);
73 
74 			bundles.put(Long.valueOf(adapter.getId()), adapter); // replace old with new one
75 
76 			if (adapter.getFragmentHost() != null) {
77 				addFragment(adapter);
78 			}
79 
80 			fireModelChangeEvent(new ModelChangeDelta[] {delta});
81 		}
82 
83 		@Override
84 		public void addService(ServiceRegistration adapter) {
85 			ModelChangeDelta serviceNameDelta = null;
86 			if (!serviceNames.contains(adapter.getName())) {
87 				ServiceName name = adapter.getName();
88 				name.setModel(RegistryModel.this);
89 
90 				serviceNames.add(name);
91 
92 				serviceNameDelta = new ModelChangeDelta(name, ModelChangeDelta.ADDED);
93 			}
94 
95 			adapter.setModel(RegistryModel.this);
96 			services.put(Long.valueOf(adapter.getId()), adapter);
97 
98 			ModelChangeDelta delta = new ModelChangeDelta(adapter, ModelChangeDelta.ADDED);
99 
100 			if (serviceNameDelta != null) {
101 				fireModelChangeEvent(new ModelChangeDelta[] {serviceNameDelta, delta});
102 			} else {
103 				fireModelChangeEvent(new ModelChangeDelta[] {delta});
104 			}
105 		}
106 
107 		@Override
108 		public void removeService(ServiceRegistration adapter) {
109 			ModelChangeDelta serviceNameDelta = null;
110 			if (getServices(adapter.getName().getClasses()).length == 0) {
111 				serviceNames.remove(adapter.getName());
112 				serviceNameDelta = new ModelChangeDelta(adapter.getName(), ModelChangeDelta.REMOVED);
113 			}
114 
115 			services.remove(Long.valueOf(adapter.getId()));
116 
117 			ModelChangeDelta delta = new ModelChangeDelta(adapter, ModelChangeDelta.REMOVED);
118 
119 			if (serviceNameDelta != null) {
120 				fireModelChangeEvent(new ModelChangeDelta[] {serviceNameDelta, delta});
121 				adapter.getName().setModel(null);
122 				adapter.setModel(null);
123 			} else {
124 				fireModelChangeEvent(new ModelChangeDelta[] {delta});
125 				adapter.setModel(null);
126 			}
127 		}
128 
129 		@Override
130 		public void updateService(ServiceRegistration adapter) {
131 			adapter.setModel(RegistryModel.this);
132 			services.put(Long.valueOf(adapter.getId()), adapter);
133 
134 			ModelChangeDelta delta = new ModelChangeDelta(adapter, ModelChangeDelta.UPDATED);
135 
136 			fireModelChangeEvent(new ModelChangeDelta[] {delta});
137 		}
138 
139 		@Override
140 		public void addExtensions(Extension[] extensionAdapters) {
141 			for (Extension extension : extensionAdapters) {
142 				extension.setModel(RegistryModel.this);
143 				String id = extension.getExtensionPointUniqueIdentifier();
144 				ExtensionPoint extPoint = extensionPoints.get(id);
145 				extPoint.getExtensions().add(extension);
146 			}
147 
148 			ModelChangeDelta[] delta = new ModelChangeDelta[extensionAdapters.length];
149 			for (int i = 0; i < delta.length; i++) {
150 				delta[i] = new ModelChangeDelta(extensionAdapters[i], ModelChangeDelta.ADDED);
151 			}
152 			fireModelChangeEvent(delta);
153 		}
154 
155 		@Override
156 		public void removeExtensions(Extension[] extensionAdapters) {
157 			for (Extension extension : extensionAdapters) {
158 				String id = extension.getExtensionPointUniqueIdentifier();
159 				ExtensionPoint extPoint = extensionPoints.get(id);
160 				extPoint.getExtensions().remove(extension);
161 			}
162 
163 			ModelChangeDelta[] delta = new ModelChangeDelta[extensionAdapters.length];
164 			for (int i = 0; i < delta.length; i++) {
165 				delta[i] = new ModelChangeDelta(extensionAdapters[i], ModelChangeDelta.REMOVED);
166 			}
167 			fireModelChangeEvent(delta);
168 
169 			for (Extension extension : extensionAdapters) {
170 				extension.setModel(null);
171 			}
172 		}
173 
174 		@Override
175 		public void addExtensionPoints(ExtensionPoint[] extensionPointAdapters) {
176 			for (ExtensionPoint extPoint : extensionPointAdapters) {
177 				extPoint.setModel(RegistryModel.this);
178 				extensionPoints.put(extPoint.getUniqueIdentifier(), extPoint);
179 			}
180 
181 			ModelChangeDelta[] delta = new ModelChangeDelta[extensionPointAdapters.length];
182 			for (int i = 0; i < delta.length; i++) {
183 				delta[i] = new ModelChangeDelta(extensionPointAdapters[i], ModelChangeDelta.ADDED);
184 			}
185 			fireModelChangeEvent(delta);
186 		}
187 
188 		@Override
189 		public void removeExtensionPoints(ExtensionPoint[] extensionPointAdapters) {
190 			for (ExtensionPoint extPoint : extensionPointAdapters) {
191 				extensionPoints.remove(extPoint.getUniqueIdentifier());
192 			}
193 
194 			ModelChangeDelta[] delta = new ModelChangeDelta[extensionPointAdapters.length];
195 			for (int i = 0; i < delta.length; i++) {
196 				delta[i] = new ModelChangeDelta(extensionPointAdapters[i], ModelChangeDelta.REMOVED);
197 			}
198 			fireModelChangeEvent(delta);
199 
200 			for (ExtensionPoint extPoint : extensionPointAdapters) {
201 				extPoint.setModel(null);
202 			}
203 		}
204 	};
205 
206 	private List<ModelChangeListener> listeners = new ArrayList<>();
207 	private Map<Long, Bundle> bundles;
208 	private Map<Long, ServiceRegistration> services;
209 	private Map<String, ExtensionPoint> extensionPoints;
210 	private Set<ServiceName> serviceNames;
211 	private Map<String, Set<Bundle>> fragments;
212 
213 	protected RegistryBackend backend;
214 
RegistryModel(RegistryBackend backend)215 	public RegistryModel(RegistryBackend backend) {
216 		bundles = Collections.synchronizedMap(new LinkedHashMap<>());
217 		services = Collections.synchronizedMap(new LinkedHashMap<>());
218 		extensionPoints = Collections.synchronizedMap(new LinkedHashMap<>());
219 		serviceNames = Collections.synchronizedSet(new LinkedHashSet<>());
220 		fragments = Collections.synchronizedMap(new LinkedHashMap<>());
221 
222 		this.backend = backend;
223 		backend.setRegistryListener(backendListener);
224 	}
225 
addFragment(Bundle fragment)226 	protected void addFragment(Bundle fragment) {
227 		Set<Bundle> hostFragments = fragments.get(fragment.getFragmentHost());
228 		if (hostFragments == null) {
229 			hostFragments = Collections.synchronizedSet(new HashSet<>());
230 			fragments.put(fragment.getFragmentHost(), hostFragments);
231 		}
232 
233 		if (!hostFragments.add(fragment)) {
234 			// not added if element already exists. So remove old and add it again.
235 			hostFragments.remove(fragment);
236 			hostFragments.add(fragment);
237 		}
238 	}
239 
removeFragment(Bundle fragment)240 	protected void removeFragment(Bundle fragment) {
241 		Set<Bundle> hostFragments = fragments.get(fragment.getFragmentHost());
242 		if (hostFragments == null) {
243 			return;
244 		}
245 
246 		hostFragments.remove(fragment);
247 	}
248 
connect(IProgressMonitor monitor, boolean forceInit)249 	public void connect(IProgressMonitor monitor, boolean forceInit) {
250 		backend.connect(monitor);
251 
252 		if (forceInit) {
253 			initialize(monitor);
254 		}
255 	}
256 
initialize(IProgressMonitor monitor)257 	public void initialize(IProgressMonitor monitor) {
258 		backend.initializeBundles(monitor);
259 		backend.initializeServices(monitor);
260 		backend.initializeExtensionPoints(monitor);
261 	}
262 
disconnect()263 	public void disconnect() {
264 		backend.disconnect();
265 	}
266 
getBundles()267 	public Bundle[] getBundles() {
268 		return bundles.values().toArray(new Bundle[bundles.size()]);
269 	}
270 
getExtensionPoints()271 	public ExtensionPoint[] getExtensionPoints() {
272 		return extensionPoints.values().toArray(new ExtensionPoint[extensionPoints.size()]);
273 	}
274 
getServices()275 	public ServiceRegistration[] getServices() {
276 		return services.values().toArray(new ServiceRegistration[services.size()]);
277 	}
278 
getServiceNames()279 	public ServiceName[] getServiceNames() {
280 		return serviceNames.toArray(new ServiceName[serviceNames.size()]);
281 	}
282 
getServices(String[] classes)283 	public ServiceRegistration[] getServices(String[] classes) {
284 		List<ServiceRegistration> result = new ArrayList<>();
285 
286 		synchronized (services) {
287 			for (Iterator<ServiceRegistration> i = services.values().iterator(); i.hasNext();) {
288 				ServiceRegistration sr = i.next();
289 				if (Arrays.equals(classes, sr.getName().getClasses()))
290 					result.add(sr);
291 			}
292 		}
293 
294 		return result.toArray(new ServiceRegistration[result.size()]);
295 	}
296 
addModelChangeListener(ModelChangeListener listener)297 	public void addModelChangeListener(ModelChangeListener listener) {
298 		listeners.add(listener);
299 	}
300 
removeModelChangeListener(ModelChangeListener listener)301 	public void removeModelChangeListener(ModelChangeListener listener) {
302 		listeners.remove(listener);
303 	}
304 
305 	/**
306 	 * For received domain types: Bundle, IExtension, IExtensionPoint, ServiceReference,
307 	 * generates delta with model types: IBundle, IExtensionAdapter, IExtensionPointAdapter, IService
308 	 *
309 	 * @param objects
310 	 */
fireModelChangeEvent(ModelChangeDelta[] delta)311 	protected void fireModelChangeEvent(ModelChangeDelta[] delta) {
312 		for (Iterator<ModelChangeListener> i = listeners.iterator(); i.hasNext();) {
313 			ModelChangeListener listener = i.next();
314 			listener.modelChanged(delta);
315 		}
316 	}
317 
getBundle(Long id)318 	public Bundle getBundle(Long id) {
319 		return bundles.get(id);
320 	}
321 
getBundle(String symbolicName, String versionRange)322 	public Bundle getBundle(String symbolicName, String versionRange) {
323 		synchronized (bundles) {
324 			for (Iterator<Bundle> i = bundles.values().iterator(); i.hasNext();) {
325 				Bundle bundle = i.next();
326 
327 				if (bundle.getSymbolicName().equals(symbolicName)) {
328 					if (versionMatches(bundle.getVersion(), versionRange))
329 						return bundle;
330 				}
331 			}
332 		}
333 
334 		return null;
335 	}
336 
getExtensionPoint(String extensionPointUniqueIdentifier)337 	public ExtensionPoint getExtensionPoint(String extensionPointUniqueIdentifier) {
338 		return extensionPoints.get(extensionPointUniqueIdentifier);
339 	}
340 
getFragments(Bundle bundle)341 	public Bundle[] getFragments(Bundle bundle) {
342 		Set<Bundle> set = fragments.get(bundle.getSymbolicName());
343 		if (set == null)
344 			return new Bundle[0];
345 
346 		List<Bundle> result = new ArrayList<>(set.size());
347 		Version hostVersion = Version.parseVersion(bundle.getVersion());
348 		for (Iterator<Bundle> i = set.iterator(); i.hasNext();) {
349 			Bundle fragment = i.next();
350 			String fragmentVersionOrRange = fragment.getFragmentHostVersion();
351 
352 			if (versionMatches(hostVersion, fragmentVersionOrRange))
353 				result.add(fragment);
354 		}
355 
356 		return result.toArray(new Bundle[result.size()]);
357 	}
358 
versionMatches(String hostVersion, String versionOrRange)359 	private boolean versionMatches(String hostVersion, String versionOrRange) {
360 		try {
361 			Version version = Version.parseVersion(hostVersion);
362 			return versionMatches(version, versionOrRange);
363 
364 		} catch (IllegalArgumentException e) {
365 			// ignore
366 		}
367 
368 		return false;
369 	}
370 
371 	/**
372 	 * Check if hostVersion is greater or equal fragmentVersion, or is included in fragment version range
373 	 * @param hostVersion Version
374 	 * @param versionOrRange Version or VersionRange
375 	 * @return true if matches, false otherwise
376 	 */
versionMatches(Version hostVersion, String versionOrRange)377 	private boolean versionMatches(Version hostVersion, String versionOrRange) {
378 		if (versionOrRange == null) {
379 			return true;
380 		}
381 
382 		try {
383 			Version version = Version.parseVersion(versionOrRange);
384 			if (hostVersion.compareTo(version) >= 0)
385 				return true;
386 
387 		} catch (IllegalArgumentException e) {
388 			// wrong formatting, try VersionRange
389 		}
390 
391 		try {
392 			VersionRange range = new VersionRange(versionOrRange);
393 			if (range.isIncluded(hostVersion))
394 				return true;
395 
396 		} catch (IllegalArgumentException e2) {
397 			// wrong range formatting
398 		}
399 
400 		return false;
401 	}
402 
403 	/*	void setEnabled(Bundle bundle, boolean enabled);
404 
405 		void start(Bundle bundle) throws BundleException; // XXX Create custom Exception
406 
407 		void stop(Bundle bundle) throws BundleException;
408 
409 		MultiStatus diagnose(Bundle bundle);*/
410 
411 }
412