1 /******************************************************************************* 2 * Copyright (c) 2007, 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.core; 15 16 import java.io.BufferedInputStream; 17 import java.io.File; 18 import java.io.FileInputStream; 19 import java.io.IOException; 20 import java.io.InputStream; 21 import java.util.zip.ZipEntry; 22 import java.util.zip.ZipFile; 23 import javax.xml.parsers.SAXParserFactory; 24 import org.eclipse.core.runtime.IContributor; 25 import org.eclipse.core.runtime.IExtensionRegistry; 26 import org.eclipse.core.runtime.IStatus; 27 import org.eclipse.core.runtime.spi.IDynamicExtensionRegistry; 28 import org.eclipse.core.runtime.spi.RegistryContributor; 29 import org.eclipse.core.runtime.spi.RegistryStrategy; 30 import org.eclipse.osgi.service.resolver.BundleDescription; 31 import org.eclipse.osgi.service.resolver.HostSpecification; 32 import org.eclipse.pde.core.plugin.IPluginModelBase; 33 import org.eclipse.pde.core.plugin.ModelEntry; 34 import org.eclipse.pde.core.plugin.PluginRegistry; 35 import org.osgi.util.tracker.ServiceTracker; 36 37 public class PDERegistryStrategy extends RegistryStrategy { 38 39 /** 40 * Tracker for the XML parser service 41 */ 42 private ServiceTracker<?, SAXParserFactory> xmlTracker = null; 43 44 private Object fKey = null; 45 46 private ModelListener fModelListener = null; 47 private ExtensionListener fExtensionListener = null; 48 private PDEExtensionRegistry fPDERegistry = null; 49 50 class RegistryListener { 51 IExtensionRegistry fRegistry; 52 removeModels(IPluginModelBase[] bases, boolean onlyInactive)53 protected final void removeModels(IPluginModelBase[] bases, boolean onlyInactive) { 54 for (IPluginModelBase base : bases) { 55 // resetModel(bases[i]); 56 if (onlyInactive && base.isEnabled()) { 57 continue; 58 } 59 removeBundle(fRegistry, base); 60 } 61 } 62 setRegistry(IExtensionRegistry registry)63 public void setRegistry(IExtensionRegistry registry) { 64 fRegistry = registry; 65 } 66 } 67 68 class ModelListener extends RegistryListener implements IPluginModelListener { 69 70 @Override modelsChanged(PluginModelDelta delta)71 public void modelsChanged(PluginModelDelta delta) { 72 if (fRegistry == null) { 73 createRegistry(); 74 } 75 // can ignore removed models since the ModelEntries is empty 76 ModelEntry[] entries = delta.getChangedEntries(); 77 for (int i = 0; i < entries.length; i++) { 78 // If we have workspace models, we need to make sure they are registered before external models so when we search for extension points, 79 // we find the workspace version 80 IPluginModelBase[] workspaceModels = entries[i].getWorkspaceModels(); 81 if (workspaceModels.length > 0) { 82 removeModels(entries[i].getExternalModels(), !entries[i].hasWorkspaceModels()); 83 removeModels(workspaceModels, true); 84 addBundles(fRegistry, entries[i].getWorkspaceModels()); 85 } 86 // make sure the external models are registered at all times 87 addBundles(fRegistry, entries[i].getExternalModels()); 88 } 89 entries = delta.getAddedEntries(); 90 ModelEntry[] removedEntries = delta.getRemovedEntries(); 91 if (removedEntries.length == entries.length && fRegistry instanceof IDynamicExtensionRegistry) { 92 for (ModelEntry entry : removedEntries) { 93 if (entry.getId() != null) { 94 IDynamicExtensionRegistry registry = (IDynamicExtensionRegistry) fRegistry; 95 IContributor[] contributors = registry.getAllContributors(); 96 for (IContributor contributor : contributors) { 97 if (entry.getId().equals(contributor.getName())) { 98 registry.removeContributor(contributor, fKey); 99 break; 100 } 101 } 102 } 103 } 104 } 105 for (ModelEntry entry : entries) { 106 addBundles(fRegistry, entry.getActiveModels()); 107 } 108 } 109 110 } 111 112 class ExtensionListener extends RegistryListener implements IExtensionDeltaListener { 113 114 @Override extensionsChanged(IExtensionDeltaEvent event)115 public void extensionsChanged(IExtensionDeltaEvent event) { 116 if (fRegistry == null) { 117 createRegistry(); 118 } 119 IPluginModelBase[] bases = event.getRemovedModels(); 120 removeModels(bases, false); 121 removeModels(event.getChangedModels(), false); 122 addBundles(fRegistry, event.getChangedModels()); 123 addBundles(fRegistry, event.getAddedModels()); 124 // if we remove the last workspace model for a Bundle-SymbolicName, then refresh the external models by removing then adding them 125 for (IPluginModelBase base : bases) { 126 ModelEntry entry = PluginRegistry.findEntry(base.getPluginBase().getId()); 127 if (entry != null && entry.getWorkspaceModels().length == 0) { 128 IPluginModelBase[] externalModels = entry.getExternalModels(); 129 removeModels(externalModels, false); 130 addBundles(fRegistry, externalModels); 131 } 132 } 133 } 134 135 } 136 PDERegistryStrategy(File[] storageDirs, boolean[] cacheReadOnly, Object key, PDEExtensionRegistry registry)137 public PDERegistryStrategy(File[] storageDirs, boolean[] cacheReadOnly, Object key, PDEExtensionRegistry registry) { 138 super(storageDirs, cacheReadOnly); 139 init(); 140 fKey = key; 141 fPDERegistry = registry; 142 } 143 init()144 protected void init() { 145 connectListeners(); 146 } 147 148 @Override log(IStatus status)149 public void log(IStatus status) { 150 // Because we are at development time, we create markers for registry problems and therefore do not log anything (bug 330648) 151 } 152 connectListeners()153 protected void connectListeners() { 154 // Listen for model changes to register new bundles and unregister removed bundles 155 PluginModelManager manager = PDECore.getDefault().getModelManager(); 156 manager.addPluginModelListener(fModelListener = new ModelListener()); 157 manager.addExtensionDeltaListener(fExtensionListener = new ExtensionListener()); 158 } 159 setListenerRegistry(IExtensionRegistry registry)160 protected void setListenerRegistry(IExtensionRegistry registry) { 161 if (fModelListener != null) { 162 fModelListener.setRegistry(registry); 163 } 164 if (fExtensionListener != null) { 165 fExtensionListener.setRegistry(registry); 166 } 167 } 168 169 @Override onStart(IExtensionRegistry registry, boolean loadedFromCache)170 public void onStart(IExtensionRegistry registry, boolean loadedFromCache) { 171 super.onStart(registry, loadedFromCache); 172 setListenerRegistry(registry); 173 if (!loadedFromCache) { 174 processBundles(registry); 175 } 176 } 177 178 @Override onStop(IExtensionRegistry registry)179 public void onStop(IExtensionRegistry registry) { 180 super.onStop(registry); 181 setListenerRegistry(null); 182 } 183 184 @Override getXMLParser()185 public SAXParserFactory getXMLParser() { 186 if (xmlTracker == null) { 187 xmlTracker = new ServiceTracker<>(PDECore.getDefault().getBundleContext(), SAXParserFactory.class, null); 188 xmlTracker.open(); 189 } 190 return xmlTracker.getService(); 191 } 192 processBundles(IExtensionRegistry registry)193 private void processBundles(IExtensionRegistry registry) { 194 addBundles(registry, fPDERegistry.getModels()); 195 } 196 addBundles(IExtensionRegistry registry, IPluginModelBase[] bases)197 private void addBundles(IExtensionRegistry registry, IPluginModelBase[] bases) { 198 for (IPluginModelBase base : bases) { 199 addBundle(registry, base); 200 } 201 } 202 addBundle(IExtensionRegistry registry, IPluginModelBase base)203 private void addBundle(IExtensionRegistry registry, IPluginModelBase base) { 204 IContributor contributor = createContributor(base); 205 if (contributor == null) { 206 return; 207 } 208 if (((IDynamicExtensionRegistry) registry).hasContributor(contributor)) { 209 return; 210 } 211 212 File input = getFile(base); 213 if (input == null) { 214 return; 215 } 216 InputStream is = null; 217 ZipFile jfile = null; 218 219 try { 220 if (new File(base.getInstallLocation()).isDirectory()) { 221 // Directory bundle, access the extensions file directly 222 is = new FileInputStream(input); 223 } else { 224 // Archived bundle, need to extract the file 225 jfile = new ZipFile(input, ZipFile.OPEN_READ); 226 String fileName = (base.isFragmentModel()) ? ICoreConstants.FRAGMENT_FILENAME_DESCRIPTOR 227 : ICoreConstants.PLUGIN_FILENAME_DESCRIPTOR; 228 ZipEntry entry = jfile.getEntry(fileName); 229 if (entry != null) { 230 is = jfile.getInputStream(entry); 231 } 232 } 233 if (is != null) { 234 registry.addContribution(new BufferedInputStream(is), contributor, true, input.getPath(), null, fKey); 235 } 236 } catch (IOException e) { 237 } finally { 238 if (jfile != null) { 239 try { 240 jfile.close(); 241 } catch (IOException e) { 242 } 243 } 244 } 245 } 246 removeBundle(IExtensionRegistry registry, IPluginModelBase base)247 private void removeBundle(IExtensionRegistry registry, IPluginModelBase base) { 248 if (registry instanceof IDynamicExtensionRegistry) { 249 IContributor contributor = createContributor(base); 250 if (contributor != null && ((IDynamicExtensionRegistry) registry).hasContributor(contributor)) { 251 ((IDynamicExtensionRegistry) registry).removeContributor(createContributor(base), fKey); 252 } 253 } 254 } 255 256 // added for releasing cached information from IPluginModelBase 257 // private void resetModel(IPluginModelBase model) { 258 // IPluginBase base = model.getPluginBase(); 259 // if (base instanceof BundlePluginBase) { 260 // IExtensions ext = ((BundlePluginBase)base).getExtensionsRoot(); 261 // if (ext != null && ext instanceof AbstractExtensions) { 262 // ((AbstractExtensions)ext).reset(); 263 // } 264 // } else if (base instanceof AbstractExtensions){ 265 // ((AbstractExtensions)base).resetExtensions(); 266 // } 267 // } 268 getFile(IPluginModelBase base)269 private File getFile(IPluginModelBase base) { 270 String loc = base.getInstallLocation(); 271 if (loc == null) { 272 return null; 273 } 274 File file = new File(loc); 275 if (!file.exists()) { 276 return null; 277 } 278 if (file.isFile()) { 279 return file; 280 } 281 String fileName = (base.isFragmentModel()) ? ICoreConstants.FRAGMENT_FILENAME_DESCRIPTOR : ICoreConstants.PLUGIN_FILENAME_DESCRIPTOR; 282 File inputFile = new File(file, fileName); 283 return (inputFile.exists()) ? inputFile : null; 284 } 285 createContributor(IPluginModelBase base)286 public IContributor createContributor(IPluginModelBase base) { 287 BundleDescription desc = base == null ? null : base.getBundleDescription(); 288 // return null if the IPluginModelBase does not have a BundleDescription (since then we won't have a valid 'id') 289 if (desc == null) { 290 return null; 291 } 292 String name = desc.getSymbolicName(); 293 String id = Long.toString(desc.getBundleId()); 294 String hostName = null; 295 String hostId = null; 296 297 HostSpecification host = desc.getHost(); 298 // make sure model is a singleton. If it is a fragment, make sure host is singleton 299 if (host != null && host.getBundle() != null && !host.getBundle().isSingleton() || host == null && !desc.isSingleton()) { 300 return null; 301 } 302 if (host != null) { 303 BundleDescription[] hosts = host.getHosts(); 304 if (hosts.length != 1) { 305 return null; 306 } 307 BundleDescription hostDesc = hosts[0]; 308 hostName = hostDesc.getSymbolicName(); 309 hostId = Long.toString(hostDesc.getBundleId()); 310 } 311 return new RegistryContributor(id, name, hostId, hostName); 312 } 313 dispose()314 public void dispose() { 315 PluginModelManager manager = PDECore.getDefault().getModelManager(); 316 manager.removePluginModelListener(fModelListener); 317 manager.removeExtensionDeltaListener(fExtensionListener); 318 if (xmlTracker != null) { 319 xmlTracker.close(); 320 xmlTracker = null; 321 } 322 } 323 createRegistry()324 private void createRegistry() { 325 fPDERegistry.getRegistry(); 326 } 327 328 // Same timestamp calculations as PDEState.computeTimestamp(URL[] urls, long timestamp) 329 @Override getContributionsTimestamp()330 public long getContributionsTimestamp() { 331 IPluginModelBase[] bases = fPDERegistry.getModels(); 332 long timeStamp = 0; 333 for (IPluginModelBase base : bases) { 334 String loc = base.getInstallLocation(); 335 if (loc == null) { 336 continue; 337 } 338 339 File location = new File(loc); 340 if (location.exists()) { 341 if (location.isFile()) { 342 timeStamp ^= location.lastModified(); 343 } else { 344 File manifest = new File(location, ICoreConstants.BUNDLE_FILENAME_DESCRIPTOR); 345 if (manifest.exists()) { 346 timeStamp ^= manifest.lastModified(); 347 } 348 manifest = new File(location, ICoreConstants.PLUGIN_FILENAME_DESCRIPTOR); 349 if (manifest.exists()) { 350 timeStamp ^= manifest.lastModified(); 351 } 352 manifest = new File(location, ICoreConstants.FRAGMENT_FILENAME_DESCRIPTOR); 353 if (manifest.exists()) { 354 timeStamp ^= manifest.lastModified(); 355 } 356 } 357 timeStamp ^= location.getAbsolutePath().hashCode(); 358 } 359 } 360 return timeStamp; 361 } 362 363 } 364