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