blob: 79c04f527af0a1696e2ea4a12643af95a06a8676 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.equinox.registry.spi;
import java.io.File;
import java.util.Map;
import java.util.ResourceBundle;
import javax.xml.parsers.SAXParserFactory;
import org.eclipse.core.internal.registry.*;
import org.eclipse.core.runtime.*;
import org.eclipse.equinox.registry.IConfigurationElement;
import org.eclipse.equinox.registry.IExtensionRegistry;
import org.eclipse.osgi.util.NLS;
/**
* This is the basic registry strategy. It describes how registry does logging,
* message translation, namespace resolution, extra start/stop processing, event scheduling,
* caching, and debugging.
*
* In this strategy:
* - Clients can add information into the registry
* - Caching is enabled and doesn't use state or time stamp validation; no alternative cache location is supplied
* - Logging is done onto System.out
* - All registry contributors are assumed to reside in separate namespaces
* - Standard Java class loading is used to create executable extensions
*
* This class can be overridden and/or instantiated.
*
* @since org.eclipse.equinox.registry 1.0
*/
public class RegistryStrategy {
/**
* File system directory to store cache files; might be null
*/
private final File theStorageDir;
/**
* Specifies if the registry file cache is read only
*/
private final boolean cacheReadOnly;
/**
* Public constructor.
*
* @param theStorageDir - file system directory to store cache files; might be null
* @param cacheReadOnly - true: cache is read only; false: cache is read/write
*/
public RegistryStrategy(File theStorageDir, boolean cacheReadOnly) {
this.theStorageDir = theStorageDir;
this.cacheReadOnly = cacheReadOnly;
}
public final File getStorage() {
return theStorageDir;
}
public final boolean isCacheReadOnly() {
return cacheReadOnly;
}
/**
* Specifies if registry clients can add information into the registry.
* @return true: clients can add information; false: proper token should be supplied
* in order to add information into the registry.
*/
public boolean isModifiable() {
return true;
}
/**
* Adds a log entry based on the supplied status.
* This method writes a message to the System.out in the following format:
*
* [Error|Warning|Log]: Main error message
* [Error|Warning|Log]: Child error message 1
* ...
* [Error|Warning|Log]: Child error message N
*
* @param status - the status to log
*/
public void log(IStatus status) {
RegistrySupport.log(status, null);
}
/**
* Translates key using the supplied resource bundle. The resource bundle is
* optional and might be null.
* The default translation routine assumes that keys are prefixed with '%'. If
* no resource bundle is present, the key itself (without leading '%') is returned.
* There is no decoding for the leading '%%'.
*
* @param key - message key to be translated
* @param resources - resource bundle, might be null
* @return - translated string
*/
public String translate(String key, ResourceBundle resources) {
return RegistrySupport.translate(key, resources);
}
/**
* Override this method to provide additional processing performed
* at the end of the registry's constructor.
*
* @param registry - the extension registry being created
*/
public void onStart(Object registry) {
// The default implementation
}
/**
* Override this method to provide additional processing before
* the extension registry's stop() method is executed.
*
* @param registry - the extension registry being stopped
*/
public void onStop(Object registry) {
// The default implementation
}
///////////////////////////////////////////////////////////////////////////
// XXX The use of longs is strange here. It is very OSGi specific.
// It is likey that they are going to be replaced with some
// generic object in the near future.
/**
* Returns Id of the namespace owner that given contributor resides in.
*
* Override this method to supply namespace resolution capabilities to
* the extension registry. If this interface is not overridden, each contributor
* is assumed to be in its own namespace (namespaceOwnerId is the same as contributorId);
* namespace name is assumed to be a String form of the contributorId.
*
* It is assumed that namespaces organize contributors into groups. Or, more formally:
*
* 1) each contributor resides in a namespace
* 2) each namespace is owned by some contributor
* 3) many contributors can reside in a single namespace
*
* @param contributorId - Id of the contributor in question
* @return - Id of the namespace owner
*/
public long getNamespaceOwnerId(long contributorId) {
return contributorId;
}
///////////////////////////////////////////////////////////////////////////
// XXX The use of longs is strange here. It is very OSGi specific.
// It is likey that they are going to be replaced with some
// generic object in the near future.
/**
* Returns name of the namespace that given contributor resides in.
* @see #getNamespaceOwnerId(long)
*
* @param contributorId - Id of the contributor in question
* @return - namespace name
*/
public String getNamespace(long contributorId) {
return String.valueOf(contributorId);
}
///////////////////////////////////////////////////////////////////////////
// XXX The use of longs is strange here. It is very OSGi specific.
// It is likey that they are going to be replaced with some
// generic object in the near future.
/**
* Returns Ids of all contributors residing in a given namespace
* @see #getNamespaceOwnerId(long)
*
* @param namespace - namespace name
* @return - array of contributor Ids residing in the namespace
*/
public long[] getNamespaceContributors(String namespace) {
Long theContributorId;
try {
theContributorId = Long.decode(namespace);
} catch (NumberFormatException e) {
theContributorId = new Long(-1);
}
return new long[] {theContributorId.longValue()};
}
/**
* Creates an executable extension. Override this method to supply an alternative processing
* for the creation of executable extensions.
*
* In this implementation registry attempts to instantiate the class specified using
* standard Java reflection mechanism; it assumes that constructor of such class has no arguments.
*
* @see IConfigurationElement#createExecutableExtension(String)
*
* @param contributorName - name of the extension supplier
* @param namespaceOwnerId - Id of the namespace owner
* @param namespaceName - name of the extension point namespace
* @param className - name of the class to be instantiated
* @param initData - initializer data (@see IExecutableExtension); might be null
* @param propertyName - name of the configuration element containing class information
* @param theConfElement - the configuration element containing executable extension
* @return - the object created; might be null
* @throws CoreException
*/
public Object createExecutableExtension(String contributorName, long namespaceOwnerId, String namespaceName, String className, Object initData, String propertyName, IConfigurationElement theConfElement) throws CoreException {
Object result = null;
Class classInstance = null;
try {
classInstance = Class.forName(className);
} catch (ClassNotFoundException e1) {
String message = NLS.bind(RegistryMessages.exExt_findClassError, getNamespace(namespaceOwnerId), className);
throw new CoreException(new Status(IStatus.ERROR, RegistryMessages.OWNER_NAME, IRegistryConstants.PLUGIN_ERROR, message, e1));
}
try {
result = classInstance.newInstance();
} catch (Exception e) {
String message = NLS.bind(RegistryMessages.exExt_instantiateClassError, getNamespace(namespaceOwnerId), className);
throw new CoreException(new Status(IStatus.ERROR, RegistryMessages.OWNER_NAME, IRegistryConstants.PLUGIN_ERROR, message, e));
}
return result;
}
/**
* Override this method to customize scheduling of an extension registry event. Note that this method
* must make the following call to actually process the event:
*
* RegistryStrategy.processChangeEvent(listeners, deltas, registry);
*
* In this implementation of the method registry events are executed in a queue
* on a separate thread (i.e. asynchronously, sequentially).
*
* @param listeners - list of active listeners (thread safe)
* @param deltas - registry deltas (thread safe)
* @param registry - the extension registry (NOT thread safe)
*/
public void scheduleChangeEvent(Object[] listeners, Map deltas, Object registry) {
if (registry instanceof ExtensionRegistry)
((ExtensionRegistry) registry).scheduleChangeEvent(listeners, deltas);
}
/**
* This method performs actual processing of the registry change event. It should
* only be used by overrides of the RegistryStrategy.scheduleChangeEvent.
*
* @param listeners - the list of active listeners
* @param deltas - the extension registry deltas
* @param registry - the extension registry
* @return - status of the operation; null if unexpected type of the registry
* was encountered
*/
public final static IStatus processChangeEvent(Object[] listeners, Map deltas, Object registry) {
if (registry instanceof ExtensionRegistry)
return ((ExtensionRegistry) registry).processChangeEvent(listeners, deltas);
return null;
}
/**
* Override this method to specify debug requirements to the registry. In this
* default implementation debug functionality is turned off.
*
* Note that, in general case, the extension registry plugin doesn't depend on OSGI and,
* therefore, can't use Eclipse .options files to discover debug options.
*
* @return true - perform debug logging and validation
*/
public boolean debug() {
return false;
}
/**
* Override this method to specify debug requirements for the registry event processing.
* In this default implementation debug functionality is turned off.
*
* Note that, in general case, the extension registry plugin doesn't depend on OSGI and,
* therefore, can't use Eclipse .options files to discover debug options.
*
* @return true - perform debug logging and validation of the registry events
*/
public boolean debugRegistryEvents() {
return false;
}
/**
* Specifies if the extension registry should use cache to store
* registry data between invocations.
*
* This basic implementation specifies that registry caching is going to tbe enabled.
*
* @return - true - should use cache; false - don't
*/
public boolean cacheUse() {
return true;
}
/**
* Specifies if lazy cahe loading is used. If cache startegy is not supplied,
* lazy cache loading is used.
*
* This basic implementation specifies that lazy cache loading is going to be used.
*
* @return - true - use lazy extension registry cache loading
*/
public boolean cacheLazyLoading() {
return true;
}
/**
* This method is called as a part of the registry cache validation. The cache is valid
* on the registry startup if the pair {state, time stamp} supplied by the application
* is the same as the {state, time stamp} saved in the registry cache.
*
* This method produces a number that corresponds to the current state of the data stored
* in the registry. Increment the state if registry content changed and the registry cache
* is no longer valid.
*
* Return 0 to indicate that state verification is not required.
*
* @return number indicating state of the application data
*/
public long cacheComputeState() {
return 0;
}
/**
* This method is called as a part of the registry cache validation. The cache is valid
* on the registry startup if the pair {state, time stamp} supplied by the application
* is the same as the {state, time stamp} saved in the registry cache.
*
* This method calculates current time stamp for the elements stored in the extension
* registry. Treat this number as a hash code for the data stored in the registry.
* It stays the same as long as the registry content is not changing. It becomes a different
* number as the registry content gets modified.
*
* Return 0 to indicate that no time stamp verification is required.
*
* @return the time stamp calculated with the application data
*/
public long cacheComputeTimeStamp() {
return 0;
}
/**
* In case if the primary cache location has no data in it, the registry
* attemps to get cached information from this alternative location. The cache
* at alternative location is always considered read-only.
*
* Return null if alternative cache location is not supported.
*
* @return - directory containing the alternative cache location
*/
public File cacheAlternativeLocation() {
return null;
}
private SAXParserFactory theXMLParserFactory = null;
/**
* The parser used by the registry to parse descriptions of extension points
* and extensions from the XML input streams.
*
* @see org.eclipse.equinox.registry.IExtensionRegistry#addContribution(java.io.InputStream, long, String, ResourceBundle, Object)
*
* @return XML parser
*/
public SAXParserFactory getXMLParser() {
if (theXMLParserFactory == null)
theXMLParserFactory = SAXParserFactory.newInstance();
return theXMLParserFactory;
}
}