1 /******************************************************************************* 2 * Copyright (c) 2005, 2018 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.core.runtime.spi; 15 16 import java.io.File; 17 import java.util.*; 18 import javax.xml.parsers.SAXParserFactory; 19 import org.eclipse.core.internal.registry.*; 20 import org.eclipse.core.runtime.*; 21 import org.eclipse.osgi.util.NLS; 22 23 /** 24 * This is the basic registry strategy. It describes how the registry does logging, 25 * message translation, extra start/stop processing, event scheduling, caching, 26 * and debugging. 27 * <p> 28 * In this strategy: 29 * </p><ul> 30 * <li>Logging is done onto <code>System.out</code>;</li> 31 * <li>The translation routine assumes that keys are prefixed with <code>'%'/</code>;</li> 32 * <li>Caching is enabled and doesn't use state or time stamp validation;</li> 33 * <li>Standard Java class loading is used to create executable extensions.</li> 34 * </ul><p> 35 * This class can be used without OSGi running. 36 * </p><p> 37 * This class can be overridden and/or instantiated by clients. 38 * </p> 39 * @since org.eclipse.equinox.registry 3.2 40 */ 41 public class RegistryStrategy { 42 43 private SAXParserFactory theXMLParserFactory = null; 44 45 /** 46 * Array of file system directories to store cache files; might be <code>null</code> 47 */ 48 private final File[] storageDirs; 49 50 /** 51 * Specifies if the registry file cache is read only; might be <code>null</code> 52 */ 53 private final boolean[] cacheReadOnly; 54 55 /** 56 * Constructor for this default registry strategy. 57 * <p> 58 * The strategy sequentially checks the array of storage directories to 59 * discover the location of the registry cache formed by previous invocations of the extension 60 * registry. Once found, the location is used to store registry cache. If this value 61 * is <code>null</code> then caching of the registry content is disabled. 62 * </p><p> 63 * The cache read-only array is an array the same length as the storage directory array. 64 * It contains boolean values indicating whether or not each storage directory is read-only. 65 * If the value at an index is <code>true</code> then the location at the corresponding index 66 * in the storage directories array is read-only; if <code>false</code> then the cache location 67 * is read-write. The array can be <code>null</code> if the <code>storageDirs</code> parameter 68 * is <code>null</code>. 69 * </p> 70 * 71 * @param storageDirs array of file system directories, or <code>null</code> 72 * @param cacheReadOnly array of read only attributes, or <code>null</code> 73 */ RegistryStrategy(File[] storageDirs, boolean[] cacheReadOnly)74 public RegistryStrategy(File[] storageDirs, boolean[] cacheReadOnly) { 75 this.storageDirs = storageDirs; 76 this.cacheReadOnly = cacheReadOnly; 77 } 78 79 /** 80 * Returns the number of possible cache locations for this registry. 81 * 82 * @return number of possible cache locations for this registry 83 */ getLocationsLength()84 public final int getLocationsLength() { 85 if (storageDirs == null) 86 return 0; 87 return storageDirs.length; 88 } 89 90 /** 91 * Returns the possible registry cache location identified by the index. 92 * 93 * @param index index of the possible registry location 94 * @return potential registry cache location 95 */ getStorage(int index)96 public final File getStorage(int index) { 97 if (storageDirs != null) 98 return storageDirs[index]; 99 return null; 100 } 101 102 /** 103 * Returns the read-only status of the registry cache location. 104 * 105 * @param index the index of the possible registry location 106 * @return <code>true</code> if the location is read only and 107 * <code>false</code> if the location is read/write 108 */ isCacheReadOnly(int index)109 public final boolean isCacheReadOnly(int index) { 110 if (cacheReadOnly != null) 111 return cacheReadOnly[index]; 112 return true; 113 } 114 115 /** 116 * Override this method to provide customized logging functionality 117 * to the registry. The method adds a log entry based on the supplied status. 118 * <p> 119 * This method writes a message to <code>System.out</code> 120 * in the following format:</p> 121 * <pre> 122 * [Error|Warning|Log]: Main error message 123 * [Error|Warning|Log]: Child error message 1 124 * ... 125 * [Error|Warning|Log]: Child error message N 126 * </pre> 127 * 128 * @param status the status to log 129 */ log(IStatus status)130 public void log(IStatus status) { 131 RegistrySupport.log(status, null); 132 } 133 134 /** 135 * Translates key using the supplied resource bundle. The resource bundle is 136 * optional and might be <code>null</code>. 137 * <p> 138 * The default translation routine assumes that keys are prefixed with '%'. If 139 * no resource bundle is present, the key itself (without leading '%') is returned. 140 * There is no decoding for the leading '%%'. 141 * </p> 142 * 143 * @param key message key to be translated 144 * @param resources resource bundle, or <code>null</code> 145 * @return the translated string, must not be <code>null</code> 146 */ translate(String key, ResourceBundle resources)147 public String translate(String key, ResourceBundle resources) { 148 return RegistrySupport.translate(key, resources); 149 } 150 151 /** 152 * Override this method to provide additional processing performed 153 * when the registry is created and started. Overrides should call 154 * <code>super.onStart()</code> at the beginning of the processing. 155 * <p> 156 * <strong>NOTE</strong>: Avoid placing duplicate functionality in 157 * this method and {@link #onStart(IExtensionRegistry, boolean)} as 158 * both methods will be called on the registry startup. 159 * </p> 160 * @param registry the extension registry being started 161 * 162 * @deprecated use {@link #onStart(IExtensionRegistry, boolean)}. 163 */ 164 @Deprecated onStart(IExtensionRegistry registry)165 public void onStart(IExtensionRegistry registry) { 166 // The default implementation 167 } 168 169 /** 170 * Override this method to provide additional processing performed 171 * when the registry is created and started. Overrides should call 172 * <code>super.onStart()</code> at the beginning of the processing. 173 * 174 * @param registry the extension registry being started 175 * @param loadedFromCache true is registry contents was loaded from 176 * cache when the registry was created 177 * 178 * @since 3.4 179 */ onStart(IExtensionRegistry registry, boolean loadedFromCache)180 public void onStart(IExtensionRegistry registry, boolean loadedFromCache) { 181 // The default implementation 182 } 183 184 /** 185 * Override this method to provide additional processing to be performed 186 * just before the registry is stopped. Overrides should call 187 * <code>super.onStop()</code> at the end of the processing. 188 * @param registry the extension registry being stopped 189 */ onStop(IExtensionRegistry registry)190 public void onStop(IExtensionRegistry registry) { 191 // The default implementation 192 } 193 194 /** 195 * Creates an executable extension. Override this method to supply an alternative processing 196 * for the creation of executable extensions. 197 * <p> 198 * This method receives the contributor of the executable extension and, possibly, 199 * an optional contributor name if specified by the executable extension. The overridden 200 * contributor name might be <code>null</code>. 201 * </p><p> 202 * In this implementation registry attempts to instantiate the class specified via 203 * the class name (must not be <code>null</code>) using standard Java reflection mechanism. 204 * This method assumes that such class has a default constructor with no arguments. 205 * </p> 206 * 207 * @param contributor the contributor of this executable extension 208 * @param className the name of the class to be instantiated 209 * @param overridenContributorName the contributor to be used, or <code>null</code> if not specified 210 * @return the object created, or <code>null</code> 211 * @throws CoreException if there was a problem creating the executable extension 212 * @see IConfigurationElement#createExecutableExtension(String) 213 * @see IExecutableExtension 214 */ createExecutableExtension(RegistryContributor contributor, String className, String overridenContributorName)215 public Object createExecutableExtension(RegistryContributor contributor, String className, String overridenContributorName) throws CoreException { 216 Object result = null; 217 Class<?> classInstance = null; 218 try { 219 classInstance = Class.forName(className); 220 } catch (ClassNotFoundException e1) { 221 String message = NLS.bind(RegistryMessages.exExt_findClassError, contributor.getActualName(), className); 222 throw new CoreException(new Status(IStatus.ERROR, RegistryMessages.OWNER_NAME, IRegistryConstants.PLUGIN_ERROR, message, e1)); 223 } 224 225 try { 226 result = classInstance.getDeclaredConstructor().newInstance(); 227 } catch (Exception e) { 228 String message = NLS.bind(RegistryMessages.exExt_instantiateClassError, contributor.getActualName(), className); 229 throw new CoreException(new Status(IStatus.ERROR, RegistryMessages.OWNER_NAME, IRegistryConstants.PLUGIN_ERROR, message, e)); 230 } 231 return result; 232 } 233 234 /** 235 * Override this method to customize scheduling of an extension registry event. Note that this method 236 * <strong>must</strong> make the following call to actually process the event: 237 * <pre><code> 238 * RegistryStrategy.processChangeEvent(listeners, deltas, registry); 239 * </code></pre><p> 240 * In the default implementation, the method registry events are executed in a queue 241 * on a separate thread (i.e. asynchronously, sequentially). 242 * </p> 243 * 244 * @param listeners the list of active listeners (thread safe); may not be <code>null</code> 245 * @param deltas the registry deltas (thread safe); may not be <code>null</code> 246 * @param registry the extension registry (NOT thread safe); may not be <code>null</code> 247 */ scheduleChangeEvent(Object[] listeners, Map<String, ?> deltas, Object registry)248 public void scheduleChangeEvent(Object[] listeners, Map<String, ?> deltas, Object registry) { 249 ((ExtensionRegistry) registry).scheduleChangeEvent(listeners, deltas); 250 } 251 252 /** 253 * This method performs actual processing of the registry change event. It should 254 * only be used by overrides of the RegistryStrategy.scheduleChangeEvent. It will 255 * return <code>null</code> if an unexpected registry type was encountered. 256 * 257 * @param listeners the list of active listeners; may not be <code>null</code> 258 * @param deltas the extension registry deltas; may not be <code>null</code> 259 * @param registry the extension registry; may not be <code>null</code> 260 * @return status of the operation or <code>null</code> 261 */ processChangeEvent(Object[] listeners, Map<String, ?> deltas, Object registry)262 public final static IStatus processChangeEvent(Object[] listeners, Map<String, ?> deltas, Object registry) { 263 if (registry instanceof ExtensionRegistry) 264 return ((ExtensionRegistry) registry).processChangeEvent(listeners, deltas); 265 return null; 266 } 267 268 /** 269 * Override this method to specify debug requirements to the registry. In the default 270 * implementation this method returns <code>false</code> indicating that debug functionality 271 * is turned off. 272 * <p> 273 * Note that in a general case the extension registry plug-in doesn't depend on OSGI and 274 * therefore cannot use Eclipse .options files to discover debug options. 275 * </p> 276 * 277 * @return <code>true</code> if debug logging and validation should be performed and 278 * <code>false</code> otherwise 279 */ debug()280 public boolean debug() { 281 return false; 282 } 283 284 /** 285 * Override this method to specify debug requirements for the registry event processing. 286 * In the default implementation this method returns <code>false</code> indicating that 287 * debug of the registry events is turned off. 288 * <p> 289 * Note that in a general case the extension registry plug-in doesn't depend on OSGI and 290 * therefore cannot use Eclipse .options files to discover debug options. 291 * </p> 292 * 293 * @return <code>true</code> if debug logging and validation of the registry events 294 * should be performed and <code>false</code> otherwise 295 */ debugRegistryEvents()296 public boolean debugRegistryEvents() { 297 return false; 298 } 299 300 /** 301 * Specifies if the extension registry should use cache to store registry data between 302 * invocations. 303 * <p> 304 * The default implementation enables caching returning <code>true</code>. 305 * </p> 306 * 307 * @return <code>true</code> if the cache should be used and <code>false</code> otherwise 308 */ cacheUse()309 public boolean cacheUse() { 310 return true; 311 } 312 313 /** 314 * Specifies if lazy cache loading is used. 315 * <p> 316 * The default implementation specifies that lazy cache loading is going to be used 317 * and therefore returns <code>true</code>. 318 * </p> 319 * 320 * @return <code>true</code> if lazy cache loading is used and <code>false</code> otherwise 321 */ cacheLazyLoading()322 public boolean cacheLazyLoading() { 323 return true; 324 } 325 326 /** 327 * This method is called as a part of the registry cache validation. The cache is valid 328 * on the registry startup if the pair {container time stamp, contributors time stamp} 329 * supplied by the registry strategy is the same as the {container time stamp, contributors time stamp} 330 * stored in the registry cache. The goal of the validation is to be able to catch modifications 331 * made to the original data contributed into the registry and not reflected in the registry cache. 332 * <p> 333 * The method produces a number that corresponds to the current state of the data stored 334 * by the container. Increment the stamp if the data stored in the container has been updated 335 * so that the data cached by the registry is no longer valid. For instance, in Eclipse addition 336 * or removal of a bundle results in the number returned by this method being incremented. As a result, 337 * if a bundle that contributed plugin.xml into the extension registry was modified, the state doesn't 338 * match the state stored in the registry cache. In this case the cache content becomes invalid and 339 * the registry needs to be re-created from the original data. 340 * </p><p> 341 * Generally, treat this number as a hash code for the data stored in the registry. 342 * It stays the same as long as the registry content is not changing. It becomes a different 343 * number as the registry content gets modified. 344 * </p><p> 345 * Return 0 to indicate that state verification is not required. 346 * </p> 347 * 348 * @return number indicating state of the application data 349 */ getContainerTimestamp()350 public long getContainerTimestamp() { 351 return 0; 352 } 353 354 /** 355 * This method is called as a part of the registry cache validation. The method calculates 356 * a number describing the time when the originating contributions (i.e., plugin.xml files 357 * in case of the Eclipse registry) were last modified. 358 * <p> 359 * The value returned by the method is compared with the timestamp tracked by the registry. 360 * If contributions changed since they have been added to the registry (i.e., plugin.xml 361 * file was modified since the last run), the value of the {@link #getContributionsTimestamp()} 362 * will change and no longer will be the same as the value tracked by the registry. In this case 363 * the cache is considered to be invalid and the registry is going to be re-populated form scratch. 364 * </p><p> 365 * (The word "timestamp" is used very loosely here. In this context, "timestamp" is more likely 366 * to be a hash value aggregating a number of actual timestamps from the contributions.) 367 * </p><p> 368 * This method may return 0 to indicate that no time stamp verification is required. 369 * </p> 370 * @return a value corresponding to the last modification time of contributions contained 371 * in the registry 372 */ getContributionsTimestamp()373 public long getContributionsTimestamp() { 374 return 0; 375 } 376 377 /** 378 * Returns the parser used by the registry to parse descriptions of extension points and extensions. 379 * This method must not return <code>null</code>. 380 * 381 * @return this strategy's parser 382 * @see org.eclipse.core.runtime.IExtensionRegistry#addContribution(java.io.InputStream, IContributor, boolean, String, ResourceBundle, Object) 383 */ getXMLParser()384 public SAXParserFactory getXMLParser() { 385 if (theXMLParserFactory == null) 386 theXMLParserFactory = SAXParserFactory.newInstance(); 387 return theXMLParserFactory; 388 } 389 390 /** 391 * Translates array of keys supplied by the contributor to the requested locale. 392 * <p> 393 * This method is intended to be overridden by specialized registry strategies 394 * that know translation conventions for the contributors, for instance, 395 * the agreed upon locations of the translated keys for bundle contributors. 396 * The base implementation simply returns the array of non-translated keys. 397 * </p><p> 398 * This method is only used if multi-language support is enabled. 399 * </p> 400 * @param nonTranslated message keys to be translated 401 * @param contributor the contributor of the keys to be translated 402 * @param locale the requested locale for the keys 403 * @return the arrays of translated strings 404 * @see IExtensionRegistry#isMultiLanguage() 405 * @since org.eclipse.equinox.registry 3.5 406 */ translate(String[] nonTranslated, IContributor contributor, String locale)407 public String[] translate(String[] nonTranslated, IContributor contributor, String locale) { 408 return nonTranslated; 409 } 410 411 /** 412 * Returns the current locale for the extension registry with enabled 413 * multi-language support. 414 * <p> 415 * The default implementation assumes that there is a single system wide 416 * locale, equivalent to {@link Locale#getDefault()}. 417 * </p><p> 418 * The result of this method should not be retained or passed to other threads. 419 * The current locale can change any time and may be different for each thread. 420 * </p><p> 421 * This method can be overridden by subclasses that wish to provide a way 422 * to change the default locale. 423 * </p><p> 424 * This method is only used if multi-language support is enabled. 425 * </p> 426 * @see IExtensionRegistry#isMultiLanguage() 427 * @return the default locale 428 * @since org.eclipse.equinox.registry 3.5 429 */ getLocale()430 public String getLocale() { 431 return Locale.getDefault().toString(); 432 } 433 } 434