com.tangosol.net.DefaultConfigurableCacheFactory Maven / Gradle / Ivy
Show all versions of coherence Show documentation
/*
* Copyright (c) 2000, 2022, Oracle and/or its affiliates.
*
* Licensed under the Universal Permissive License v 1.0 as shown at
* https://oss.oracle.com/licenses/upl.
*/
package com.tangosol.net;
import com.oracle.coherence.common.base.Disposable;
import com.oracle.coherence.common.base.Lockable;
import com.tangosol.coherence.config.CacheConfig;
import com.tangosol.coherence.config.ResolvableParameterList;
import com.tangosol.coherence.config.SchemeMappingRegistry;
import com.tangosol.coherence.config.builder.NamedEventInterceptorBuilder;
import com.tangosol.coherence.config.builder.InstanceBuilder;
import com.tangosol.config.expression.NullParameterResolver;
import com.tangosol.config.expression.Parameter;
import com.tangosol.io.AsyncBinaryStore;
import com.tangosol.io.AsyncBinaryStoreManager;
import com.tangosol.io.BinaryStore;
import com.tangosol.io.BinaryStoreManager;
import com.tangosol.io.ClassLoaderAware;
import com.tangosol.io.bdb.BerkeleyDBBinaryStoreManager;
import com.tangosol.io.nio.BinaryMap;
import com.tangosol.io.nio.BinaryMapStore;
import com.tangosol.io.nio.ByteBufferManager;
import com.tangosol.io.nio.MappedBufferManager;
import com.tangosol.io.nio.MappedStoreManager;
import com.tangosol.net.topic.NamedTopic;
import com.tangosol.net.cache.AbstractBundler;
import com.tangosol.net.cache.BinaryEntryStore;
import com.tangosol.net.cache.BundlingNamedCache;
import com.tangosol.net.cache.CacheLoader;
import com.tangosol.net.cache.CacheStore;
import com.tangosol.net.cache.ConfigurableCacheMap;
import com.tangosol.net.cache.LocalCache;
import com.tangosol.net.cache.MapCacheStore;
import com.tangosol.net.cache.NearCache;
import com.tangosol.net.cache.OverflowMap;
import com.tangosol.net.cache.ReadWriteBackingMap;
import com.tangosol.net.cache.SerializationCache;
import com.tangosol.net.cache.SerializationMap;
import com.tangosol.net.cache.SerializationPagedCache;
import com.tangosol.net.cache.SimpleOverflowMap;
import com.tangosol.net.cache.SimpleSerializationMap;
import com.tangosol.net.cache.VersionedBackingMap;
import com.tangosol.net.cache.VersionedNearCache;
import com.tangosol.net.events.EventDispatcherRegistry;
import com.tangosol.net.events.EventInterceptor;
import com.tangosol.net.events.InterceptorRegistry;
import com.tangosol.net.events.annotation.Interceptor;
import com.tangosol.net.events.internal.InterceptorManager;
import com.tangosol.net.events.internal.NamedEventInterceptor;
import com.tangosol.net.events.internal.Registry;
import com.tangosol.net.internal.ScopedCacheReferenceStore;
import com.tangosol.net.management.MBeanHelper;
import com.tangosol.net.partition.ObservableSplittingBackingCache;
import com.tangosol.net.partition.ObservableSplittingBackingMap;
import com.tangosol.net.partition.PartitionAwareBackingMap;
import com.tangosol.net.partition.ReadWriteSplittingBackingMap;
import com.tangosol.net.security.Security;
import com.tangosol.net.security.StorageAccessAuthorizer;
import com.tangosol.run.xml.SimpleElement;
import com.tangosol.run.xml.SimpleValue;
import com.tangosol.run.xml.XmlConfigurable;
import com.tangosol.run.xml.XmlDocument;
import com.tangosol.run.xml.XmlElement;
import com.tangosol.run.xml.XmlHelper;
import com.tangosol.run.xml.XmlValue;
import com.tangosol.util.AbstractKeyBasedMap;
import com.tangosol.util.Base;
import com.tangosol.util.ClassHelper;
import com.tangosol.util.ExternalizableHelper;
import com.tangosol.util.MapListener;
import com.tangosol.util.MapSet;
import com.tangosol.util.NullImplementation;
import com.tangosol.util.ObservableMap;
import com.tangosol.util.RegistrationBehavior;
import com.tangosol.util.ResourceRegistry;
import com.tangosol.util.SafeHashMap;
import com.tangosol.util.SimpleResourceRegistry;
import java.io.File;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
/**
* This class has now been deprecated.
* It remains part of the distribution of Coherence only for the purpose of
* providing backwards compatibility for those applications that have extend it
* or depend on specific implementation semantics.
*
* Developers are strongly encouraged to refactor the implementations that depend
* on this class to use the extension mechanisms prescribed
* by the the {@link ExtensibleConfigurableCacheFactory}.
*
* At some point in the future this class will be removed. No further development
* or enhancement of this class will occur going forward.
*
* It is strongly recommended that developers get a ConfigurableCacheFactory instance via
* CacheFactory.getCacheFactoryBuilder().getConfigurableCacheFactory(), rather than
* instantiate a DefaultConfigurableCacheFactory instance directly.
*
*
*
* DefaultConfigurableCacheFactory provides a facility to access caches declared
* in a "coherence-cache-config.xsd" compliant configuration file.
*
* This class is designed to be easily extendable with a collection of factory
* methods allowing subclasses to customize it by overriding any subset of
* cache instantiation routines or even allowing addition of custom schemes.
*
* There are various ways of using this factory:
*
* ConfigurableCacheFactory factory =
* new DefaultConfigurableCacheFactory(sPath);
* ...
* ClassLoader loader = getClass().getClassLoader();
* NamedCache cacheOne = factory.ensureCache("one", loader);
* NamedCache cacheTwo = factory.ensureCache("two", loader);
*
* Using this approach allows an easy customization by extending the
* DefaultConfigurableCacheFactory and changing the instantiation line:
*
* ConfigurableCacheFactory factory = new CustomConfigurableCacheFactory();
* ...
*
*
* Another option is using the static version of the "ensureCache" call:
*
* ClassLoader loader = getClass().getClassLoader();
* NamedCache cacheOne = CacheFactory.getCache("one", loader);
* NamedCache cacheTwo = CacheFactory.getCache("two", loader);
*
* which uses an instance of ConfigurableCacheFactory obtained by
* {@link CacheFactory#getConfigurableCacheFactory()}.
*
* @see CacheFactory#getCache(String, ClassLoader, NamedCache.Option...)
*
* @author gg 2003.05.26
*
* @since Coherence 2.2
*/
@Deprecated
public class DefaultConfigurableCacheFactory
extends Base
implements ConfigurableCacheFactory
{
// ----- constructors -------------------------------------------------
/**
* Construct a default DefaultConfigurableCacheFactory using the
* default configuration file name.
*/
public DefaultConfigurableCacheFactory()
{
this(loadConfig(FILE_CFG_CACHE));
}
/**
* Construct a DefaultConfigurableCacheFactory using the specified path to
* a "coherence-cache-config.xsd" compliant configuration file or resource.
*
* @param sPath the configuration resource name or file path
*/
public DefaultConfigurableCacheFactory(String sPath)
{
this(loadConfig(sPath));
}
/**
* Construct a DefaultConfigurableCacheFactory using the specified path to
* a "coherence-cache-config.xsd" compliant configuration file or resource.
*
* @param sPath the configuration resource name or file path
* @param loader (optional) ClassLoader that should be used to load the
* configuration resource
*/
public DefaultConfigurableCacheFactory(String sPath, ClassLoader loader)
{
this(loadConfig(sPath, loader), loader);
}
/**
* Construct a DefaultConfigurableCacheFactory using the specified
* configuration XML.
*
* @param xmlConfig the configuration XmlElement
*/
public DefaultConfigurableCacheFactory(XmlElement xmlConfig)
{
this(xmlConfig, null);
}
/**
* Construct a DefaultConfigurableCacheFactory using the specified
* configuration XML.
*
* @param xmlConfig the configuration XmlElement
* @param loader (optional) ClassLoader that should be used to load the
* configuration resource
*/
public DefaultConfigurableCacheFactory(XmlElement xmlConfig, ClassLoader loader)
{
setConfigClassLoader(loader);
setConfig(xmlConfig);
CacheFactory.log("Created cache factory " + this.getClass().getName(), CacheFactory.LOG_INFO);
}
// ----- ConfigurableCacheFactory interface -----------------------------
/**
* Obtain the factory configuration XML.
*
* @return the configuration XML
*/
public XmlElement getConfig()
{
return (XmlElement) m_xmlConfig.clone();
}
/**
* Obtain a mutable reference to the factory configuration XML.
*
* Note: The caller may not modify the resulting XmlElement's in any way.
*
* @return the configuration XML
*/
protected XmlElement getConfigUnsafe()
{
return m_xmlConfig;
}
/**
* Specify the factory configuration XML.
*
* @param xmlConfig the configuration XML
*/
public void setConfig(XmlElement xmlConfig)
{
xmlConfig = (XmlElement) xmlConfig.clone();
XmlHelper.replaceSystemProperties(xmlConfig, "system-property");
String sScopeName = xmlConfig.getSafeElement("scope-name").getString();
if (!sScopeName.isEmpty())
{
m_sScopeName = sScopeName;
}
m_xmlConfig = xmlConfig;
ResourceRegistry registry = m_registry;
if (registry != null)
{
registry.dispose();
}
registry = m_registry = new SimpleResourceRegistry();
Registry eventRegistry = new Registry();
registry.registerResource(InterceptorRegistry.class, eventRegistry);
registry.registerResource(EventDispatcherRegistry.class, eventRegistry);
// No-op InterceptorManager
CacheConfig cacheConfig = new CacheConfig(new NullParameterResolver());
cacheConfig.addCacheMappingRegistry(new SchemeMappingRegistry());
InterceptorManager manager = new InterceptorManager(cacheConfig, getConfigClassLoader(), registry);
registry.registerResource(InterceptorManager.class, manager);
configureInterceptors(xmlConfig);
m_store.clear();
}
/**
* {@inheritDoc}
*/
@Override
public NamedCache ensureCache(String sCacheName,
ClassLoader loader,
NamedCache.Option... options)
{
NamedCache cache;
if (sCacheName == null || sCacheName.length() == 0)
{
throw new IllegalArgumentException("Cache name cannot be null");
}
loader = ensureClassLoader(loader);
ScopedCacheReferenceStore store = m_store;
while (true)
{
cache = store.getCache(sCacheName, loader);
if (cache != null && cache.isActive())
{
// the common path; the cache reference is active and reusable
checkPermission(cache);
return cache;
}
if (cache != null && !cache.isDestroyed() && !cache.isReleased())
{
try
{
// this can only indicate that the cache was "disconnected" due to a
// network failure or an abnormal cache service termination;
// the underlying cache service will “restart" during the very next call
// to any of the NamedCache API method (see COH-15083 for details)
checkPermission(cache);
return cache;
}
catch (IllegalStateException e)
{
// Fail to access due to cluster or service being explicitly stopped or shutdown
// and not properly restarted.
// Remove this cache and the process of returning a new one ensures all services needed by cache.
store.releaseCache(cache, loader);
}
}
store.clearInactiveCacheRefs();
CacheInfo infoCache = findSchemeMapping(sCacheName);
XmlElement xmlScheme = resolveScheme(infoCache);
String sSchemeType = xmlScheme.getName();
int nSchemeType = translateSchemeType(sSchemeType);
int iHashCurrent = loader == null ? 0 : loader.hashCode();
xmlScheme.addAttribute("tier").setString("front"); // mark the "entry point"
pushCacheContext("tier=front,loader=" + iHashCurrent);
cache = configureCache(infoCache, xmlScheme, loader);
// TODO: The knowledge of transactional cache is left out of
// ScopedReferenceStore. We should re-consider.
if (nSchemeType == SCHEME_TRANSACTIONAL)
{
return cache;
}
if (store.putCacheIfAbsent(cache, loader) == null)
{
break;
}
}
//NOTE: this is an unsafe cast as DCCF doesn't support type checking
return cache;
}
/**
* {@inheritDoc}
*/
public void releaseCache(NamedCache cache)
{
releaseCache(cache, /*fDestroy*/ false);
}
/**
* {@inheritDoc}
*/
public void destroyCache(NamedCache cache)
{
releaseCache(cache, /*fDestroy*/ true);
}
/**
* This method will throw an {@link UnsupportedOperationException} as
* {@link NamedTopic}s are not supported by DefaultConfigurableCacheFactory.
*/
@Override
public NamedTopic ensureTopic(String sName, ClassLoader loader, NamedTopic.Option... options)
{
throw new UnsupportedOperationException("NamedTopic is not supported by DefaultConfigurableCacheFactory");
}
/**
* This method will throw an {@link UnsupportedOperationException} as
* {@link NamedTopic}s are not supported by DefaultConfigurableCacheFactory.
*/
@Override
public void releaseTopic(NamedTopic> topic)
{
throw new UnsupportedOperationException("NamedTopic is not supported by DefaultConfigurableCacheFactory");
}
/**
* This method will throw an {@link UnsupportedOperationException} as
* {@link NamedTopic}s are not supported by DefaultConfigurableCacheFactory.
*/
@Override
public void destroyTopic(NamedTopic> topic)
{
throw new UnsupportedOperationException("NamedTopic is not supported by DefaultConfigurableCacheFactory");
}
/**
* {@inheritDoc}
*/
public Service ensureService(String sServiceName)
{
return ensureService(findServiceScheme(sServiceName));
}
/**
* {@inheritDoc}
*/
@Override
public synchronized void activate()
{
// do nothing
}
/**
* {@inheritDoc}
*/
@Override
public synchronized void dispose()
{
// do nothing
}
/**
* {@inheritDoc}
*/
public boolean isCacheActive(String sCacheName, ClassLoader loader)
{
return m_store.getCache(sCacheName, ensureClassLoader(loader)) != null;
}
// ----- helpers and inheritance support --------------------------------
/**
* Return the scope name for this ConfigurableCacheFactory. If specified,
* this name will be used as a prefix for the name of all services created
* by this factory.
*
* @return the scope name for this ConfigurableCacheFactory; may be null
*/
public String getScopeName()
{
return m_sScopeName;
}
/**
* Set the scope name for this ConfigurableCacheFactory. Note that this
* method should only be invoked by a {@link CacheFactoryBuilder}
* when initializing this ConfigurableCacheFactory instance.
*
* This method will be removed in a future release.
*
* @param sScopeName the scope name for this ConfigurableCacheFactory
*
* @throws IllegalStateException if this setter is invoked after a
* service start
*/
public void setScopeName(String sScopeName)
{
if (m_store.getNames().isEmpty())
{
m_sScopeName = sScopeName;
}
else
{
throw new IllegalStateException("Scope cannot be set after service start");
}
}
/**
* Return the class loader used to load the configuration for this factory.
*
* @return the class loader to use for loading the config
*/
protected ClassLoader getConfigClassLoader()
{
return m_loader;
}
/**
* Return the {@link ResourceRegistry} for this factory.
*
* @return the ResourceRegistry for this factory
*/
public ResourceRegistry getResourceRegistry()
{
return m_registry;
}
/**
* Return the {@link InterceptorRegistry} used to register event interceptors
* for this factory.
*
* @return the InterceptorRegistry for this factory
*/
public InterceptorRegistry getInterceptorRegistry()
{
ResourceRegistry registry = m_registry;
return registry == null ? null : registry.getResource(InterceptorRegistry.class);
}
/**
* Check if the current user is allowed to "join" the cache.
*
* @param cache the cache
*
* @throws SecurityException if permission is denied
*/
protected static void checkPermission(NamedCache cache)
{
// This is a secondary check to make sure that cached references
// are allowed with the current Subject. The primary check is in
// SafeCacheService.ensureCache().
Service service = cache.getCacheService();
Security.checkPermission(service.getCluster(), service.getInfo().
getServiceName(), cache.getCacheName(), "join");
}
/**
* Set the class loader used to load the configuration for this factory.
*
* @param loader the class loader to use for loading the config
*/
public void setConfigClassLoader(ClassLoader loader)
{
m_loader = ensureClassLoader(loader);
}
/**
* Load the configuration from a file or resource.
*
* @param sName the name of file or resource.
*
* @return the configuration XML
*/
public static XmlDocument loadConfig(String sName)
{
return loadConfig(sName, null);
}
/**
* Load the configuration from a file or resource.
*
* @param sName the name of file or resource.
* @param loader (optional) ClassLoader that should be used to load the
* configuration resource
*
* @return the configuration XML
*/
public static XmlDocument loadConfig(String sName, ClassLoader loader)
{
return XmlHelper.loadFileOrResource(sName, "cache configuration", loader);
}
/**
* Load the configuration as a resource.
*
* @param sResource the resource name
* @param loader (optional) ClassLoader that should be used to load the
* configuration resource
*
* @return the configuration XML
*/
public static XmlDocument loadConfigAsResource(String sResource, ClassLoader loader)
{
return loadConfig(sResource, loader);
}
/**
* Load the configuration from a file or directory.
*
* @param file file or directory to load the configuration from
*
* @return the configuration XML
*/
public static XmlDocument loadConfigFromFile(File file)
{
if (file.isDirectory())
{
file = new File(file, FILE_CFG_CACHE);
}
return loadConfig(file.getAbsolutePath(), null);
}
/**
* In the configuration XML find a "cache-mapping" element associated with a
* given cache name.
*
* @param sCacheName the value of the "cache-name" element to look for
*
* @return a CacheInfo object associated with a given cache name
*/
public CacheInfo findSchemeMapping(String sCacheName)
{
XmlElement xmlDefaultMatch = null;
XmlElement xmlPrefixMatch = null;
XmlElement xmlExactMatch = null;
String sSuffix = null;
for (Iterator iter = m_xmlConfig.getSafeElement("caching-scheme-mapping").
getElements("cache-mapping"); iter.hasNext();)
{
XmlElement xmlMapping = (XmlElement) iter.next();
String sName = xmlMapping.getSafeElement("cache-name").getString();
if (sName.equals(sCacheName))
{
xmlExactMatch = xmlMapping;
break;
}
else if (sName.equals("*"))
{
xmlDefaultMatch = xmlMapping;
}
else
{
int cchPrefix = sName.indexOf('*');
if (cchPrefix >= 0)
{
String sPrefix = sName.substring(0, cchPrefix);
if (sCacheName.startsWith(sPrefix))
{
if (cchPrefix != sName.length() - 1)
{
throw new IllegalArgumentException(
"Invalid wildcard pattern:\n" + xmlMapping);
}
xmlPrefixMatch = xmlMapping;
sSuffix = sCacheName.substring(cchPrefix);
}
}
}
}
XmlElement xmlMatch;
if (xmlExactMatch != null)
{
xmlMatch = xmlExactMatch;
sSuffix = "";
}
else if (xmlPrefixMatch != null)
{
xmlMatch = xmlPrefixMatch;
}
else
{
xmlMatch = xmlDefaultMatch;
sSuffix = sCacheName;
}
if (xmlMatch == null)
{
throw new IllegalArgumentException(
"No scheme for cache: \"" + sCacheName +'"');
}
String sScheme = xmlMatch.getSafeElement("scheme-name").getString();
Map mapAttr = new HashMap();
for (Iterator iter = xmlMatch.getSafeElement("init-params").
getElements("init-param"); iter.hasNext();)
{
XmlElement xmlParam = (XmlElement) iter.next();
String sName = xmlParam.getSafeElement("param-name" ).getString();
String sValue = xmlParam.getSafeElement("param-value").getString();
if (sName.length() != 0)
{
int ofReplace = sValue.indexOf('*');
if (ofReplace >= 0 && sSuffix != null)
{
sValue = sValue.substring(0, ofReplace) + sSuffix +
sValue.substring(ofReplace + 1);
}
mapAttr.put(sName, sValue);
}
}
return new CacheInfo(sCacheName, sScheme, mapAttr);
}
/**
* In the configuration XML find a "scheme" element associated with a
* given cache and resolve it (recursively) using the "scheme-ref"
* elements. The returned XML is always a clone of the actual configuration
* and could be safely modified.
*
* @param info the cache info
*
* @return a resolved "scheme" element associated with a given cache
*/
public XmlElement resolveScheme(CacheInfo info)
{
XmlElement xmlScheme = findScheme(info.getSchemeName());
info.replaceAttributes(xmlScheme);
return resolveScheme(xmlScheme, info, false, true);
}
/**
* In the configuration XML find a "scheme" element associated with a given
* scheme name.
*
* @param sSchemeName the value of the "scheme-name" element to look for
*
* @return a "scheme" element associated with a given scheme name
*/
protected XmlElement findScheme(String sSchemeName)
{
XmlElement xmlScheme = findScheme(m_xmlConfig, sSchemeName);
if (xmlScheme != null)
{
return (XmlElement) xmlScheme.clone();
}
throw new IllegalArgumentException("Missing scheme: \"" + sSchemeName + '"');
}
/**
* In the specified configuration XML, find a "scheme" element associated with
* the specified scheme name.
*
* @param xmlConfig the xml configuration
*
* @param sSchemeName the value of the "scheme-name" element to look for
* @return a "scheme" element associated with a given scheme name, or null if
* none is found
*/
protected static XmlElement findScheme(XmlElement xmlConfig, String sSchemeName)
{
if (sSchemeName != null)
{
for (Iterator iter = xmlConfig.getSafeElement("caching-schemes").
getElementList().iterator(); iter.hasNext();)
{
XmlElement xml = (XmlElement) iter.next();
if (xml.getSafeElement("scheme-name").getString().equals(sSchemeName))
{
return xml;
}
}
}
return null;
}
/**
* Collect a map keyed by service names with values of corresponding service
* schemes in the specified cache configuration.
*
* @param xmlConfig the cache configuration
*
* @return a map keyed by service names with values of service schemes
*/
public static Map collectServiceSchemes(XmlElement xmlConfig)
{
HashMap mapService = new HashMap();
for (Iterator iter = xmlConfig.getSafeElement("caching-schemes").
getElementList().iterator(); iter.hasNext();)
{
XmlElement xmlScheme = (XmlElement) iter.next();
collectServiceSchemes(xmlScheme, xmlConfig, mapService);
}
return mapService;
}
/**
* Collect the service-schemes referenced by the specified scheme element in
* the cache configuration and update the specified mapping of service names
* to the associated service schemes.
*
* @param xmlScheme a scheme element
* @param xmlConfig the cache configuration
* @param mapService a map of service name to service scheme
*/
protected static void collectServiceSchemes(XmlElement xmlScheme, XmlElement xmlConfig, HashMap mapService)
{
String sSchemeType = xmlScheme.getName();
String sServiceName = xmlScheme.getSafeElement("service-name").getString();
String sServiceType = null;
switch (translateStandardSchemeType(sSchemeType))
{
case SCHEME_REPLICATED:
sServiceType = CacheService.TYPE_REPLICATED;
break;
case SCHEME_OPTIMISTIC:
sServiceType = CacheService.TYPE_OPTIMISTIC;
break;
case SCHEME_DISTRIBUTED:
sServiceType = CacheService.TYPE_DISTRIBUTED;
break;
case SCHEME_LOCAL:
case SCHEME_OVERFLOW:
case SCHEME_DISK:
case SCHEME_EXTERNAL:
case SCHEME_EXTERNAL_PAGED:
case SCHEME_FLASHJOURNAL:
case SCHEME_RAMJOURNAL:
case SCHEME_CLASS:
sServiceType = CacheService.TYPE_LOCAL;
break;
case SCHEME_NEAR:
{
XmlElement xmlBack = resolveScheme(
xmlConfig, xmlScheme.getSafeElement("back-scheme"), null, true, true, false);
collectServiceSchemes(xmlBack, xmlConfig, mapService);
return;
}
case SCHEME_VERSIONED_NEAR:
{
XmlElement xmlVer = resolveScheme(
xmlConfig, xmlScheme.getSafeElement("version-transient-scheme"), null, true, true, false);
XmlElement xmlBack = resolveScheme(
xmlConfig, xmlScheme.getSafeElement("back-scheme"), null, true, true, false);
collectServiceSchemes(xmlVer, xmlConfig, mapService);
collectServiceSchemes(xmlBack, xmlConfig, mapService);
return;
}
case SCHEME_INVOCATION:
sServiceType = InvocationService.TYPE_DEFAULT;
break;
case SCHEME_PROXY:
sServiceType = "Proxy";
break;
case SCHEME_REMOTE_CACHE:
sServiceType = CacheService.TYPE_REMOTE;
break;
case SCHEME_REMOTE_INVOCATION:
sServiceType = InvocationService.TYPE_REMOTE;
break;
}
if (sServiceName.length() == 0)
{
sServiceName = sServiceType;
}
if (sServiceName != null)
{
mapService.put(sServiceName, xmlScheme);
}
}
/**
* In the configuration XML find a "scheme" element associated with a
* given service name.
*
* @param sServiceName the value of the "service-name" element to look for
*
* @return a "scheme" element associated with a given service name
*/
protected XmlElement findServiceScheme(String sServiceName)
{
if (sServiceName != null)
{
for (Iterator iter = m_xmlConfig.getSafeElement("caching-schemes").
getElementList().iterator(); iter.hasNext();)
{
XmlElement xml = (XmlElement) iter.next();
if (xml.getSafeElement("service-name").getString().equals(sServiceName))
{
return (XmlElement) xml.clone();
}
}
}
throw new IllegalArgumentException("Missing scheme for service: \"" + sServiceName + '"');
}
/**
* Resolve the specified "XYZ-scheme" by retrieving the base element
* referred to by the "scheme-ref" element, resolving it recursively,
* and combining it with the specified overrides and cache specific attributes.
*
* @param xmlConfig the cache configuration xml
* @param xmlScheme a scheme element to resolve
* @param info the cache info (optional)
* @param fChild if true, the actual cache scheme is the only "xyz-scheme"
* child of the specified xmlScheme element;
* otherwise it's the xmlScheme element itself
* @param fRequired if true, the child scheme must be present; false otherwise
* @param fApply if true, apply the specified overrides and cache-specific
* attributes to the base scheme element; otherwise return
* a reference to the base scheme element
*
* @return a "scheme" element referred to by the "scheme-ref" value;
* null if the child is missing and is not required
*/
protected static XmlElement resolveScheme(XmlElement xmlConfig, XmlElement xmlScheme,
CacheInfo info, boolean fChild, boolean fRequired, boolean fApply)
{
if (fChild)
{
XmlElement xmlChild = null;
for (Iterator iter = xmlScheme.getElementList().iterator(); iter.hasNext();)
{
XmlElement xml = (XmlElement) iter.next();
if (xml.getName().endsWith("-scheme"))
{
if (xmlChild == null)
{
xmlChild = xml;
}
else
{
throw new IllegalArgumentException(
"Scheme contains more then one child scheme:\n" + xmlScheme);
}
}
}
if (xmlChild == null)
{
if (fRequired)
{
String sName = xmlScheme.getName();
if (xmlScheme == xmlScheme.getParent().getElement(sName))
{
throw new IllegalArgumentException(
"Child scheme is missing at:\n" + xmlScheme);
}
else
{
throw new IllegalArgumentException(
"Element \"" + sName + "\" is missing at:\n" + xmlScheme.getParent());
}
}
return null;
}
xmlScheme = xmlChild;
}
String sRefName = xmlScheme.getSafeElement("scheme-ref").getString();
if (sRefName.length() == 0)
{
return xmlScheme;
}
XmlElement xmlBase = findScheme(xmlConfig, sRefName);
if (xmlBase == null)
{
throw new IllegalArgumentException("Unresolved reference to scheme:\n" +
sRefName);
}
xmlBase = fApply ? (XmlElement) xmlBase.clone() : xmlBase;
if (!xmlScheme.getName().equals(xmlBase.getName()))
{
throw new IllegalArgumentException("Reference does not match the scheme type: scheme=\n" +
xmlScheme + "\nbase=" + xmlBase);
}
if (xmlScheme.equals(xmlBase))
{
throw new IllegalArgumentException("Circular reference in scheme:\n" +
xmlScheme);
}
if (info != null)
{
info.replaceAttributes(xmlBase);
}
XmlElement xmlResolve = resolveScheme(
xmlConfig, xmlBase, info, false, false, fApply);
if (fApply)
{
for (Iterator iter = xmlScheme.getElementList().iterator();
iter.hasNext();)
{
XmlHelper.replaceElement(xmlResolve, (XmlElement) iter.next());
}
}
return xmlResolve;
}
/**
* Resolve the specified "XYZ-scheme" by retrieving the base element
* referred to by the "scheme-ref" element, resolving it recursively,
* and combining it with the specified overrides and cache specific attributes.
*
* @param xmlScheme a scheme element to resolve
* @param info the cache info (optional)
* @param fChild if true, the actual cache scheme is the only "xyz-scheme"
* child of the specified xmlScheme element;
* otherwise it's the xmlScheme element itself
* @param fRequired if true, the child scheme must be present; false otherwise
*
* @return a "scheme" element associated with a given cache name; null if
* the child is missing and is not required
*/
protected XmlElement resolveScheme(XmlElement xmlScheme, CacheInfo info, boolean fChild, boolean fRequired)
{
return resolveScheme(m_xmlConfig, xmlScheme, info, fChild, fRequired, true);
}
/**
* Obtain the NamedCache reference for the cache service defined by the
* specified scheme.
*
* @param info the cache info
* @param xmlScheme the scheme element for the cache
* @param loader (optional) ClassLoader that should be used to
* deserialize objects in the cache
*
* @return NamedCache instance
*/
protected NamedCache ensureCache(
CacheInfo info, XmlElement xmlScheme, ClassLoader loader)
{
try
{
// The CacheService will check permission for the service and
// cache. If a cache reference is handed out later, then permission
// should be checked using DefaultConfigurableCacheFactory.
// checkPermission().
CacheService service = (CacheService) ensureService(xmlScheme);
return service.ensureCache(info.getCacheName(), loader);
}
catch (ClassCastException e)
{
throw new IllegalArgumentException("Invalid scheme:\n" + xmlScheme);
}
}
/**
* Ensure the service for the specified scheme.
*
* @param xmlScheme the scheme
*
* @return running Service corresponding to the scheme
*/
public Service ensureService(XmlElement xmlScheme)
{
return ensureServiceInternal(
resolveScheme(xmlScheme, null, false, false));
}
/**
* Ensure the service for the specified scheme.
*
* @param xmlScheme the scheme
*
* @return running Service corresponding to the scheme
*/
protected Service ensureServiceInternal(XmlElement xmlScheme)
{
String sSchemeType = xmlScheme.getName();
int nSchemeType = translateSchemeType(sSchemeType);
Cluster cluster;
String sServiceType;
switch (nSchemeType)
{
case SCHEME_REPLICATED:
cluster = CacheFactory.ensureCluster();
sServiceType = CacheService.TYPE_REPLICATED;
break;
case SCHEME_OPTIMISTIC:
cluster = CacheFactory.ensureCluster();
sServiceType = CacheService.TYPE_OPTIMISTIC;
break;
case SCHEME_DISTRIBUTED:
cluster = CacheFactory.ensureCluster();
sServiceType = CacheService.TYPE_DISTRIBUTED;
break;
case SCHEME_LOCAL:
case SCHEME_OVERFLOW:
case SCHEME_DISK:
case SCHEME_EXTERNAL:
case SCHEME_EXTERNAL_PAGED:
case SCHEME_CLASS:
case SCHEME_FLASHJOURNAL:
case SCHEME_RAMJOURNAL:
cluster = CacheFactory.getCluster();
sServiceType = CacheService.TYPE_LOCAL;
break;
case SCHEME_NEAR:
return ensureServiceInternal(
resolveScheme(xmlScheme.getSafeElement("back-scheme"), null, true, true));
case SCHEME_VERSIONED_NEAR:
{
XmlElement xmlVer = resolveScheme(
xmlScheme.getSafeElement("version-transient-scheme"), null, true, true);
XmlElement xmlBack = resolveScheme(
xmlScheme.getSafeElement("back-scheme"), null, true, true);
ensureServiceInternal(xmlVer);
return ensureServiceInternal(xmlBack);
}
case SCHEME_INVOCATION:
cluster = CacheFactory.ensureCluster();
sServiceType = InvocationService.TYPE_DEFAULT;
break;
case SCHEME_PROXY:
cluster = CacheFactory.ensureCluster();
sServiceType = "Proxy";
break;
case SCHEME_REMOTE_CACHE:
cluster = CacheFactory.getCluster();
sServiceType = CacheService.TYPE_REMOTE;
break;
case SCHEME_REMOTE_INVOCATION:
cluster = CacheFactory.getCluster();
sServiceType = InvocationService.TYPE_REMOTE;
break;
case SCHEME_TRANSACTIONAL:
throw new UnsupportedOperationException("Transactions are not supported in Coherence CE");
default:
throw new UnsupportedOperationException("ensureService: " + sSchemeType);
}
String sServiceName = xmlScheme.getSafeElement("service-name").getString();
if (sServiceName.length() == 0)
{
sServiceName = sServiceType;
}
sServiceName = getScopedServiceName(sServiceName);
// Note: SafeCluster implements Lockable (COH-23345)
try (Lockable.Unlockable unlockable = ((Lockable) cluster).exclusively())
{
Service service = cluster.ensureService(sServiceName, sServiceType);
if (service.isRunning())
{
if (service instanceof CacheService)
{
validateBackingMapManager((CacheService) service);
}
}
else
{
// merge the standard service config parameters
XmlElement xmlConfig = CacheFactory.getServiceConfig(sServiceType);
if (xmlConfig != null)
{
List listStandard = xmlConfig.getElementList();
for (int i = 0, c = listStandard.size(); i < c; i++)
{
XmlElement xmlParamStandard = (XmlElement) listStandard.get(i);
String sParamName = xmlParamStandard.getName();
XmlElement xmlParam = xmlScheme.getElement(sParamName);
if (xmlParam != null && !XmlHelper.isEmpty(xmlParam))
{
xmlParam = resolveScheme
(m_xmlConfig, xmlParam, null, false, false, true);
listStandard.set(i, xmlParam.clone());
}
}
}
// resolve nested serializers for remote and proxy services
XmlElement xmlSub;
switch (nSchemeType)
{
case SCHEME_PROXY:
xmlSub = xmlConfig.getSafeElement("acceptor-config");
resolveSerializer(xmlSub);
xmlSub = xmlSub.getElement("tcp-acceptor");
if (xmlSub != null)
{
resolveSocketProvider(xmlSub);
}
break;
case SCHEME_REMOTE_CACHE:
case SCHEME_REMOTE_INVOCATION:
xmlSub = xmlConfig.getSafeElement("initiator-config");
resolveSerializer(xmlSub);
xmlSub = xmlSub.getElement("tcp-initiator");
if (xmlSub != null)
{
resolveSocketProvider(xmlSub);
}
break;
}
resolveSerializer(xmlConfig);
// configure and start the service
service.configure(xmlConfig);
if (service instanceof CacheService)
{
BackingMapManager mgr =
instantiateBackingMapManager(nSchemeType, xmlScheme);
registerBackingMapManager(mgr);
((CacheService) service).setBackingMapManager(mgr);
}
startService(service);
}
return service;
}
}
/**
* Start the given {@link Service}. Extensions of this class can
* override this method to provide pre/post start functionality.
*
* @param service the {@link Service} to start
*/
protected void startService(Service service)
{
service.start();
}
/**
* Apply the scope name prefix to the given service name. The
* name will be formatted as such: [scope name]:[service name]
*
* @param sServiceName the service name
*
* @return the service name with scope prefix, if there is a scope
* name configured
*/
public String getScopedServiceName(String sServiceName)
{
String sScopeName = getScopeName();
return (sScopeName == null || sScopeName.length() == 0) ?
sServiceName : sScopeName + ":" + sServiceName;
}
/**
* Resolve and inject service serializer elements based on defaults
* defined in the cache configuration.
*
* @param xmlConfig the configuration element to examine and modify
*/
protected void resolveSerializer(XmlElement xmlConfig)
{
// check if the "serializer" element is missing or empty
XmlElement xmlSerializer = xmlConfig.getElement("serializer");
if (xmlSerializer == null || XmlHelper.isEmpty(xmlSerializer))
{
// remove an empty serializer element from the service config
if (xmlSerializer != null)
{
XmlHelper.removeElement(xmlConfig, "serializer");
}
// apply the default serializer (if specified)
xmlSerializer = getConfig().findElement("defaults/serializer");
if (xmlSerializer != null)
{
xmlConfig.getElementList().add(xmlSerializer);
}
}
}
/**
* Resolve and inject service socket-provider elements based on defaults
* defined in the cache configuration.
*
* @param xmlConfig the configuration element to examine and modify
*/
protected void resolveSocketProvider(XmlElement xmlConfig)
{
// check if the "socket-provider" element is missing or empty
XmlElement xmlProvider = xmlConfig.getElement("socket-provider");
if (xmlProvider == null || XmlHelper.isEmpty(xmlProvider))
{
// remove an empty provider element from the service config
if (xmlProvider != null)
{
XmlHelper.removeElement(xmlConfig, "socket-provider");
}
// apply the default socket-provider (if specified)
xmlProvider = getConfig().findElement("defaults/socket-provider");
if (xmlProvider != null)
{
xmlConfig.getElementList().add(xmlProvider);
}
}
}
/**
* Instantiate a BackingMapManager for a given scheme type.
*
* Note: we rely on the BackingMapManager implementations to have their
* equals() method test for object identity using the "==" operator (in
* other words not to override the hashCode() and equals() methods).
*
* @param nSchemeType the scheme type (one of the SCHEME-* constants)
* @param xmlScheme the scheme used to configure the corresponding service
*
* @return a new BackingMapManager instance
*/
protected BackingMapManager instantiateBackingMapManager(
int nSchemeType, XmlElement xmlScheme)
{
switch (nSchemeType)
{
case SCHEME_TRANSACTIONAL:
default:
return new Manager();
}
}
/**
* Register the specified BackingMapManager as a "valid" one. That registry
* is used to identify services configured and started by this factory and
* prevent accidental usage of (potentially incompatible) cache services
* with the same name created by other factories.
*
* @param mgr a BackingMapManager instance instantiated by this factory
*/
protected void registerBackingMapManager(BackingMapManager mgr)
{
m_setManager.add(mgr);
}
/**
* Ensures that the backing map manager of the specified service was
* configured by this (or equivalent) factory. This validation is performed
* to prevent accidental usage of (potentially incompatible) cache services
* with the same name created by other factories.
*
* @param service the CacheService to validate
*/
protected void validateBackingMapManager(CacheService service)
{
BackingMapManager manager = service.getBackingMapManager();
if (m_setManager.contains(manager))
{
return;
}
if (!(manager instanceof Manager))
{
throw new IllegalStateException("Service \"" + service.getInfo().getServiceName() +
"\" has been started " + (manager == null ?
"without a BackingMapManager" :
"with a non-compatible BackingMapManager: " + manager));
}
DefaultConfigurableCacheFactory that = ((Manager) manager).getCacheFactory();
if (that != this && !equals(that.getConfig(), this.getConfig()))
{
throw new IllegalStateException("Service \"" + service.getInfo().getServiceName() +
"\" has been started by the factory with a different configuration descriptor");
}
}
/**
* Ensures a cache for given scheme.
*
* @param info the cache info
* @param xmlScheme the corresponding scheme
* @param loader ClassLoader that should be used to deserialize
* objects in the cache
*
* @return a named cache created according to the description
* in the configuration
*/
public NamedCache configureCache(CacheInfo info, XmlElement xmlScheme, ClassLoader loader)
{
String sSchemeType = xmlScheme.getName();
NamedCache cache;
switch (translateSchemeType(sSchemeType))
{
case SCHEME_CLASS:
// check whether this class-scheme naturally implements
// the NamedCache and return it without SafeCache "wrapping"
Object oCache = instantiateAny(info, xmlScheme, null, loader);
if (oCache instanceof NamedCache)
{
return (NamedCache) oCache;
}
// break through for any other classes
case SCHEME_REPLICATED:
case SCHEME_OPTIMISTIC:
case SCHEME_LOCAL:
case SCHEME_OVERFLOW:
case SCHEME_DISK:
case SCHEME_EXTERNAL:
case SCHEME_EXTERNAL_PAGED:
case SCHEME_FLASHJOURNAL:
case SCHEME_RAMJOURNAL:
cache = ensureCache(info, xmlScheme, loader);
break;
case SCHEME_DISTRIBUTED:
// TODO: move it up along with other schemes when bundling is
// implemented by the service itself
case SCHEME_REMOTE_CACHE:
{
cache = ensureCache(info, xmlScheme, loader);
XmlElement xmlBundling = xmlScheme.getElement("operation-bundling");
if (xmlBundling != null)
{
cache = instantiateBundlingNamedCache(cache, xmlBundling);
}
break;
}
case SCHEME_NEAR:
{
String sCacheCtx = popCacheContext();
XmlElement xmlFront = resolveScheme(xmlScheme.getSafeElement("front-scheme"), info, true, true);
Map mapFront = configureBackingMap(info, xmlFront, null, loader, null);
XmlElement xmlBack = resolveScheme(xmlScheme.getSafeElement("back-scheme"), info, true, true);
NamedCache cacheBack = configureCache(info, xmlBack, loader);
String sStrategy = xmlScheme.getSafeElement("invalidation-strategy").getString("auto");
int nStrategy = sStrategy.equalsIgnoreCase("none") ? NearCache.LISTEN_NONE
: sStrategy.equalsIgnoreCase("present") ? NearCache.LISTEN_PRESENT
: sStrategy.equalsIgnoreCase("all") ? NearCache.LISTEN_ALL
: sStrategy.equalsIgnoreCase("auto") ? NearCache.LISTEN_AUTO
: sStrategy.equalsIgnoreCase("logical") ? NearCache.LISTEN_LOGICAL
: Integer.MIN_VALUE;
NearCache cacheNear;
if (nStrategy == Integer.MIN_VALUE)
{
CacheFactory.log("Invalid invalidation strategy of '" + sStrategy +
"'; proceeding with default of 'auto'", CacheFactory.LOG_WARN);
nStrategy = NearCache.LISTEN_AUTO;
}
String sSubclass = xmlScheme.getSafeElement("class-name").getString();
if (sSubclass.length() == 0)
{
cacheNear = instantiateNearCache(mapFront, cacheBack, nStrategy);
}
else
{
Object[] aoParam = new Object[] {mapFront, cacheBack, Integer.valueOf(nStrategy)};
cacheNear = (NearCache) instantiateSubclass(sSubclass, NearCache.class, loader,
aoParam, xmlScheme.getElement("init-params"));
}
if (sCacheCtx != null)
{
cacheNear.setRegistrationContext(sCacheCtx);
register(cacheNear, sCacheCtx);
}
cache = cacheNear;
break;
}
case SCHEME_VERSIONED_NEAR:
{
String sCacheCtx = popCacheContext();
XmlElement xmlFront = resolveScheme(xmlScheme.getSafeElement("front-scheme"), info, true, true);
Map mapFront = configureBackingMap(info, xmlFront, null, loader, null);
XmlElement xmlBack = resolveScheme(xmlScheme.getSafeElement("back-scheme"), info, true, true);
NamedCache cacheBack = ensureCache(info, xmlBack, loader);
XmlElement xmlVer = xmlScheme.getSafeElement("version-transient-scheme");
String sSuffix = xmlVer.getSafeElement("cache-name-suffix").getString("-version");
xmlVer = resolveScheme(xmlVer, info, true, true);
NamedCache cacheVer = ensureCache(info.getSyntheticInfo(sSuffix), xmlVer, loader);
NearCache cacheNear;
String sSubclass = xmlScheme.getSafeElement("class-name").getString();
if (sSubclass.length() == 0)
{
cacheNear = instantiateVersionedNearCache(mapFront, cacheBack, cacheVer);
}
else
{
Object[] aoParam = new Object[] {mapFront, cacheBack, cacheVer};
cacheNear = (NearCache) instantiateSubclass(sSubclass, VersionedNearCache.class, loader,
aoParam, xmlScheme.getElement("init-params"));
}
if (sCacheCtx != null)
{
cacheNear.setRegistrationContext(sCacheCtx);
register(cacheNear, sCacheCtx);
}
cache = cacheNear;
break;
}
case SCHEME_TRANSACTIONAL:
throw new UnsupportedOperationException("Transactions are not supported in Coherence CE");
default:
throw new UnsupportedOperationException("configureCache: " + sSchemeType);
}
verifyMapListener(info, cache, xmlScheme, null, loader, null);
return cache;
}
/**
* Construct an NearCache using the specified parameters.
*
* This method exposes a corresponding NearCache
* {@link NearCache#NearCache(Map, NamedCache, int) constructor}
* and is provided for the express purpose of allowing its override.
*/
protected NearCache instantiateNearCache(Map mapFront, NamedCache mapBack, int nStrategy)
{
return new NearCache(mapFront, mapBack, nStrategy);
}
/**
* Construct an VersionedNearCache using the specified parameters.
*
* This method exposes a corresponding VersionedNearCache
* {@link VersionedNearCache#VersionedNearCache(Map, NamedCache, NamedCache) constructor}
* and is provided for the express purpose of allowing its override.
*/
protected VersionedNearCache instantiateVersionedNearCache(Map mapLocal, NamedCache mapDist, NamedCache mapVersion)
{
return new VersionedNearCache(mapLocal, mapDist, mapVersion);
}
/**
* Configures a backing map according to the scheme.
*
* @param info the cache info
* @param xmlScheme the scheme element for cache configuration
* @param context BackingMapManagerContext to be used
* @param loader the ClassLoader to instantiate necessary classes
* @param mapListeners map of registered map listeners keyed by the
* corresponding map references
*
* @return a backing map configured according to the scheme
*/
public Map configureBackingMap(CacheInfo info, XmlElement xmlScheme,
BackingMapManagerContext context, ClassLoader loader, Map mapListeners)
{
xmlScheme = resolveBackingMapScheme(info, xmlScheme);
int nType = translateSchemeType(xmlScheme.getName());
String sCtx = popCacheContext();
Map map;
if (loader == null && context != null)
{
loader = context.getClassLoader();
}
String sPartitioned = xmlScheme.getSafeAttribute("partitioned").getString();
if (isPartitioned(sPartitioned, nType)) // default is "false"
{
if (nType == SCHEME_READ_WRITE_BACKING)
{
map = instantiateReadWriteBackingMap(info, xmlScheme, context, mapListeners);
}
else
{
XmlElement xmlTemp = (XmlElement) xmlScheme.clone();
xmlTemp.setAttribute("partitioned", new SimpleValue("false"));
PartitionedBackingMapManager mgrInner =
new PartitionedBackingMapManager(info, xmlTemp, context, loader);
mgrInner.init(context);
PartitionAwareBackingMap pabm;
String sCacheName = info.getCacheName();
if (sPartitioned.equals("observable") ||
nType == SCHEME_FLASHJOURNAL || nType == SCHEME_RAMJOURNAL)
{
// for "observable" and the flash/ram journal schemes
// the ObservableSplittingBackingMap should be used
pabm = new ObservableSplittingBackingMap(mgrInner, sCacheName);
}
else
{
// default behavior for everything else
pabm = new ObservableSplittingBackingCache(mgrInner, sCacheName);
}
map = pabm;
}
}
else
{
switch (nType)
{
case SCHEME_LOCAL:
map = instantiateLocalCache(info, xmlScheme, context, loader);
break;
case SCHEME_READ_WRITE_BACKING:
if (context == null)
{
throw new IllegalArgumentException("ReadWriteBackingMap requires BackingMapManagerContext");
}
map = instantiateReadWriteBackingMap(info, xmlScheme, context, mapListeners);
break;
case SCHEME_VERSIONED_BACKING:
if (context == null)
{
throw new IllegalArgumentException("VersionedBackingMap requires BackingMapManagerContext");
}
map = instantiateVersionedBackingMap(info, xmlScheme, context, mapListeners);
break;
case SCHEME_OVERFLOW:
map = instantiateOverflowBackingMap(info, xmlScheme, context, loader, mapListeners);
break;
case SCHEME_DISK:
map = instantiateDiskBackingMap(info, xmlScheme, context, loader);
break;
case SCHEME_EXTERNAL:
map = instantiateExternalBackingMap(info, xmlScheme, context, loader);
break;
case SCHEME_EXTERNAL_PAGED:
map = instantiatePagedExternalBackingMap(info, xmlScheme, context, loader);
break;
case SCHEME_REMOTE_CACHE:
map = configureCache(info, xmlScheme, loader);
break;
case SCHEME_FLASHJOURNAL:
map = instantiateFlashJournalBackingMap(info, xmlScheme, context, loader);
break;
case SCHEME_RAMJOURNAL:
map = instantiateRamJournalBackingMap(info, xmlScheme, context, loader);
break;
case SCHEME_CLASS:
map = instantiateMap(info, xmlScheme, context, loader);
break;
default:
throw new UnsupportedOperationException("configureBackingMap: " + xmlScheme.getName());
}
}
// don't register event listeners and MBeans for individual partitions
if (xmlScheme.getAttribute("partition-name") == null)
{
verifyMapListener(info, map, xmlScheme, context, loader, mapListeners);
if (sCtx != null && context != null)
{
register(context.getCacheService(), info.getCacheName(), sCtx, map);
}
}
return map;
}
/**
* Traverse the specified scheme to find an enclosed "backing-map-scheme" or
* a scheme that could serve as such.
*
* @param info the cache info
* @param xmlScheme the scheme element for cache configuration
*
* @return the resolved backing map scheme
*/
public XmlElement resolveBackingMapScheme(CacheInfo info, XmlElement xmlScheme)
{
String sSchemeType = xmlScheme.getName();
switch (translateSchemeType(sSchemeType))
{
case SCHEME_REPLICATED:
case SCHEME_OPTIMISTIC:
case SCHEME_DISTRIBUTED:
{
XmlElement xmlBM = xmlScheme.getSafeElement("backing-map-scheme");
XmlElement xmlMap = resolveScheme(xmlBM, info, true, true);
// transfer a non-default value as an attribute to the actual map config
XmlElement xmlPartitioned = xmlBM.getSafeElement("partitioned");
if (xmlPartitioned != null)
{
xmlMap.addAttribute("partitioned").setString(xmlPartitioned.getString());
}
return xmlMap;
}
case SCHEME_NEAR:
{
XmlElement xmlBack = resolveScheme(xmlScheme.getSafeElement("back-scheme"), info, true, true);
return resolveBackingMapScheme(info, xmlBack);
}
case SCHEME_VERSIONED_NEAR:
{
// we need to figure out what backing map is requested:
// the one for the cache data itself or the one for version info
XmlElement xmlBack = resolveScheme(xmlScheme.getSafeElement("back-scheme"), info, true, true);
XmlElement xmlVer = xmlScheme.getSafeElement("version-transient-scheme");
String sSuffix = xmlVer.getSafeElement("cache-name-suffix").getString("-version");
xmlVer = resolveScheme(xmlVer, info, true, true);
return resolveBackingMapScheme(info,
info.getCacheName().endsWith(sSuffix) ? xmlVer : xmlBack);
}
default:
return xmlScheme;
}
}
/**
* Check whether or not a MapListener has to be instantiated and
* added to a Map according to a scheme definition.
*
* @param info the cache info
* @param map an ObservableMap to add a listener to
* @param xmlScheme the corresponding scheme
* @param context BackingMapManagerContext to be used
* @param loader ClassLoader that should be used to instantiate
* a MapListener object
* @param mapListeners map of registered map listeners keyed by the
* corresponding map references
*
* @throws IllegalArgumentException if the listener is required, but the
* map does not implement ObservableMap interface or if the
* listener cannot be instantiated
*/
protected void verifyMapListener(CacheInfo info, Map map, XmlElement xmlScheme,
BackingMapManagerContext context, ClassLoader loader, Map mapListeners)
{
// CONSIDER: allow to configure filter-based listener and replace
// mapListeners values with MapListenerSupport objects
XmlElement xmlListener = xmlScheme.getSafeElement("listener");
XmlElement xmlClass = resolveScheme(xmlListener, info, true, false);
if (xmlClass != null)
{
String sTier = xmlScheme.getSafeAttribute("tier").getString();
if (sTier.length() > 0)
{
// The "tier" is a synthetic attribute only injected for the top
// level scheme definition. A listener could be defined at this
// level in two cases:
// (a) standard top level cache definition
// (b) local cache using the same scheme for both front and
// back tier declaration
// To cover a very esoteric case when one desires to listen
// to the actual LocalCache instance that serves as a
// backing map for a "local-scheme" we support an undocumented
// "target" attribute with a valid value of "backing-map"
boolean fBackOnly = xmlListener.getSafeAttribute("target").
getString().equals("backing-map");
if (sTier.equals("front") && fBackOnly)
{
// the backing-map listener is requested
return;
}
if (sTier.equals("back") && !fBackOnly)
{
// prevent local cache registering a listener twice:
// once for the front tier and the second time for the back
return;
}
}
MapListener listener = instantiateMapListener(info, xmlClass, context, loader);
try
{
((ObservableMap) map).addMapListener(listener);
if (mapListeners != null)
{
mapListeners.put(map, listener);
}
}
catch (ClassCastException e)
{
throw new IllegalArgumentException("Map is not observable: " + map.getClass());
}
}
}
/**
* Create a ReadWriteBackingMap using the "read-write-backing-map-scheme" element.
*
* @param info the cache info
* @param xmlRWBM "read-write-backing-map-scheme" element
* @param context BackingMapManagerContext to be used
* @param mapListeners map of registered map listeners keyed by the
* corresponding map references
*
* @return a newly instantiated Map
*/
protected Map instantiateReadWriteBackingMap(
CacheInfo info, XmlElement xmlRWBM, BackingMapManagerContext context, Map mapListeners)
{
XmlElement xmlInternal = resolveScheme(xmlRWBM.getSafeElement("internal-cache-scheme"), info, true, true);
XmlElement xmlMisses = resolveScheme(xmlRWBM.getSafeElement("miss-cache-scheme"), info, true, false);
XmlElement xmlStore = resolveScheme(xmlRWBM.getSafeElement("cachestore-scheme"), info, false, false);
ClassLoader loader = context.getClassLoader();
Map mapMisses = xmlMisses == null ? null :
instantiateLocalCache(info, xmlMisses, context, loader);
boolean fReadOnly = xmlRWBM.getSafeElement("read-only").getBoolean();
double dflRefreshAhead = convertDouble(xmlRWBM.getSafeElement("refresh-ahead-factor"));
double dflWriteFactor = convertDouble(xmlRWBM.getSafeElement("write-batch-factor"));
int cWriteRequeue = convertInt(xmlRWBM.getSafeElement("write-requeue-threshold"));
String sSplitting = xmlRWBM.getSafeAttribute("partitioned").getString();
boolean fSplitting = isPartitioned(sSplitting, translateSchemeType(xmlInternal.getName()));
long cStoreTimeout = parseTime(xmlRWBM.getSafeElement("cachestore-timeout").getString("0"));
boolean fRethrow = xmlRWBM.getSafeElement("rollback-cachestore-failures").getBoolean(true);
int cBatchSize = convertInt(xmlRWBM.getSafeElement("write-max-batch-size"), 128);
if (!fRethrow)
{
CacheFactory.log("The rollback-cachestore-failures setting is explicitly " +
"configured to prevent CacheStore exceptions from being " +
"propagated to the client; this setting is not recommended " +
"and has been deprecated", CacheFactory.LOG_WARN);
}
// get the write behind delay; if the "write-delay" element exists, try
// to parse it; otherwise, parse the "write-delay-seconds" element
long cWriteBehindMillis;
String sWriteDelay = xmlRWBM.getSafeElement("write-delay").getString();
if (sWriteDelay == null || sWriteDelay.length() == 0)
{
cWriteBehindMillis = 1000L * xmlRWBM.getSafeElement("write-delay-seconds").getInt();
}
else
{
cWriteBehindMillis = parseTime(sWriteDelay, UNIT_S);
}
int cWriteBehindSec = cWriteBehindMillis == 0 ? 0 :
Math.max(1, (int) (cWriteBehindMillis / 1000));
ObservableMap mapInternal;
try
{
if (fSplitting)
{
xmlInternal = (XmlElement) xmlInternal.clone();
xmlInternal.addAttribute("partitioned").setString(sSplitting);
}
mapInternal = (ObservableMap) configureBackingMap(info, xmlInternal, context, null, mapListeners);
}
catch (ClassCastException e)
{
throw new IllegalArgumentException("Map is not observable:\n" + xmlInternal);
}
Object store = instantiateCacheStore(info, xmlStore, context, loader);
BinaryEntryStore storeBinary = null;
if (store instanceof BinaryEntryStore)
{
// If the store implements the BinaryEntryStore interface, use it.
// The only exception from that rule is the SCHEME_REMOTE_CACHE case,
// (which always returns the SafeNamedCache), that was de-optimized
// due to the Serializers incompatibility
if (!(store instanceof NamedCache
&& store instanceof ClassLoaderAware
&& ((ClassLoaderAware) store).getContextClassLoader() !=
NullImplementation.getClassLoader()))
{
storeBinary = (BinaryEntryStore) store;
}
}
ReadWriteBackingMap rwbm;
String sSubclass = xmlRWBM.getSafeElement("class-name").getString();
if (sSubclass.length() == 0)
{
if (storeBinary == null)
{
CacheLoader storeObject = (CacheLoader) store;
rwbm = fSplitting ?
instantiateReadWriteSplittingBackingMap(context, (PartitionAwareBackingMap) mapInternal,
mapMisses, storeObject, fReadOnly, cWriteBehindSec, dflRefreshAhead) :
instantiateReadWriteBackingMap(context, mapInternal, mapMisses, storeObject, fReadOnly,
cWriteBehindSec, dflRefreshAhead);
}
else
{
rwbm = fSplitting ?
instantiateReadWriteSplittingBackingMap(context, (PartitionAwareBackingMap) mapInternal,
mapMisses, storeBinary, fReadOnly, cWriteBehindSec, dflRefreshAhead) :
instantiateReadWriteBackingMap(context, mapInternal, mapMisses, storeBinary, fReadOnly,
cWriteBehindSec, dflRefreshAhead);
}
}
else
{
Object[] aoParam = storeBinary == null ?
new Object[] {context, mapInternal, mapMisses, store, new Boolean(fReadOnly),
Integer.valueOf(cWriteBehindSec), new Double(dflRefreshAhead)} :
new Object[] {context, mapInternal, mapMisses, storeBinary, new Boolean(fReadOnly),
Integer.valueOf(cWriteBehindSec), new Double(dflRefreshAhead)};
rwbm = (ReadWriteBackingMap) instantiateSubclass(sSubclass, ReadWriteBackingMap.class, loader,
aoParam, xmlRWBM.getElement("init-params"));
}
// Read/Write Threads will have the cache name appended to the thread name
rwbm.setCacheName(info.getCacheName());
rwbm.setRethrowExceptions(fRethrow);
rwbm.setWriteBatchFactor(dflWriteFactor);
rwbm.setWriteRequeueThreshold(cWriteRequeue);
rwbm.setWriteMaxBatchSize(cBatchSize);
if (cWriteBehindMillis != 1000L * cWriteBehindSec)
{
rwbm.setWriteBehindMillis(cWriteBehindMillis);
}
rwbm.setCacheStoreTimeoutMillis(cStoreTimeout);
XmlElement xmlBundling = xmlStore.getElement("operation-bundling");
if (xmlBundling != null)
{
ReadWriteBackingMap.StoreWrapper storeWrapper = rwbm.getCacheStore();
for (Iterator iter = xmlBundling.getElements("bundle-config");
iter.hasNext();)
{
XmlElement xmlBundle = (XmlElement) iter.next();
String sOperation = xmlBundle.getSafeElement("operation-name").getString("all");
int cBundle = convertInt(xmlBundle.getSafeElement("preferred-size"));
if (sOperation.equals("all"))
{
initializeBundler(storeWrapper.ensureLoadBundler(cBundle), xmlBundle);
initializeBundler(storeWrapper.ensureStoreBundler(cBundle), xmlBundle);
initializeBundler(storeWrapper.ensureEraseBundler(cBundle), xmlBundle);
}
else if (sOperation.equals("load"))
{
initializeBundler(storeWrapper.ensureLoadBundler(cBundle), xmlBundle);
}
else if (sOperation.equals("store"))
{
initializeBundler(storeWrapper.ensureStoreBundler(cBundle), xmlBundle);
}
else if (sOperation.equals("erase"))
{
initializeBundler(storeWrapper.ensureEraseBundler(cBundle), xmlBundle);
}
else
{
throw new IllegalArgumentException(
"Invalid \"operation-name\" element:\n" + xmlBundle);
}
}
}
return rwbm;
}
/**
* Construct a ReadWriteBackingMap using the specified parameters.
*
* This method exposes a corresponding ReadWriteBackingMap
* {@link ReadWriteBackingMap#ReadWriteBackingMap(BackingMapManagerContext, ObservableMap, Map, CacheLoader, boolean, int, double) constructor}
* and is provided for the express purpose of allowing its override.
*/
protected ReadWriteBackingMap instantiateReadWriteBackingMap(BackingMapManagerContext context,
ObservableMap mapInternal, Map mapMisses, CacheLoader store, boolean fReadOnly,
int cWriteBehindSeconds, double dflRefreshAheadFactor)
{
return new ReadWriteBackingMap(context, mapInternal, mapMisses, store, fReadOnly, cWriteBehindSeconds, dflRefreshAheadFactor);
}
/**
* Construct a ReadWriteBackingMap using the specified parameters.
*
* This method exposes a corresponding ReadWriteBackingMap
* {@link ReadWriteBackingMap#ReadWriteBackingMap(BackingMapManagerContext, ObservableMap, Map, BinaryEntryStore, boolean, int, double) constructor}
* and is provided for the express purpose of allowing its override.
*/
protected ReadWriteBackingMap instantiateReadWriteBackingMap(BackingMapManagerContext context,
ObservableMap mapInternal, Map mapMisses, BinaryEntryStore storeBinary, boolean fReadOnly,
int cWriteBehindSeconds, double dflRefreshAheadFactor)
{
return new ReadWriteBackingMap(context, mapInternal, mapMisses, storeBinary, fReadOnly, cWriteBehindSeconds, dflRefreshAheadFactor);
}
/**
* Construct a ReadWriteSplittingBackingMap using the specified parameters.
*
* This method exposes a corresponding ReadWriteSplittingBackingMap
* {@link ReadWriteSplittingBackingMap#ReadWriteSplittingBackingMap(BackingMapManagerContext, PartitionAwareBackingMap, Map, CacheLoader, boolean, int, double) constructor}
* and is provided for the express purpose of allowing its override.
*/
protected ReadWriteSplittingBackingMap instantiateReadWriteSplittingBackingMap(BackingMapManagerContext context,
PartitionAwareBackingMap mapInternal, Map mapMisses, CacheLoader store, boolean fReadOnly,
int cWriteBehindSeconds, double dflRefreshAheadFactor)
{
return new ReadWriteSplittingBackingMap(context, mapInternal, mapMisses, store, fReadOnly, cWriteBehindSeconds, dflRefreshAheadFactor);
}
/**
* Construct a ReadWriteSplittingBackingMap using the specified parameters.
*
* This method exposes a corresponding ReadWriteSplittingBackingMap
* {@link ReadWriteSplittingBackingMap#ReadWriteSplittingBackingMap(BackingMapManagerContext, PartitionAwareBackingMap, Map, BinaryEntryStore, boolean, int, double) constructor}
* and is provided for the express purpose of allowing its override.
*/
protected ReadWriteSplittingBackingMap instantiateReadWriteSplittingBackingMap(BackingMapManagerContext context,
PartitionAwareBackingMap mapInternal, Map mapMisses, BinaryEntryStore storeBinary, boolean fReadOnly,
int cWriteBehindSeconds, double dflRefreshAheadFactor)
{
return new ReadWriteSplittingBackingMap(context, mapInternal, mapMisses, storeBinary, fReadOnly, cWriteBehindSeconds, dflRefreshAheadFactor);
}
/**
* Create a VersionedBackingMap using the "versioned-backing-map-scheme" element.
*
* @param info the cache info
* @param xmlVBM "versioned-backing-map-scheme" element
* @param context BackingMapManagerContext to be used
* @param mapListeners map of registered map listeners keyed by the
* corresponding map references
*
* @return a newly instantiated Map
*/
protected Map instantiateVersionedBackingMap(
CacheInfo info, XmlElement xmlVBM, BackingMapManagerContext context, Map mapListeners)
{
XmlElement xmlPersist = xmlVBM.getElement("version-persistent-scheme");
String sPersistSuffix = "-persist";
if (xmlPersist != null)
{
sPersistSuffix = xmlPersist.getSafeElement("cache-name-suffix").getString(sPersistSuffix);
xmlPersist = resolveScheme(xmlPersist, info, true, false);
if (info.getCacheName().endsWith(sPersistSuffix) && xmlPersist != null)
{
return configureBackingMap(info, xmlPersist, context, null, mapListeners);
}
}
XmlElement xmlTrans = xmlVBM.getElement("version-transient-scheme");
String sTransSuffix = "-version";
if (xmlTrans != null)
{
sTransSuffix = xmlTrans.getSafeElement("cache-name-suffix").getString(sTransSuffix);
xmlTrans = resolveScheme(xmlTrans, info, true, false);
if (info.getCacheName().endsWith(sTransSuffix) && xmlTrans != null)
{
return configureBackingMap(info, xmlTrans, context, null, mapListeners);
}
}
XmlElement xmlInternal = resolveScheme(xmlVBM.getSafeElement("internal-cache-scheme"), info, true, true);
XmlElement xmlMisses = resolveScheme(xmlVBM.getSafeElement("miss-cache-scheme"), info, true, false);
XmlElement xmlStore = resolveScheme(xmlVBM.getSafeElement("cachestore-scheme"), info, false, false);
ClassLoader loader = context.getClassLoader();
Map mapMisses = xmlMisses == null ? null :
instantiateLocalCache(info, xmlMisses, context, loader);
boolean fReadOnly = xmlVBM.getSafeElement("read-only").getBoolean();
double dflRefreshAhead = convertDouble(xmlVBM.getSafeElement("refresh-ahead-factor"));
boolean fRethrow = xmlVBM.getSafeElement("rollback-cachestore-failures").getBoolean(true);
double dflWriteFactor = convertDouble(xmlVBM.getSafeElement("write-batch-factor"));
int cWriteRequeue = convertInt(xmlVBM.getSafeElement("write-requeue-threshold"));
NamedCache cachePersist = xmlPersist == null ? null :
ensureCache(info.getSyntheticInfo(sPersistSuffix), xmlPersist, loader);
NamedCache cacheTrans = xmlTrans == null ? null :
ensureCache(info.getSyntheticInfo(sTransSuffix), xmlTrans, loader);
boolean fManageTrans = xmlVBM.getSafeElement("manage-transient").getBoolean();
if (!fRethrow)
{
CacheFactory.log("The rollback-cachestore-failures setting is explicitly " +
"configured to prevent CacheStore exceptions from being " +
"propagated to the client; this setting is not recommended " +
"and has been deprecated", CacheFactory.LOG_WARN);
}
// get the write behind delay; if the "write-delay" element exists, try
// to parse it; otherwise, parse the "write-delay-seconds" element
long cWriteBehindMillis;
String sWriteDelay = xmlVBM.getSafeElement("write-delay").getString();
if (sWriteDelay == null || sWriteDelay.length() == 0)
{
cWriteBehindMillis = 1000L * xmlVBM.getSafeElement("write-delay-seconds").getInt();
}
else
{
cWriteBehindMillis = parseTime(sWriteDelay, UNIT_S);
}
int cWriteBehindSec = cWriteBehindMillis == 0 ? 0 :
Math.max(1, (int) (cWriteBehindMillis / 1000));
ObservableMap mapInternal;
try
{
mapInternal = (ObservableMap) configureBackingMap(info, xmlInternal, context, null, mapListeners);
}
catch (ClassCastException e)
{
throw new IllegalArgumentException("Map is not observable:\n" + xmlInternal);
}
VersionedBackingMap vbm;
CacheLoader store = (CacheLoader) instantiateCacheStore(info, xmlStore, context, loader);
String sSubclass = xmlVBM.getSafeElement("class-name").getString();
if (sSubclass.length() == 0)
{
vbm = store instanceof CacheStore ?
instantiateVersionedBackingMap(context, mapInternal, mapMisses, (CacheStore) store, fReadOnly, cWriteBehindSec,
dflRefreshAhead, cacheTrans, cachePersist, fManageTrans) :
instantiateVersionedBackingMap(context, mapInternal, mapMisses, store,
cacheTrans, cachePersist, fManageTrans);
}
else
{
Object[] aoParam = store instanceof CacheStore ?
new Object[] {context, mapInternal, mapMisses, store, new Boolean(fReadOnly), Integer.valueOf(cWriteBehindSec),
new Double(dflRefreshAhead), cacheTrans, cachePersist, new Boolean(fManageTrans)} :
new Object[] {context, mapInternal, mapMisses, store,
cacheTrans, cachePersist, new Boolean(fManageTrans)};
vbm = (VersionedBackingMap) instantiateSubclass(sSubclass, VersionedBackingMap.class, loader,
aoParam, xmlVBM.getElement("init-params"));
}
vbm.setRethrowExceptions(fRethrow);
vbm.setWriteBatchFactor(dflWriteFactor);
vbm.setWriteRequeueThreshold(cWriteRequeue);
if (cWriteBehindMillis != 1000L * cWriteBehindSec)
{
vbm.setWriteBehindMillis(cWriteBehindMillis);
}
return vbm;
}
/**
* Construct a VersionedBackingMap using the specified parameters.
*
* This method exposes a corresponding VersionedBackingMap
* {@link VersionedBackingMap#VersionedBackingMap(BackingMapManagerContext, ObservableMap, Map,
* CacheStore, boolean, int, double, NamedCache, NamedCache, boolean) constructor}
* and is provided for the express purpose of allowing its override.
*/
protected VersionedBackingMap instantiateVersionedBackingMap(BackingMapManagerContext context,
ObservableMap mapInternal, Map mapMisses, CacheStore store, boolean fReadOnly,
int cWriteBehindSeconds, double dflRefreshAheadFactor, NamedCache mapVersionTransient,
NamedCache mapVersionPersist, boolean fManageTransient)
{
return new VersionedBackingMap(context, mapInternal, mapMisses, store, fReadOnly, cWriteBehindSeconds,
dflRefreshAheadFactor, mapVersionTransient, mapVersionPersist, fManageTransient);
}
/**
* Construct a VersionedBackingMap using the specified parameters.
*
* This method exposes a corresponding VersionedBackingMap
* {@link VersionedBackingMap#VersionedBackingMap(BackingMapManagerContext, ObservableMap, Map,
* CacheLoader, NamedCache, NamedCache, boolean) constructor}
* and is provided for the express purpose of allowing its override.
*/
protected VersionedBackingMap instantiateVersionedBackingMap(BackingMapManagerContext context,
ObservableMap mapInternal, Map mapMisses, CacheLoader loader,
NamedCache mapVersionTransient, NamedCache mapVersionPersist, boolean fManageTransient)
{
return new VersionedBackingMap(context, mapInternal, mapMisses, loader,
mapVersionTransient, mapVersionPersist, fManageTransient);
}
/**
* Create a backing Map using the "local-scheme" element.
*
* @param info the cache info
* @param xmlLocal "local-scheme" element
* @param context BackingMapManagerContext to be used
* @param loader the ClassLoader to instantiate necessary classes
*
* @return a newly instantiated Map
*/
protected Map instantiateLocalCache(CacheInfo info, XmlElement xmlLocal, BackingMapManagerContext context, ClassLoader loader)
{
int cHighUnits = (int) parseMemorySize(xmlLocal.getSafeElement("high-units").getString("0"));
int cLowUnits = (int) parseMemorySize(xmlLocal.getSafeElement("low-units" ).getString("0"));
int cExpiryDelayMillis = (int) parseTime(xmlLocal.getSafeElement("expiry-delay").getString("0"), UNIT_S);
// check and default all of the Cache options
if (cHighUnits <= 0)
{
cHighUnits = Integer.MAX_VALUE;
}
if (cLowUnits <= 0)
{
cLowUnits = (int) (cHighUnits * LocalCache.DEFAULT_PRUNE);
}
if (cExpiryDelayMillis < 0)
{
cExpiryDelayMillis = 0;
}
// configure and return the LocalCache
LocalCache cache;
String sSubclass = xmlLocal.getSafeElement("class-name").getString();
if (sSubclass.length() == 0)
{
cache = instantiateLocalCache(cHighUnits, cExpiryDelayMillis);
}
else
{
Object[] aoParam = new Object[]
{Integer.valueOf(cHighUnits), Integer.valueOf(cExpiryDelayMillis)};
cache = (LocalCache) instantiateSubclass(sSubclass, LocalCache.class, loader,
aoParam, xmlLocal.getElement("init-params"));
}
cache.setLowUnits(cLowUnits);
XmlElement xmlEviction = xmlLocal.getElement("eviction-policy");
if (xmlEviction != null)
{
String sEvictionType = xmlEviction.getString();
int nEvictionType = sEvictionType.equalsIgnoreCase("HYBRID") ? LocalCache.EVICTION_POLICY_HYBRID
: sEvictionType.equalsIgnoreCase("LRU") ? LocalCache.EVICTION_POLICY_LRU
: sEvictionType.equalsIgnoreCase("LFU") ? LocalCache.EVICTION_POLICY_LFU
: Integer.MIN_VALUE;
if (nEvictionType >= 0)
{
cache.setEvictionType(nEvictionType);
}
else
{
XmlElement xmlClass = xmlEviction.getElement("class-scheme");
if (xmlClass == null)
{
throw new IllegalArgumentException("Unknown eviction policy:\n"
+ xmlEviction);
}
try
{
cache.setEvictionPolicy((ConfigurableCacheMap.EvictionPolicy)
instantiateAny(info, xmlClass, context, loader));
}
catch (Exception e)
{
throw Base.ensureRuntimeException(e,
"Error instantiating custom eviction policy:\n"
+ xmlEviction);
}
}
}
configureUnitCalculator(xmlLocal, cache, info, context, loader);
XmlElement xmlStore = resolveScheme(xmlLocal.getSafeElement("cachestore-scheme"), info, false, false);
CacheLoader store = (CacheLoader) instantiateCacheStore(info, xmlStore, context, loader);
if (store != null)
{
cache.setCacheLoader(store);
}
if (xmlLocal.getSafeElement("pre-load").getBoolean())
{
try
{
cache.loadAll();
}
catch (Throwable e)
{
String sText = "An exception occurred while pre-loading the \"" + info.getCacheName() + "\" cache:"
+ '\n' + indentString(getStackTrace(e), " ")
+ "\nThe following configuration was used for the \"" + info.getCacheName() + "\" cache:"
+ '\n' + indentString(xmlLocal.toString(), " ");
if (!(e instanceof Error))
{
sText += "\n(The exception has been logged and will be ignored.)";
}
CacheFactory.log(sText, CacheFactory.LOG_WARN);
if (e instanceof Error)
{
throw (Error) e;
}
}
}
return cache;
}
/**
* Configure a UnitCalculator for the specified ConfigurableCacheMap.
*
* @param xmlCache cache scheme that may contain a "unit-calculator" element
* @param cache the corresponding ConfigurableCacheMap
* @param info the cache info
* @param context BackingMapManagerContext to be used
* @param loader the ClassLoader to instantiate necessary classes
*/
protected void configureUnitCalculator(XmlElement xmlCache, ConfigurableCacheMap cache,
CacheInfo info, BackingMapManagerContext context, ClassLoader loader)
{
XmlElement xmlCalculator = xmlCache.getElement("unit-calculator");
if (xmlCalculator == null)
{
return;
}
ConfigurableCacheMap.UnitCalculator calculator;
String sType = xmlCalculator.getString();
if (sType.equalsIgnoreCase("FIXED"))
{
calculator = LocalCache.INSTANCE_FIXED;
}
else if (sType.equalsIgnoreCase("BINARY"))
{
calculator = LocalCache.INSTANCE_BINARY;
}
else
{
XmlElement xmlClass = xmlCalculator.getElement("class-scheme");
if (xmlClass == null)
{
throw new IllegalArgumentException("Unknown unit calculator:\n"
+ xmlCalculator);
}
try
{
calculator = (ConfigurableCacheMap.UnitCalculator)
instantiateAny(info, xmlClass, context, loader);
}
catch (Exception e)
{
throw Base.ensureRuntimeException(e,
"Error instantiating custom unit calculator:\n"
+ xmlCalculator);
}
}
if (calculator != null)
{
cache.setUnitCalculator(calculator);
XmlElement xmlFactor = xmlCache.getElement("unit-factor");
if (xmlFactor != null)
{
cache.setUnitFactor((int) parseMemorySize(xmlFactor.getString("1")));
}
}
}
/**
* Construct a LocalCache using the specified parameters.
*
* This method exposes a corresponding LocalCache
* {@link LocalCache#LocalCache(int, int) constructor}
* and is provided for the express purpose of allowing its override.
*
* @param cUnits high watermark
* @param cExpiryMillis the expiry value
*
* @return a newly instantiated LocalCache
*/
protected LocalCache instantiateLocalCache(int cUnits, int cExpiryMillis)
{
return new LocalCache(cUnits, cExpiryMillis);
}
/**
* Create a backing Map using the "overflow-scheme" element.
*
* @param info the cache info
* @param xmlOverflow "overflow-scheme" element
* @param context BackingMapManagerContext to be used
* @param loader the ClassLoader to instantiate necessary classes
* @param mapListeners map of registered map listeners keyed by the
* corresponding map references
*
* @return a newly instantiated Map
*/
protected Map instantiateOverflowBackingMap(CacheInfo info, XmlElement xmlOverflow,
BackingMapManagerContext context, ClassLoader loader, Map mapListeners)
{
XmlElement xmlFront = resolveScheme(xmlOverflow.getSafeElement("front-scheme"), info, true, true);
XmlElement xmlBack = resolveScheme(xmlOverflow.getSafeElement("back-scheme"), info, true, true);
XmlElement xmlMisses = resolveScheme(xmlOverflow.getSafeElement("miss-cache-scheme"), info, true, false);
Map mapFront = configureBackingMap(info, xmlFront, context, loader, mapListeners);
Map mapBack = configureBackingMap(info, xmlBack, context, loader, mapListeners);
Map mapMisses = xmlMisses == null ? null
: instantiateLocalCache(info, xmlMisses, context, loader);
String sSubclass = xmlOverflow.getSafeElement("class-name").getString();
boolean fExplicit = sSubclass != null && sSubclass.length() > 0;
int cExpiryMillis = (int) parseTime(xmlOverflow.getSafeElement("expiry-delay").getString("0"), UNIT_S);
boolean fExpiry = cExpiryMillis > 0
|| xmlOverflow.getSafeElement("expiry-enabled").getBoolean();
boolean fObservable = mapBack instanceof ObservableMap;
boolean fExplicitOverflow = fExplicit && sSubclass.equals(OverflowMap.class.getName());
boolean fExplicitSimple = fExplicit && sSubclass.equals(SimpleOverflowMap.class.getName());
Map mapOverflow;
try
{
if (fExplicit && !fExplicitSimple && !fExplicitOverflow)
{
// figure out which type of overflow they are instantiating
try
{
Class clz = ExternalizableHelper.loadClass(sSubclass, loader,
OverflowMap.class.getClassLoader());
if (OverflowMap.class.isAssignableFrom(clz))
{
fExplicitOverflow = true;
}
else if (SimpleOverflowMap.class.isAssignableFrom(clz))
{
fExplicitSimple = true;
}
else
{
throw new IllegalArgumentException(sSubclass
+ " is not a sub-class of either OverflowMap or SimpleOverflowMap");
}
}
catch (Exception e)
{
throw ensureRuntimeException(e);
}
// prepare constructor parameters
Object[] aoParam = fExplicitSimple && mapMisses != null
? new Object[] {(ObservableMap) mapFront, mapBack, mapMisses}
: new Object[] {(ObservableMap) mapFront, mapBack};
XmlElement xmlParams = xmlOverflow.getElement("init-params");
// instantiate the overflow
mapOverflow = (Map) instantiateSubclass(sSubclass,
OverflowMap.class, loader, aoParam, xmlParams);
}
else if (fExplicitSimple)
{
mapOverflow = instantiateSimpleOverflowMap((ObservableMap) mapFront, mapBack, mapMisses);
}
else
{
mapOverflow = instantiateOverflowMap((ObservableMap) mapFront, mapBack, fExpiry);
}
}
catch (ClassCastException e)
{
throw new IllegalArgumentException("FrontMap is not observable: " + mapFront.getClass());
}
// post-instantiation configuration of options
// and explanation of ignored options etc.
if (mapOverflow instanceof OverflowMap)
{
if (cExpiryMillis > 0)
{
((OverflowMap) mapOverflow).setExpiryDelay(cExpiryMillis);
}
if (mapMisses != null)
{
CacheFactory.log("Cache " + info.getCacheName()
+ " of scheme " + info.getSchemeName()
+ " has a \"miss-cache-scheme\" configured; since"
+ " the default OverflowMap implementation has been"
+ " selected, the miss cache will not be used.",
CacheFactory.LOG_WARN);
}
}
else if (mapOverflow instanceof SimpleOverflowMap)
{
if (fExpiry)
{
CacheFactory.log("Cache " + info.getCacheName()
+ " of scheme " + info.getSchemeName()
+ " has \"expiry-enabled\" set to true or"
+ " \"expiry-delay\" configured; these settings will"
+ " have no effect, and expiry will not work,"
+ " because the scheme explicitly ues a"
+ " SimpleOverflowMap.",
CacheFactory.LOG_WARN);
}
if (fObservable)
{
CacheFactory.log("Cache " + info.getCacheName()
+ " of scheme " + info.getSchemeName()
+ " has a \"back-scheme\" that is observable;"
+ " the events from the back map will be ignored"
+ " because the scheme explicitly uses a"
+ " SimpleOverflowMap, and this could result in"
+ " missing events if the back map actively expires"
+ " and/or evicts its entries.",
CacheFactory.LOG_WARN);
}
}
return mapOverflow;
}
/**
* Construct an OverflowMap using the specified parameters.
*
* This method exposes a corresponding OverflowMap
* {@link OverflowMap#OverflowMap(ObservableMap, Map) constructor}
* and is provided for the express purpose of allowing its override.
*/
protected OverflowMap instantiateOverflowMap(ObservableMap mapFront, Map mapBack, boolean fExpiry)
{
OverflowMap map = new OverflowMap(mapFront, mapBack);
if (fExpiry)
{
map.setExpiryEnabled(true);
}
return map;
}
/**
* Construct a SimpleOverflowMap using the specified parameters.
*
* This method exposes a corresponding SimpleOverflowMap
* {@link SimpleOverflowMap#SimpleOverflowMap(ObservableMap, Map, Map) constructor}
* and is provided for the express purpose of allowing its override.
*/
protected SimpleOverflowMap instantiateSimpleOverflowMap(ObservableMap mapFront, Map mapBack, Map mapMisses)
{
return new SimpleOverflowMap(mapFront, mapBack, mapMisses);
}
/**
* Create a backing Map using the "disk-scheme" element.
*
* @param info the cache info
* @param xmlDisk "disk-scheme" element
* @param context BackingMapManagerContext to be used
* @param loader the ClassLoader to instantiate necessary classes
*
* @return a newly instantiated Map
*
* @deprecated As of Coherence 3.0, replaced by
* {@link #instantiateExternalBackingMap} and
* {@link #instantiatePagedExternalBackingMap}
*/
protected Map instantiateDiskBackingMap(CacheInfo info, XmlElement xmlDisk,
BackingMapManagerContext context, ClassLoader loader)
{
String sSubclass = xmlDisk.getSafeElement("class-name").getString();
String sFS = xmlDisk.getSafeElement("file-manager").getString();
String sPath = xmlDisk.getSafeElement("directory").getString();
int cHighUnits = convertInt(xmlDisk.getSafeElement("high-units"));
int cPages = convertInt(xmlDisk.getSafeElement("page-limit"));
String sTarget = xmlDisk.getSafeAttribute("target").getString(); // see $Storage.instantiateBackupMap()
boolean fAsync = xmlDisk.getSafeElement("async").getBoolean();
int cbMaxAsync = (int) (parseMemorySize(xmlDisk.getSafeElement("async-limit").getString("0")));
int cPageSecs = (int) (parseTime(xmlDisk.getSafeElement("page-duration").getString("0"), UNIT_S) / 1000L);
int cExpiryMillis = (int) parseTime(xmlDisk.getSafeElement("expiry-delay").getString("0"), UNIT_S);
if (sPath.length() == 0)
{
// deprecated but supported
sPath = xmlDisk.getSafeElement("root-directory").getString();
}
File file = sPath.length() == 0 ? null : new File(sPath);
boolean fPaged = cPages > 0 && cPageSecs > 0;
boolean fBackup = sTarget.equals("backup");
boolean fBinaryMap = context != null && CacheService.TYPE_DISTRIBUTED.
equals(context.getCacheService().getInfo().getServiceType());
BinaryStore store = null;
BinaryStoreManager storeMgr = null;
if (sFS.equalsIgnoreCase("NIO-file"))
{
long cbInit = parseMemorySize(xmlDisk.getSafeElement("initial-size").getString("1"), POWER_M);
long cbMax = parseMemorySize(xmlDisk.getSafeElement("maximum-size").getString("1024"), POWER_M);
// Bounds check:
// 1 <= cbInitSize <= cbMaxSize <= Integer.MAX_VALUE - 1023
// (Integer.MAX_VALUE - 1023 is the largest integer multiple of 1024)
int cbMaxSize = (int) Math.min(Math.max(cbMax, 1L), (long) Integer.MAX_VALUE - 1023);
int cbInitSize = (int) Math.min(Math.max(cbInit, 1L), cbMaxSize);
if (fPaged)
{
storeMgr = new MappedStoreManager(cbInitSize, cbMaxSize, file);
}
else
{
ByteBufferManager bufferMgr = new MappedBufferManager(cbInitSize, cbMaxSize, file);
store = new BinaryMapStore(new BinaryMap(bufferMgr));
}
}
else
{
throw new UnsupportedOperationException("file-manager: " + sFS);
}
if (fAsync)
{
if (store != null)
{
store = instantiateAsyncBinaryStore(store, cbMaxAsync);
}
else if (storeMgr != null)
{
storeMgr = instantiateAsyncBinaryStoreManager(storeMgr, cbMaxAsync);
}
else
{
throw new UnsupportedOperationException("async option without BinaryStore or BinaryStoreManager!");
}
}
if (fPaged)
{
if (sSubclass.length() == 0)
{
return fBinaryMap ?
instantiateSerializationPagedCache(storeMgr, cPages, cPageSecs, true, fBackup) :
instantiateSerializationPagedCache(storeMgr, cPages, cPageSecs, loader);
}
else
{
Object[] aoParam = fBinaryMap ?
new Object[] {storeMgr, Integer.valueOf(cPages), Integer.valueOf(cPageSecs), Boolean.TRUE, new Boolean(fBackup)} :
new Object[] {storeMgr, Integer.valueOf(cPages), Integer.valueOf(cPageSecs), loader};
return (Map) instantiateSubclass(sSubclass, SerializationPagedCache.class, loader,
aoParam, xmlDisk.getElement("init-params"));
}
}
else
{
return instantiateSerializationMap(store, fBinaryMap, loader,
cHighUnits, cExpiryMillis, sSubclass, xmlDisk.getElement("init-params"));
}
}
/**
* Instantiate a SerializationMap, SerializationCache,
* SimpleSerializationMap, or any sub-class thereof.
*
* @param store a BinaryStore to use to write serialized data to
* @param fBinaryMap true if the only data written to the Map will
* already be in Binary form
* @param loader the ClassLoader to use (if not a Binary map)
* @param cHighUnits the max size in units for the serialization cache
* @param cExpiryMillis the expiry time in milliseconds for the cache
* @param sSubclass the sub-class name (or "")
* @param xmlInitParams the init params for the sub-class
*
* @return a BinaryMap, SerializationMap, SerializationCache,
* SimpleSerializationMap, or a subclass thereof
*/
protected Map instantiateSerializationMap(BinaryStore store,
boolean fBinaryMap, ClassLoader loader,
int cHighUnits, int cExpiryMillis,
String sSubclass, XmlElement xmlInitParams)
{
if (sSubclass.length() == 0)
{
if (cHighUnits > 0 || cExpiryMillis > 0)
{
SerializationCache cache = fBinaryMap
? instantiateSerializationCache(store, cHighUnits, true)
: instantiateSerializationCache(store, cHighUnits, loader);
if (cExpiryMillis > 0)
{
cache.setExpiryDelay(cExpiryMillis);
}
return cache;
}
else if (fBinaryMap && store.getClass() == BinaryMapStore.class)
{
// optimization: instead of taking binary objects, writing
// them through a serialization map that knows that they are
// binary into a BinaryStore that wraps a BinaryMap, we just
// use the BinaryMap directly
return ((BinaryMapStore) store).getBinaryMap();
}
else
{
return fBinaryMap
? instantiateSerializationMap(store, true)
: instantiateSerializationMap(store, loader);
}
}
else
{
if (cHighUnits > 0 || cExpiryMillis > 0)
{
Object[] aoParam = fBinaryMap
? new Object[] {store, Integer.valueOf(cHighUnits), Boolean.TRUE}
: new Object[] {store, Integer.valueOf(cHighUnits), loader};
SerializationCache cache = (SerializationCache) instantiateSubclass(sSubclass,
SerializationCache.class, loader, aoParam, xmlInitParams);
if (cExpiryMillis > 0)
{
cache.setExpiryDelay(cExpiryMillis);
}
return cache;
}
else
{
Object[] aoParam = fBinaryMap
? new Object[] {store, Boolean.TRUE}
: new Object[] {store, loader};
// the custom class may subclass one of the following:
//
// (1) SerializationMap
// (2) SimpleSerializationMap
//
// the common ancestor of these classes is AbstractKeyBasedMap
Map map = (Map) instantiateSubclass(sSubclass,
AbstractKeyBasedMap.class, loader, aoParam,
xmlInitParams);
if (map instanceof SerializationMap ||
map instanceof SimpleSerializationMap)
{
return map;
}
throw new IllegalArgumentException(sSubclass
+ " does not extend either "
+ SerializationMap.class.getName()
+ " or "
+ SimpleSerializationMap.class.getName());
}
}
}
/**
* Create a backing Map using the "external-scheme" element.
*
* @param info the cache info
* @param xmlExternal "external-scheme" element
* @param context BackingMapManagerContext to be used
* @param loader the ClassLoader to instantiate necessary classes
*
* @return a newly instantiated Map
*/
protected Map instantiateExternalBackingMap(CacheInfo info,
XmlElement xmlExternal, BackingMapManagerContext context,
ClassLoader loader)
{
String sSubclass = xmlExternal.getSafeElement("class-name").getString();
int cHighUnits = (int) parseMemorySize(xmlExternal.getSafeElement("high-units" ).getString("0"));
int cExpiryMillis = (int) parseTime(xmlExternal.getSafeElement("expiry-delay").getString("0"), UNIT_S);
boolean fBinaryMap = context != null && CacheService.TYPE_DISTRIBUTED.equals(context.getCacheService().getInfo().getServiceType());
BinaryStore store = instantiateBinaryStoreManager(xmlExternal, loader, false).createBinaryStore();
Map map = instantiateSerializationMap(store, fBinaryMap, loader,
cHighUnits, cExpiryMillis, sSubclass, xmlExternal.getElement("init-params"));
if (map instanceof ConfigurableCacheMap)
{
configureUnitCalculator(
xmlExternal, (ConfigurableCacheMap) map, info, context, loader);
}
return map;
}
/**
* Create a backing Map using the "paged-external-scheme" element.
*
* @param info the cache info
* @param xmlPaged "paged-external-scheme" element
* @param context BackingMapManagerContext to be used
* @param loader the ClassLoader to instantiate necessary classes
*
* @return a newly instantiated Map
*/
protected Map instantiatePagedExternalBackingMap(CacheInfo info,
XmlElement xmlPaged, BackingMapManagerContext context,
ClassLoader loader)
{
String sSubclass = xmlPaged.getSafeElement("class-name").getString();
int cPages = convertInt(xmlPaged.getSafeElement("page-limit"));
int cPageSecs = (int) (parseTime(xmlPaged.getSafeElement("page-duration").getString("5"), UNIT_S) / 1000L);
boolean fBackup = xmlPaged.getSafeAttribute("target").getString().equals("backup");
boolean fBinaryMap = context != null && CacheService.TYPE_DISTRIBUTED.equals(context.getCacheService().getInfo().getServiceType());
BinaryStoreManager mgr = instantiateBinaryStoreManager(xmlPaged, loader, true);
if (sSubclass.length() == 0)
{
return fBinaryMap ?
instantiateSerializationPagedCache(mgr, cPages, cPageSecs, true, fBackup) :
instantiateSerializationPagedCache(mgr, cPages, cPageSecs, loader);
}
else
{
Object[] aoParam = fBinaryMap ?
new Object[] {mgr, Integer.valueOf(cPages), Integer.valueOf(cPageSecs), Boolean.TRUE, new Boolean(fBackup)} :
new Object[] {mgr, Integer.valueOf(cPages), Integer.valueOf(cPageSecs), loader};
return (Map) instantiateSubclass(sSubclass, SerializationPagedCache.class, loader,
aoParam, xmlPaged.getElement("init-params"));
}
}
/**
* Create a backing Map using the "flashjournal-scheme" element.
*
* @param info the cache info
* @param xmlJournal "flashjournal-scheme" element
* @param context BackingMapManagerContext to be used
* @param loader the ClassLoader to instantiate necessary classes
*
* @return a newly instantiated Map
*/
protected Map instantiateFlashJournalBackingMap(CacheInfo info,
XmlElement xmlJournal, BackingMapManagerContext context,
ClassLoader loader)
{
throw new UnsupportedOperationException("Elastic Data features are not supported in Coherence CE");
}
/**
* Create a backing Map using the "ramjournal-scheme" element.
*
* @param info the cache info
* @param xmlJournal "ramjournal-scheme" element
* @param context BackingMapManagerContext to be used
* @param loader the ClassLoader to instantiate necessary classes
*
* @return a newly instantiated Map
*/
protected Map instantiateRamJournalBackingMap(CacheInfo info,
XmlElement xmlJournal, BackingMapManagerContext context,
ClassLoader loader)
{
throw new UnsupportedOperationException("Elastic Data features are not supported in Coherence CE");
}
/**
* Create a BinaryStoreManager using the specified XML configuration. The
* given XML configuration must contain a valid child BinaryStoreManager
* element:
*
* - async-store-manager
* - custom-store-manager
* - bdb-store-manager
* - nio-file-manager
*
*
* @param xmlConfig the XmlElement that contains the configuration info for
* the BinaryStoreManager that will be instantiated
* @param loader the ClassLoader to instantiate necessary classes
* @param fPaged flag indicating whether or not the returned
* BinaryStoreManager will be used by a
* SerializationPagedCache
*
* @return a newly instantiated BinaryStoreManager created using the given
* XML configuration
*/
protected BinaryStoreManager instantiateBinaryStoreManager(XmlElement xmlConfig,
ClassLoader loader, boolean fPaged)
{
for (Iterator iter = xmlConfig.getElementList().iterator(); iter.hasNext();)
{
XmlElement xmlStore = (XmlElement) iter.next();
String sType = xmlStore.getName();
// parse common configuration elements
String sSubclass = xmlStore.getSafeElement("class-name").getString();
XmlElement xmlParams = xmlStore.getElement("init-params");
File fileDir = null;
int cbMaxSize = 0;
int cbInitSize = 0;
if (sType.equals("nio-file-manager") ||
sType.equals("bdb-store-manager"))
{
String sPath = xmlStore.getSafeElement("directory").getString();
fileDir = sPath.length() == 0 ? null : new File(sPath);
}
if (sType.equals("nio-file-manager"))
{
long cbInit = parseMemorySize(xmlStore.getSafeElement("initial-size").getString("1"), POWER_M);
long cbMax = parseMemorySize(xmlStore.getSafeElement("maximum-size").getString("1024"), POWER_M);
// bounds check:
// 1 <= cbInitSize <= cbMaxSize <= Integer.MAX_VALUE - 1023
// (Integer.MAX_VALUE - 1023 is the largest integer multiple of 1024)
cbMaxSize = (int) Math.min(Math.max(cbMax, 1L), (long) Integer.MAX_VALUE - 1023);
cbInitSize = (int) Math.min(Math.max(cbInit, 1L), cbMaxSize);
// warn about changes to configured values
if (cbInitSize != cbInit)
{
CacheFactory.log("Invalid initial-size specified for " +
sType + "; changed to: " + cbInitSize + " bytes",
CacheFactory.LOG_WARN);
}
if (cbMaxSize != cbMax)
{
CacheFactory.log("Invalid maximum-size specified for " +
sType + "; changed to: " + cbMaxSize + " bytes",
CacheFactory.LOG_WARN);
}
}
// bdb-store
if (sType.equals("bdb-store-manager"))
{
String sStoreName = xmlStore.getSafeElement("store-name").getString();
try
{
if (sSubclass.length() == 0)
{
BerkeleyDBBinaryStoreManager bdbMgr =
new BerkeleyDBBinaryStoreManager(fileDir, sStoreName);
if (xmlParams != null)
{
XmlElement xmlInit = new SimpleElement("config");
XmlHelper.transformInitParams(xmlInit, xmlParams);
bdbMgr.setConfig(xmlInit);
}
return bdbMgr;
}
else
{
Object[] aoParam = new Object[] {fileDir, sStoreName};
return (BinaryStoreManager) instantiateSubclass(sSubclass,
BerkeleyDBBinaryStoreManager.class, loader, aoParam,
xmlParams);
}
}
catch (NoClassDefFoundError e)
{
String sMsg = "Berkeley DB JE libraries are required to utilize a 'bdb-store-manager'," +
" visit www.sleepycat.com for additional information.";
throw ensureRuntimeException(e, sMsg);
}
}
// nio-file
if (sType.equals("nio-file-manager"))
{
if (sSubclass.length() == 0)
{
return new MappedStoreManager(cbInitSize, cbMaxSize, fileDir);
}
else
{
Object[] aoParam = new Object[] {Integer.valueOf(cbInitSize),
Integer.valueOf(cbMaxSize), fileDir};
return (BinaryStoreManager) instantiateSubclass(sSubclass,
MappedStoreManager.class, loader, aoParam, xmlParams);
}
}
// async-store
if (sType.equals("async-store-manager"))
{
int cbMaxAsync = (int) (parseMemorySize(xmlStore.getSafeElement("async-limit")
.getString("0")));
BinaryStoreManager mgr = instantiateBinaryStoreManager(xmlStore, loader, fPaged);
if (sSubclass.length() == 0)
{
return instantiateAsyncBinaryStoreManager(mgr, cbMaxAsync);
}
else
{
Object[] aoParam = cbMaxAsync <= 0 ? new Object[] {mgr}
: new Object[] {mgr, Integer.valueOf(cbMaxAsync)};
return (BinaryStoreManager) instantiateSubclass(sSubclass,
AsyncBinaryStoreManager.class, loader, aoParam, xmlParams);
}
}
// custom-store
if (sType.equals("custom-store-manager"))
{
if (sSubclass.length() == 0)
{
throw new IllegalArgumentException("Missing class-name:\n" +
xmlStore);
}
return (BinaryStoreManager) instantiateSubclass(sSubclass,
BinaryStoreManager.class, loader, null, xmlParams);
}
}
throw new IllegalArgumentException(
"Missing BinaryStoreManager configuration:\n" + xmlConfig);
}
/**
* Construct an AsyncBinaryStore using the specified parameters.
*
* @param store the BinaryStore to make asynchronous
* @param cbMaxAsync the maximum amount of "async writes" data that will
* be queued
*
* @return a new AsyncBinaryStore wrapping the passed BinaryStore
*/
protected AsyncBinaryStore instantiateAsyncBinaryStore(BinaryStore store, int cbMaxAsync)
{
return cbMaxAsync <= 0 ? new AsyncBinaryStore(store)
: new AsyncBinaryStore(store, cbMaxAsync);
}
/**
* Construct an AsyncBinaryStoreManager using the specified parameters.
*
* @param storeMgr the BinaryStoreManager to make asynchronous
* @param cbMaxAsync the maximum amount of "async writes" data that will
* be queued
*
* @return a new AsyncBinaryStoreManager wrapping the passed BinaryStoreManager
*/
protected AsyncBinaryStoreManager instantiateAsyncBinaryStoreManager(BinaryStoreManager storeMgr, int cbMaxAsync)
{
return cbMaxAsync <= 0 ? new AsyncBinaryStoreManager(storeMgr)
: new AsyncBinaryStoreManager(storeMgr, cbMaxAsync);
}
/**
* Construct an SerializationPagedCache using the specified parameters.
*
* This method exposes a corresponding SerializationPagedCache
* {@link SerializationPagedCache#SerializationPagedCache(BinaryStoreManager, int, int, ClassLoader) constructor}
* and is provided for the express purpose of allowing its override.
*/
protected SerializationPagedCache instantiateSerializationPagedCache(BinaryStoreManager storeMgr,
int cPages, int cPageSecs, ClassLoader loader)
{
return new SerializationPagedCache(storeMgr, cPages, cPageSecs, loader);
}
/**
* Construct an SerializationPagedCache using the specified parameters.
*
* This method exposes a corresponding SerializationPagedCache
* {@link SerializationPagedCache#SerializationPagedCache(BinaryStoreManager, int, int, boolean, boolean) constructor}
* and is provided for the express purpose of allowing its override.
*/
protected SerializationPagedCache instantiateSerializationPagedCache(BinaryStoreManager storeMgr,
int cPages, int cPageSecs, boolean fBinaryMap, boolean fPassive)
{
return new SerializationPagedCache(storeMgr, cPages, cPageSecs, fBinaryMap, fPassive);
}
/**
* Construct an SerializationCache using the specified parameters.
*
* This method exposes a corresponding SerializationCache
* {@link SerializationCache#SerializationCache(BinaryStore, int, ClassLoader) constructor}
* and is provided for the express purpose of allowing its override.
*/
protected SerializationCache instantiateSerializationCache(BinaryStore store, int cMax, ClassLoader loader)
{
return new SerializationCache(store, cMax, loader);
}
/**
* Construct an SerializationCache using the specified parameters.
*
* This method exposes a corresponding SerializationCache
* {@link SerializationCache#SerializationCache(BinaryStore, int, boolean) constructor}
* and is provided for the express purpose of allowing its override.
*/
protected SerializationCache instantiateSerializationCache(BinaryStore store, int cMax, boolean fBinaryMap)
{
return new SerializationCache(store, cMax, fBinaryMap);
}
/**
* Construct an SerializationMap using the specified parameters.
*
* This method exposes a corresponding SerializationMap
* {@link SerializationMap#SerializationMap(BinaryStore, ClassLoader) constructor}
* and is provided for the express purpose of allowing its override.
*/
protected SerializationMap instantiateSerializationMap(BinaryStore store, ClassLoader loader)
{
return new SerializationMap(store, loader);
}
/**
* Construct an SerializationMap using the specified parameters.
*
* This method exposes a corresponding SerializationMap
* {@link SerializationMap#SerializationMap(BinaryStore, boolean) constructor}
* and is provided for the express purpose of allowing its override.
*/
protected SerializationMap instantiateSerializationMap(BinaryStore store, boolean fBinaryMap)
{
return new SerializationMap(store, fBinaryMap);
}
/**
* Construct a SimpleSerializationMap using the specified parameters.
*
* This method exposes a corresponding SerializationMap {@link
* SimpleSerializationMap#SimpleSerializationMap(BinaryStore, ClassLoader)
* constructor} and is provided for the express purpose of allowing its
* override.
*
* @since Coherence 3.7
*/
protected SimpleSerializationMap instantiateSimpleSerializationMap(
BinaryStore store, ClassLoader loader)
{
return new SimpleSerializationMap(store, loader);
}
/**
* Construct a SimpleSerializationMap using the specified parameters.
*
* This method exposes a corresponding SerializationMap {@link
* SimpleSerializationMap#SimpleSerializationMap(BinaryStore, boolean)
* constructor} and is provided for the express purpose of allowing its
* override.
*
* @since Coherence 3.7
*/
protected SimpleSerializationMap instantiateSimpleSerializationMap(
BinaryStore store, boolean fBinaryMap)
{
return new SimpleSerializationMap(store, fBinaryMap);
}
/**
* Create a BundlingNamedCache using the "operation-bundling" element.
*
* @param cache the wrapped cache
* @param xmlBundling the "operation-bundling" element
*
* @return a newly instantiated BundlingNamedCache
*/
protected BundlingNamedCache instantiateBundlingNamedCache(NamedCache cache,
XmlElement xmlBundling)
{
BundlingNamedCache cacheBundle = new BundlingNamedCache(cache);
for (Iterator iter = xmlBundling.getElements("bundle-config");
iter.hasNext();)
{
XmlElement xmlBundle = (XmlElement) iter.next();
String sOperation = xmlBundle.getSafeElement("operation-name").getString("all");
int cBundle = convertInt(xmlBundle.getSafeElement("preferred-size"));
if (sOperation.equals("all"))
{
initializeBundler(cacheBundle.ensureGetBundler(cBundle), xmlBundle);
initializeBundler(cacheBundle.ensurePutBundler(cBundle), xmlBundle);
initializeBundler(cacheBundle.ensureRemoveBundler(cBundle), xmlBundle);
}
else if (sOperation.equals("get"))
{
initializeBundler(cacheBundle.ensureGetBundler(cBundle), xmlBundle);
}
else if (sOperation.equals("put"))
{
initializeBundler(cacheBundle.ensurePutBundler(cBundle), xmlBundle);
}
else if (sOperation.equals("remove"))
{
initializeBundler(cacheBundle.ensureRemoveBundler(cBundle), xmlBundle);
}
else
{
throw new IllegalArgumentException(
"Invalid \"operation-name\" element:\n" + xmlBundle);
}
}
return cacheBundle;
}
/**
* Initialize the specified bundler using the "bundle-config" element.
*
* @param bundler the bundler
* @param xmlBundle a "bundle-config" element
*/
protected void initializeBundler(AbstractBundler bundler, XmlElement xmlBundle)
{
if (bundler != null)
{
bundler.setThreadThreshold(
convertInt(xmlBundle.getSafeElement("thread-threshold"), 4));
bundler.setDelayMillis(
convertInt(xmlBundle.getSafeElement("delay-millis"), 1));
bundler.setAllowAutoAdjust(
xmlBundle.getSafeElement("auto-adjust").getBoolean(false));
}
}
/**
* Create a backing Map using the "class-scheme" element.
* This method is a thin wrapper around
* {@link #instantiateAny instantiateAny}.
*
* @param info the cache info
* @param xmlClass "class-scheme" element
* @param context BackingMapManagerContext to be used
* @param loader the ClassLoader to instantiate necessary classes
*
* @return a newly instantiated Map
*/
protected Map instantiateMap(CacheInfo info, XmlElement xmlClass,
BackingMapManagerContext context, ClassLoader loader)
{
try
{
return (Map) instantiateAny(info, xmlClass, context, loader);
}
catch (ClassCastException e)
{
throw new IllegalArgumentException("Not a map:\n" + xmlClass);
}
}
/**
* Create a CacheLoader, CacheStore or BinaryEntryStore using the
* "cachestore-scheme" element.
*
* @param info the cache info
* @param xmlStore "cachestore-scheme" element for the store or loader
* @param context BackingMapManagerContext to be used
* @param loader the ClassLoader to instantiate necessary classes
*
* @return a newly instantiated CacheLoader, CacheStore or BinaryEntryStore
*/
protected Object instantiateCacheStore(CacheInfo info, XmlElement xmlStore,
BackingMapManagerContext context, ClassLoader loader)
{
xmlStore = resolveScheme(xmlStore, info, true, false);
if (xmlStore == null || XmlHelper.isEmpty(xmlStore))
{
return null;
}
String sSchemeType = xmlStore.getName();
try
{
switch (translateSchemeType(sSchemeType))
{
case SCHEME_CLASS:
return instantiateAny(info, xmlStore, context, loader);
case SCHEME_REMOTE_CACHE:
{
NamedCache cacheRemote = configureCache(info, xmlStore,
NullImplementation.getClassLoader());
if (!isSerializerCompatible(
cacheRemote.getCacheService(), context.getCacheService()))
{
Service service = context.getCacheService();
ExternalizableHelper.reportIncompatibleSerializers(cacheRemote,
service.getInfo().getServiceName(), service.getSerializer());
cacheRemote.release();
cacheRemote = configureCache(info, xmlStore, loader);
}
return cacheRemote;
}
default:
throw new UnsupportedOperationException(
"instantiateCacheStore: " + sSchemeType);
}
}
catch (ClassCastException e)
{
throw new IllegalArgumentException(
"Not a CacheLoader:\n" + xmlStore);
}
}
/**
* Create a MapListener using the using the "class-scheme" element.
* If the value of any "param-value" element contains the literal
* "{cache-name}", replace it with the actual cache name.
*
* @param info the cache info
* @param xmlClass "class-scheme" element
* @param context BackingMapManagerContext to be used
* @param loader the ClassLoader to instantiate necessary classes
*
* @return a newly instantiated MapListener
*/
protected MapListener instantiateMapListener(CacheInfo info, XmlElement xmlClass,
BackingMapManagerContext context, ClassLoader loader)
{
try
{
return (MapListener) instantiateAny(info, xmlClass, context, loader);
}
catch (ClassCastException e)
{
throw new IllegalArgumentException("Not a listener:\n" + xmlClass);
}
}
/**
* Create an Object using "class-scheme" element.
*
* If the value of any "param-value" element contains the literal
* "{cache-name}", replace it with the actual cache name.
* If the value of "param-value" element is "{class-loader}"
* and "param-type" element is "java.lang.ClassLoader" replace it
* with the current ClassLoader object.
* If the value of "param-value" element is "{manager-context}"
* and "param-type" element is "com.tangosol.net.BackingMapManagerContext"
* replace it with the current BackingMapManagerContext object.
* Finally, if the value of "param-type" is "{scheme-ref}" then the
* "param-value" should be a name of the scheme that will be used in place
* of the value.
*
* @param info the cache info
* @param xmlClass "class-scheme" element
* @param context BackingMapManagerContext to be used
* @param loader the ClassLoader to instantiate necessary classes
*
* @return a newly instantiated Object
*/
public Object instantiateAny(final CacheInfo info, XmlElement xmlClass,
final BackingMapManagerContext context, final ClassLoader loader)
{
if (translateSchemeType(xmlClass.getName()) != SCHEME_CLASS)
{
throw new IllegalArgumentException("Invalid class definition: " + xmlClass);
}
XmlHelper.ParameterResolver resolver = new XmlHelper.ParameterResolver()
{
public Object resolveParameter(String sType, String sValue)
{
if (sValue.equals(CLASS_LOADER))
{
// sType extends "java.lang.ClassLoader"
return loader;
}
if (sValue.equals(MGR_CONTEXT))
{
// sType implements "com.tangosol.net.BackingMapManagerContext"
return context;
}
if (sType.equals(SCHEME_REF))
{
// sValue is the scheme name
XmlElement xmlScheme = resolveScheme(new CacheInfo(
info.getCacheName(), sValue, info.getAttributes()));
String sSchemeType = xmlScheme.getName();
switch (translateSchemeType(sSchemeType))
{
case SCHEME_REPLICATED:
case SCHEME_OPTIMISTIC:
case SCHEME_DISTRIBUTED:
case SCHEME_NEAR:
case SCHEME_VERSIONED_NEAR:
case SCHEME_REMOTE_CACHE:
return configureCache(info, xmlScheme, loader);
case SCHEME_INVOCATION:
case SCHEME_REMOTE_INVOCATION:
return ensureServiceInternal(xmlScheme);
case SCHEME_LOCAL:
case SCHEME_OVERFLOW:
case SCHEME_DISK:
case SCHEME_EXTERNAL:
case SCHEME_EXTERNAL_PAGED:
case SCHEME_READ_WRITE_BACKING:
case SCHEME_VERSIONED_BACKING:
case SCHEME_FLASHJOURNAL:
case SCHEME_RAMJOURNAL:
{
Map mapListeners = context == null ? null :
((Manager) context.getManager()).m_mapBackingMapListeners;
return configureBackingMap(
info, xmlScheme, context, null, mapListeners);
}
case SCHEME_CLASS:
return instantiateAny(info, xmlScheme, context, loader);
default:
throw new UnsupportedOperationException(
"instantiateAny: " + sSchemeType);
}
}
if (sType.equals(CACHE_REF))
{
// sValue is the referenced cache name
return ensureCache(sValue, loader);
}
return XmlHelper.ParameterResolver.UNRESOLVED;
}
};
return XmlHelper.createInstance(xmlClass, loader, resolver);
}
/**
* Construct an instance of the specified class using the specified
* parameters.
*
* @param sClass the class name
* @param clzSuper the super class of the newly instantiated class
* @param loader the ClassLoader to instantiate necessary classes
* @param aoParam the constructor parameters
* @param xmlParams the "init-params" XmlElement (optional)
*
* @return a newly instantiated Object
*/
public Object instantiateSubclass(String sClass, Class clzSuper, ClassLoader loader,
Object[] aoParam, XmlElement xmlParams)
{
if (sClass == null || sClass.length() == 0 || clzSuper == null )
{
throw new IllegalArgumentException(
"Class name and super class must be specified");
}
try
{
Class clz = ExternalizableHelper.loadClass(sClass, loader,
clzSuper.getClassLoader());
if (!clzSuper.isAssignableFrom(clz))
{
throw new IllegalArgumentException(
clzSuper + " is not a super-class of " + clz);
}
Object oTarget;
if (aoParam == null)
{
oTarget = clz.newInstance();
}
else
{
oTarget = ClassHelper.newInstance(clz, aoParam);
}
if (xmlParams != null && oTarget instanceof XmlConfigurable)
{
XmlElement xmlConfig = new SimpleElement("config");
XmlHelper.transformInitParams(xmlConfig, xmlParams);
((XmlConfigurable) oTarget).setConfig(xmlConfig);
}
return oTarget;
}
catch (Exception e)
{
throw ensureRuntimeException(e,
"Fail to instantiate subclass: " + sClass + " of " + clzSuper);
}
}
/**
* Release all resources associated with the specified backing map.
*
* @param map the map being released
* @param mapListeners map of registered map listeners keyed by the
* corresponding map references
*/
public void release(Map map, Map mapListeners)
{
// remove known map listener
if (map instanceof ObservableMap && mapListeners != null)
{
MapListener listener = (MapListener) mapListeners.get(map);
if (listener != null)
{
((ObservableMap) map).removeMapListener(listener);
mapListeners.remove(map);
}
}
// process recursively
if (map instanceof LocalCache)
{
CacheLoader loader = ((LocalCache) map).getCacheLoader();
if (loader instanceof MapCacheStore)
{
release(((MapCacheStore) loader).getMap(), mapListeners);
}
else
{
release(loader);
}
}
else if (map instanceof OverflowMap)
{
release(((OverflowMap) map).getFrontMap(), mapListeners);
release(((OverflowMap) map).getBackMap() , mapListeners);
}
else if (map instanceof ReadWriteBackingMap)
{
((ReadWriteBackingMap) map).release();
release(((ReadWriteBackingMap) map).getInternalCache(), mapListeners);
}
else if (map instanceof SerializationMap)
{
release(((SerializationMap) map).getBinaryStore());
}
else if (map instanceof SimpleSerializationMap)
{
release (((SimpleSerializationMap) map).getBinaryStore());
}
else if (map instanceof BinaryMap)
{
ByteBufferManager bufmgr = ((BinaryMap) map).getBufferManager();
if (bufmgr instanceof MappedBufferManager)
{
((MappedBufferManager) bufmgr).close();
}
}
// regardless of the above, the map may be disposable as well
if (map instanceof Disposable)
{
((Disposable) map).dispose();
}
}
/**
* Release all resources associated with the specified loader.
*
* @param loader the cache loader being released
*/
protected void release(CacheLoader loader)
{
if (loader instanceof Disposable)
{
((Disposable) loader).dispose();
}
else
{
try
{
ClassHelper.invoke(loader, "close", ClassHelper.VOID);
}
catch (Exception e) {}
}
}
/**
* Release all resources associated with the specified binary store.
*
* @param store the binary store being released
*/
protected void release(BinaryStore store)
{
if (store instanceof Disposable)
{
((Disposable) store).dispose();
}
else
{
try
{
ClassHelper.invoke(store, "close", ClassHelper.VOID);
}
catch (Exception e) {}
}
}
/**
* Release a cache managed by this factory, optionally destroying it.
*
* @param cache the cache to release
* @param fDestroy true to destroy the cache as well
*/
protected void releaseCache(NamedCache cache, boolean fDestroy)
{
ScopedCacheReferenceStore store = m_store;
String sCacheName = cache.getCacheName();
ClassLoader loader = cache instanceof ClassLoaderAware
? ((ClassLoaderAware) cache).getContextClassLoader()
: getContextClassLoader();
Runnable runRelease = () ->
{
// allow cache to release/destroy internal resources
if (fDestroy)
{
cache.destroy();
}
else
{
cache.release();
}
};
if (store.releaseCache(cache, loader, runRelease))
{
// nothing to do
}
else if (cache.isActive())
{
// active, but not managed by this factory
throw new IllegalArgumentException("The cache " + sCacheName +
" was created using a different factory; that same" +
" factory should be used to release the cache.");
}
}
/**
* Translate the scheme name into the scheme type. Valid scheme types are
* any of the SCHEME_* constants.
*
* @param sScheme the scheme name
*
* @return the scheme type
*/
public int translateSchemeType(String sScheme)
{
return translateStandardSchemeType(sScheme);
}
/**
* Translate the scheme name into the scheme type. Valid scheme types are
* any of the SCHEME_* constants.
*
* @param sScheme the scheme name
*
* @return the scheme type
*/
public static int translateStandardSchemeType(String sScheme)
{
return sScheme.equals("replicated-scheme") ? SCHEME_REPLICATED
: sScheme.equals("optimistic-scheme") ? SCHEME_OPTIMISTIC
: sScheme.equals("distributed-scheme") ? SCHEME_DISTRIBUTED
: sScheme.equals("local-scheme") ? SCHEME_LOCAL
: sScheme.equals("overflow-scheme") ? SCHEME_OVERFLOW
: sScheme.equals("disk-scheme") ? SCHEME_DISK
: sScheme.equals("external-scheme") ? SCHEME_EXTERNAL
: sScheme.equals("paged-external-scheme") ? SCHEME_EXTERNAL_PAGED
: sScheme.equals("class-scheme") ? SCHEME_CLASS
: sScheme.equals("near-scheme") ? SCHEME_NEAR
: sScheme.equals("versioned-near-scheme") ? SCHEME_VERSIONED_NEAR
: sScheme.equals("read-write-backing-map-scheme") ? SCHEME_READ_WRITE_BACKING
: sScheme.equals("versioned-backing-map-scheme") ? SCHEME_VERSIONED_BACKING
: sScheme.equals("invocation-scheme") ? SCHEME_INVOCATION
: sScheme.equals("proxy-scheme") ? SCHEME_PROXY
: sScheme.equals("remote-cache-scheme") ? SCHEME_REMOTE_CACHE
: sScheme.equals("remote-invocation-scheme") ? SCHEME_REMOTE_INVOCATION
: sScheme.equals("transactional-scheme") ? SCHEME_TRANSACTIONAL
: sScheme.equals("flashjournal-scheme") ? SCHEME_FLASHJOURNAL
: sScheme.equals("ramjournal-scheme") ? SCHEME_RAMJOURNAL
: sScheme.equals("paged-topic-scheme") ? SCHEME_PAGED_TOPIC
: SCHEME_UNKNOWN;
}
/**
* Determines whether or not the specified Map is optimized for a
* {@link Map#putAll putAll()} operation versus a regular
* {@link Map#put put()} operation.
*
* @param map a Map instance to check
*
* @return true if putAll should be preferred over put if the return value
* is not needed; false otherwise
*/
public static boolean isPutAllOptimized(Map map)
{
if (map instanceof SafeHashMap || map instanceof HashMap)
{
return false;
}
if (map instanceof ReadWriteBackingMap)
{
ReadWriteBackingMap mapRW = (ReadWriteBackingMap) map;
return mapRW.isWriteThrough()
|| isPutAllOptimized(mapRW.getInternalCache());
}
// assume it is for all other types
return true;
}
/**
* Determine whether the provided map allows reference access to the keys it holds.
*
* @param map a Map instance to check
*
* @return true iff the Map's keys can be canonicalized
*/
public static boolean isCanonicalKeySupported(Map map)
{
// only CCMs allow us to fetch the original key (#getCacheEntry)
if (map instanceof ConfigurableCacheMap)
{
return true;
}
return false;
}
/**
* Return the request timeout based on the {@link XmlElement}.
*
* @param xmlScheme the xml scheme that stores the request timeout
*
* @return the request timeout
*/
protected static long getRequestTimeout(XmlElement xmlScheme)
{
String sTimeout;
switch (translateStandardSchemeType(xmlScheme.getName()))
{
case SCHEME_REMOTE_CACHE:
case SCHEME_REMOTE_INVOCATION:
sTimeout = xmlScheme.getSafeElement("initiator-config/outgoing-message-handler/request-timeout")
.getString();
break;
default:
sTimeout = xmlScheme.getSafeElement("request-timeout").getString();
}
return sTimeout.isEmpty() ? -1 : XmlHelper.parseTime(sTimeout);
}
/**
* Parse undocumented values of the <partitioned> element or
* attribute value to determine if the backing map is partitioned.
*
* @param sPartitioned the value of the <partitioned> element
* @param nSchemeType the type of the scheme
*
* @return true if the backing map is partitioned
*
* @since Coherence 3.6
*/
private static boolean isPartitioned(String sPartitioned, int nSchemeType)
{
if (sPartitioned.length() == 0)
{
// if no particular value is specified, we'll check the scheme type
switch (nSchemeType)
{
// flash and ram journals are by default partitioned
case SCHEME_FLASHJOURNAL:
case SCHEME_RAMJOURNAL:
return true;
default:
return false;
}
}
if (sPartitioned.equals("observable")) // do NOT doc!
{
return true;
}
Boolean BPartitioned = (Boolean) XmlHelper.convert(sPartitioned, XmlValue.TYPE_BOOLEAN);
if (BPartitioned == null)
{
throw new IllegalArgumentException("Invalid \"partitioned\" value: \""
+ sPartitioned + "\"");
}
return BPartitioned.booleanValue();
}
/**
* Determines whether or not the serializers for the specified services are
* compatible. In other words, this method returns true iff object
* serialized with the first Serializer can be deserialized by the second
* and visa versa.
*
* @param serviceThis the first Service
* @param serviceThat the second Service
*
* @return true iff the two Serializers are stream compatible
*/
protected boolean isSerializerCompatible(Service serviceThis, Service serviceThat)
{
return ExternalizableHelper.isSerializerCompatible(
serviceThis.getSerializer(), serviceThat.getSerializer());
}
// ----- Interceptor support --------------------------------------------
/**
* Using the provided base XML find all interceptors instantiating as
* appropriate and registering with the Events {@link Registry}.
*
* Interceptors may exist in either the caching-scheme-mapping
* or within a distributed-scheme. The former allows restricting
* events based on cache named whilst the latter can be bound at service
* level.
*
* @param xmlConfig the base cache configuration xml
*
* @since 12.1.2
*/
protected void configureInterceptors(XmlElement xmlConfig)
{
// register global interceptors
XmlElement xmlGlobalIncptrs = xmlConfig.getElement("interceptors");
if (xmlGlobalIncptrs != null)
{
for (Iterator iterInterceptor = xmlGlobalIncptrs.getElements("interceptor");
iterInterceptor.hasNext();)
{
XmlElement xmlInterceptor = (XmlElement) iterInterceptor.next();
registerInterceptor(xmlInterceptor, "", "");
}
}
// process caching-scheme-mapping section
for (Iterator iter = xmlConfig.getSafeElement("caching-scheme-mapping").
getElements("cache-mapping"); iter.hasNext();)
{
XmlElement xmlMapping = (XmlElement) iter.next();
XmlElement xmlInterceptors = xmlMapping.getElement("interceptors");
if (xmlInterceptors == null)
{
continue;
}
XmlElement xmlScheme = findScheme(xmlMapping.getSafeElement("scheme-name").getString());
String sServiceName = xmlScheme.getSafeElement("service-name").getString(null);
String sCacheName = xmlMapping.getSafeElement("cache-name").getString(null);
for (Iterator iterInterceptor = xmlInterceptors.getElements("interceptor");
iterInterceptor.hasNext();)
{
XmlElement xmlInterceptor = (XmlElement) iterInterceptor.next();
registerInterceptor(xmlInterceptor, sCacheName, sServiceName);
}
}
// process cache-schemes section
for (Iterator iter = xmlConfig.getSafeElement("caching-schemes").
getElementList().iterator(); iter.hasNext();)
{
XmlElement xmlScheme = (XmlElement) iter.next();
String sSchemeType = xmlScheme.getName();
XmlElement xmlInterceptors = xmlScheme.getElement("interceptors");
if (xmlInterceptors == null || translateStandardSchemeType(sSchemeType) != SCHEME_DISTRIBUTED)
{
continue;
}
String sServiceName = xmlScheme.getSafeElement("service-name").getString();
for (Iterator iterInterceptor = xmlInterceptors.getElements("interceptor");
iterInterceptor.hasNext();)
{
XmlElement xmlInterceptor = (XmlElement) iterInterceptor.next();
registerInterceptor(xmlInterceptor, "", sServiceName);
}
}
}
/**
* This method will instantiate an {@link EventInterceptor} based on the
* XML provided. Additionally the service and cache names will be passed
* to the interceptor if possible. These values should be empty strings
* if they are to be ignored.
*
* This method will register the {@link EventInterceptor} instantiated
* with the events registry; {@link Registry}.
*
* @param xmlInterceptor the xml containing the definition of the
* interceptor
* @param sCacheName the cache name or an empty string
* @param sServiceName the service name or an empty string
*
* @throws IllegalArgumentException if a nested instance
* or class-scheme is absent, an interceptor identifier
* is missing or the {@link EventInterceptor} implementation can
* not be resolved
*
* @since Coherence 12.1.2
*/
protected void registerInterceptor(XmlElement xmlInterceptor, String sCacheName, String sServiceName)
{
if (xmlInterceptor == null)
{
return;
}
// mandatory elements
XmlElement xmlClass = xmlInterceptor.getElement("instance");
xmlClass = xmlClass == null ? xmlInterceptor.getElement("class-scheme") : xmlClass;
// validate
if (xmlClass == null)
{
throw new IllegalArgumentException(String.format(
"Interceptor specified in [cacheName: %s, serviceName: %s] must specify class",
sCacheName, sServiceName));
}
String sClassName = xmlClass.getSafeElement("class-name").getString();
NamedEventInterceptorBuilder builder = new NamedEventInterceptorBuilder();
builder.setCustomBuilder(new InstanceBuilder(sClassName, XmlHelper.parseInitParams(xmlClass.getSafeElement("init-params"))));
// optional attributes
RegistrationBehavior behavior = com.tangosol.util.RegistrationBehavior.ALWAYS;
String sIdentifier = xmlInterceptor.getSafeElement("name").getString();
if (sIdentifier.length() > 0)
{
builder.setName(sIdentifier);
// if name is explicitly configured then fail if there is a duplicate
behavior = RegistrationBehavior.FAIL;
}
builder.setRegistrationBehavior(behavior);
String sOrder = xmlInterceptor.getSafeElement("order").getString();
if (sOrder.length() > 0)
{
builder.setOrder(Interceptor.Order.valueOf(sOrder));
}
// create a parameter resolver containing the cache pattern and service name
// so that the builder can use it if required
ResolvableParameterList resolver = new ResolvableParameterList();
if (sCacheName != null && !sCacheName.isEmpty())
{
resolver.add(new Parameter("cache-name", sCacheName));
}
if (sServiceName != null && !sServiceName.isEmpty())
{
resolver.add(new Parameter("service-name", sServiceName));
}
NamedEventInterceptor interceptor = builder.realize(resolver, getConfigClassLoader(), null);
getInterceptorRegistry().registerEventInterceptor(interceptor);
}
// ----- Registration support --------------------------------------------
/**
* Register the specified NamedCache with the cluster registry.
*
* @param cache the NamedCache object to register
* @param sContext the cache context (tier)
*
* @deprecated as of Coherence 3.7.1; use {@link
* MBeanHelper#registerCacheMBean(NamedCache, String)} instead
*/
protected void register(NamedCache cache, String sContext)
{
MBeanHelper.registerCacheMBean(cache, sContext);
}
/**
* Register the specified cache with the cluster registry.
*
* @param service the CacheService that the cache belongs to
* @param sCacheName the cache name
* @param sContext the cache context (tier)
* @param map the cache object to register
*
* @deprecated as of Coherence 3.7.1; use {@link
* MBeanHelper#registerCacheMBean(CacheService, String, String, Map)} instead
*/
protected void register(CacheService service, String sCacheName,
String sContext, Map map)
{
MBeanHelper.registerCacheMBean(service, sCacheName, sContext, map);
}
/**
* Unregister all the managed objects that belong to the specified cache
* from the cluster registry.
*
* @param service the CacheService that the cache belongs to
* @param sCacheName the cache name
* @deprecated as of Coherence 3.7.1; use {@link
* MBeanHelper#unregisterCacheMBean(CacheService, String, String)} instead
*/
protected void unregister(CacheService service, String sCacheName)
{
MBeanHelper.unregisterCacheMBean(service, sCacheName, "tier=back");
}
/**
* Unregister the caches for a given cache name and context
* from the cluster registry.
*
* @param sCacheName the cache name
* @param sContext the cache context (tier)
*
* @deprecated as of Coherence 3.7.1; use {@link
* MBeanHelper#unregisterCacheMBean(String, String)} instead
*/
protected void unregister(String sCacheName, String sContext)
{
MBeanHelper.unregisterCacheMBean(sCacheName, sContext);
}
/**
* Push cache context into a thread-local storage.
*
* @param sContext cache context (tag)
*/
protected void pushCacheContext(String sContext)
{
m_tlo.set(sContext);
}
/**
* Pop cache context from a thread-local storage.
*
* @return the popped cache context
*/
protected String popCacheContext()
{
String s = (String) m_tlo.get();
m_tlo.set(null); // ThreadLocal.remove() is 1.5 only
return s;
}
/**
* Convert the value in the specified {@link XmlValue} to an int. If the
* conversion fails, a warning will be logged.
*
* @param xmlValue the element expected to contain an int value
*
* @return the int value in the provided element, or 0 upon a
* conversion failure
*/
protected int convertInt(XmlValue xmlValue)
{
return convertInt(xmlValue, 0);
}
/**
* Convert the value in the specified {@link XmlValue} to an int. If the
* conversion fails, a warning will be logged.
*
* @param xmlValue the element expected to contain an int value
* @param nDefault the value that will be returned if the element does
* not contain a value that can be converted to int
*
* @return the int value in the provided element, or nDefault upon a
* conversion failure
*/
protected int convertInt(XmlValue xmlValue, int nDefault)
{
try
{
String sValue = xmlValue.getString();
Integer I = (Integer) XmlHelper.convert(sValue, XmlValue.TYPE_INT);
return I == null ? nDefault : I.intValue();
}
catch (RuntimeException e)
{
reportConversionError(xmlValue, "int", String.valueOf(nDefault), e);
return nDefault;
}
}
/**
* Convert the value in the specified {@link XmlValue} to a long. If the
* conversion fails, a warning will be logged.
*
* @param xmlValue the element expected to contain a long value
*
* @return the long value in the provided element, or 0 upon a
* conversion failure
*/
protected long convertLong(XmlValue xmlValue)
{
long lDefault = 0;
try
{
String sValue = xmlValue.getString();
Long L = (Long) XmlHelper.convert(sValue, XmlValue.TYPE_LONG);
return L == null ? lDefault : L.longValue();
}
catch (RuntimeException e)
{
reportConversionError(xmlValue, "long", String.valueOf(lDefault), e);
return lDefault;
}
}
/**
* Convert the value in the specified {@link XmlValue} to a double. If the
* conversion fails, a warning will be logged.
*
* @param xmlValue the element expected to contain a double value
*
* @return the double value in the provided element, or 0.0 upon a
* conversion failure
*/
protected double convertDouble(XmlValue xmlValue)
{
double dDefault = 0.0;
try
{
String sValue = xmlValue.getString();
Double D = (Double) XmlHelper.convert(sValue, XmlValue.TYPE_DOUBLE);
return D == null ? dDefault : D.doubleValue();
}
catch (RuntimeException e)
{
reportConversionError(xmlValue, "double", String.valueOf(dDefault), e);
return dDefault;
}
}
/**
* Log a failed type conversion.
*
* @param xmlValue element that contains the value that failed conversion
* @param sType type that conversion was attempted to
* @param sDefault default value that will be substituted
* @param e root cause of failed type conversion
*/
protected void reportConversionError(XmlValue xmlValue, String sType,
String sDefault, RuntimeException e)
{
CacheFactory.log("Error converting " + xmlValue +
" to " + sType + "; proceeding with default value of "
+ sDefault + "\n" + getStackTrace(e),
CacheFactory.LOG_WARN);
}
// ----- inner classes --------------------------------------------------
/**
* BackingMapManager implementation that uses the configuration XML to
* create the required backing maps and provides client access to those maps.
*/
public class Manager
extends AbstractBackingMapManager
{
// ----- constructors -------------------------------------------
public Manager()
{
}
// ----- BackingMapManager interface ----------------------------
/**
* {@inheritDoc}
*/
public void init(BackingMapManagerContext context)
{
super.init(context);
m_mapBackingMap = new HashMap();
m_mapBackingMapListeners = new IdentityHashMap();
}
/**
* {@inheritDoc}
*/
public Map instantiateBackingMap(String sName)
{
CacheInfo infoCache = findSchemeMapping(sName);
XmlElement xmlScheme = resolveScheme(infoCache);
xmlScheme.addAttribute("tier").setString("back"); // mark the "entry point"
pushCacheContext("tier=back");
Map map = configureBackingMap(
infoCache, xmlScheme, getContext(), null, m_mapBackingMapListeners);
setBackingMap(sName, map);
return map;
}
/**
* {@inheritDoc}
*/
public boolean isBackingMapPersistent(String sName)
{
CacheInfo infoCache = findSchemeMapping(sName);
XmlElement xmlScheme = resolveScheme(infoCache);
return !xmlScheme.getSafeElement("transient").getBoolean(true);
}
/**
* {@inheritDoc}
*/
public boolean isBackingMapSlidingExpiry(String sName)
{
CacheInfo infoCache = findSchemeMapping(sName);
XmlElement xmlScheme = resolveScheme(infoCache);
return !xmlScheme.getSafeElement("sliding-expiry").getBoolean();
}
/**
* {@inheritDoc}
*/
public StorageAccessAuthorizer getStorageAccessAuthorizer(String sName)
{
return null;
}
/**
* {@inheritDoc}
*/
public void releaseBackingMap(String sName, Map map)
{
unregister(getContext().getCacheService(), sName);
release(map, m_mapBackingMapListeners);
setBackingMap(sName, null);
}
// ---- helpers -------------------------------------------------
/**
* Get the backing Map associated with a given cache.
*
* @param sName the cache name
*
* @return a Map associated with the specified name
*/
public Map getBackingMap(String sName)
{
return m_mapBackingMap == null ? null :
(Map) m_mapBackingMap.get(sName);
}
/**
* Associate the specified backing Map with a given name.
*
* @param sName the cache name
* @param map the backing map associated with the specified name
*/
protected void setBackingMap(String sName, Map map)
{
if (map != null && getBackingMap(sName) != null)
{
throw new IllegalArgumentException("BackingMap is not resettable: " + sName);
}
m_mapBackingMap.put(sName, map);
}
/**
* Obtain the "container" DefaultConfigurableCacheFactory that created
* this manager and which this manager is bound to.
*
* @return the DefaultConfigurableCacheFactory that created this manager
*/
public DefaultConfigurableCacheFactory getCacheFactory()
{
return DefaultConfigurableCacheFactory.this;
}
// ----- data fields --------------------------------------------
/**
* The map of backing maps keyed by corresponding cache names.
*/
protected Map m_mapBackingMap;
/**
* The map of backing map listeners keyed by the corresponding backing
* map references.
*/
protected Map m_mapBackingMapListeners;
}
/**
* CacheInfo is a placeholder for cache attributes retrieved during parsing
* the corresponding cache mapping element.
*/
public static class CacheInfo
{
/**
* Construct a CacheInfo object.
*
* @param sCacheName the cache name
* @param sSchemeName the corresponding scheme name
* @param mapAttribute the corresponding map of attributes
*/
public CacheInfo(String sCacheName, String sSchemeName, Map mapAttribute)
{
m_sCacheName = sCacheName;
m_sSchemeName = sSchemeName;
m_mapAttribute = mapAttribute;
}
// ----- accessors ------------------------------------------------
/**
* Obtain the cache name.
*
* @return the cache name
*/
public String getCacheName()
{
return m_sCacheName;
}
/**
* Obtain the scheme name.
*
* @return the scheme name
*/
public String getSchemeName()
{
return m_sSchemeName;
}
/**
* Obtain the attribute map.
*
* @return the attribute map
*/
public Map getAttributes()
{
return m_mapAttribute;
}
// ----- helpers --------------------------------------------------
/**
* Find and replace the attributes names in "{}" format with the
* corresponding values for this cache info.
*
* Note: the content of the specified XmlElement could be modified,
* so the caller is supposed to clone the passed in XML if necessary.
*
* @param xml the XmlElement to replace "{}" attributes at
*/
public void replaceAttributes(XmlElement xml)
{
for (Iterator iter = xml.getElementList().iterator(); iter.hasNext();)
{
XmlElement xmlChild = (XmlElement) iter.next();
if (!xmlChild.isEmpty())
{
String sText = xmlChild.getString();
int ofStart = sText.indexOf('{');
int ofEnd = -1;
boolean fReplace = false;
StringBuffer sbTextNew = new StringBuffer();
while (ofStart >= 0)
{
sbTextNew.append(sText.substring(ofEnd + 1, ofStart));
ofEnd = sText.indexOf('}', ofStart);
if (ofEnd < 0)
{
CacheFactory.log("Invalid attribute format: "
+ sText, CacheFactory.LOG_ERR);
fReplace = false;
break;
}
String sAttribute = sText.substring(ofStart, ofEnd + 1); // "{name value}"
String sAttrName = sText.substring(ofStart + 1, ofEnd).trim(); // "name value"
String sDefault = null;
String[] asToken = sAttrName.split("\\s+");
if (asToken.length == 2)
{
sAttrName = asToken[0];
sDefault = asToken[1];
}
String sValue = sAttribute.equals(CACHE_NAME) ?
getCacheName() : (String) getAttributes().get(sAttrName);
if (sValue == null)
{
if (sDefault == null)
{
if (!sAttribute.equals(CLASS_LOADER)
&& !sAttribute.equals(MGR_CONTEXT)
&& !sAttribute.equals(SCHEME_REF)
&& !sAttribute.equals(CACHE_REF))
{
CacheFactory.log("Missing parameter definition: "
+ sAttribute + " for cache \""
+ getCacheName() + '"', CacheFactory.LOG_WARN);
}
fReplace = false;
break;
}
else
{
sValue = sDefault;
}
}
sbTextNew.append(sValue);
fReplace = true;
ofStart = sText.indexOf('{', ofEnd);
}
if (fReplace)
{
sbTextNew.append(sText.substring(ofEnd + 1));
xmlChild.setString(sbTextNew.toString());
}
}
replaceAttributes(xmlChild);
}
}
/**
* Generate a synthetic CacheInfo for a cache that has a name suffixed
* with the specified string.
*
* @param sSuffix the cache name suffix
*
* @return the "cloned" synthetic CacheInfo
*/
public CacheInfo getSyntheticInfo(String sSuffix)
{
return new CacheInfo(getCacheName() + sSuffix, null, getAttributes());
}
// ----- data fields ----------------------------------------------
/**
* The cache name.
*/
protected String m_sCacheName;
/**
* The corresponding scheme name.
*/
protected String m_sSchemeName;
/**
* Map of scheme attributes.
*/
protected Map m_mapAttribute;
}
/**
* BackingMapManager implementation used by PartitionAwareBackingMap(s) to
* lazily configure the enclosing PABM based on the configuration settings of
* the enclosed maps.
*/
protected class PartitionedBackingMapManager
extends AbstractBackingMapManager
{
protected PartitionedBackingMapManager(CacheInfo info, XmlElement xmlScheme,
BackingMapManagerContext context, ClassLoader loader)
{
m_info = info;
m_xmlScheme = xmlScheme;
m_context = context;
m_loader = loader;
}
// ----- BackingMapManager interface ------------------------------
/**
* {@inheritDoc}
*/
public Map instantiateBackingMap(String sName)
{
XmlElement xmlScheme = m_xmlScheme;
// the "partition-name" attribute serves as a flag to the
// configureBackingMap() method indicating that the resulting
// map is a part of the composite PABM topology
xmlScheme.addAttribute("partition-name").setString(sName);
return configureBackingMap(m_info, xmlScheme, m_context, m_loader, null);
}
/**
* {@inheritDoc}
*/
public boolean isBackingMapPersistent(String sName)
{
// this method should never be called
throw new UnsupportedOperationException();
}
/**
* {@inheritDoc}
*/
public boolean isBackingMapSlidingExpiry(String sName)
{
// this method should never be called
throw new UnsupportedOperationException();
}
/**
* {@inheritDoc}
*/
public StorageAccessAuthorizer getStorageAccessAuthorizer(String sName)
{
return null;
}
/**
* {@inheritDoc}
*/
public void releaseBackingMap(String sName, Map map)
{
release(map, null);
}
// ----- accessors and helpers ------------------------------------
/**
* Obtain the "container" DefaultConfigurableCacheFactory that created
* this manager and which this manager is bound to.
*
* @return the DefaultConfigurableCacheFactory that created this manager
*/
public DefaultConfigurableCacheFactory getCacheFactory()
{
return DefaultConfigurableCacheFactory.this;
}
// ----- data fields ----------------------------------------------
/**
* The CacheInfo for the enclosed backing maps.
*/
protected CacheInfo m_info;
/**
* The xml configuration for the enclosed backing maps.
*/
protected XmlElement m_xmlScheme;
/**
* The BackingMapManagerContext to pass to the enclosed backing maps.
*/
protected BackingMapManagerContext m_context;
/**
* The ClassLoader to pass to the enclosed backing maps.
*/
protected ClassLoader m_loader;
}
// ----- data fields and constants --------------------------------------
/**
* The default configuration file name.
*/
public static final String FILE_CFG_CACHE = "coherence-cache-config.xml";
/**
* The name of the replaceable parameter representing the cache name.
*/
public static final String CACHE_NAME = "{cache-name}";
/**
* The name of the replaceable parameter representing the class loader.
*/
public static final String CLASS_LOADER = "{class-loader}";
/**
* The name of the replaceable parameter representing the backing map
* manager context.
*/
public static final String MGR_CONTEXT = "{manager-context}";
/**
* The name of the replaceable parameter representing a scheme reference.
*/
public static final String SCHEME_REF = "{scheme-ref}";
/**
* The name of the replaceable parameter representing a cache reference.
*/
public static final String CACHE_REF = "{cache-ref}";
/**
* The unknown scheme type.
*/
public static final int SCHEME_UNKNOWN = 0;
/**
* The replicated cache scheme.
*/
public static final int SCHEME_REPLICATED = 1;
/**
* The optimistic cache scheme.
*/
public static final int SCHEME_OPTIMISTIC = 2;
/**
* The distributed cache scheme.
*/
public static final int SCHEME_DISTRIBUTED = 3;
/**
* The near cache scheme.
*/
public static final int SCHEME_NEAR = 4;
/**
* The versioned near cache scheme.
*/
public static final int SCHEME_VERSIONED_NEAR = 5;
/**
* The local cache scheme.
*/
public static final int SCHEME_LOCAL = 6;
/**
* The overflow map scheme.
*/
public static final int SCHEME_OVERFLOW = 7;
/**
* The disk scheme.
*
* @deprecated As of Coherence 3.0, replaced by {@link #SCHEME_EXTERNAL}
* and {@link #SCHEME_EXTERNAL_PAGED}
*/
public static final int SCHEME_DISK = 8;
/**
* The external scheme.
*/
public static final int SCHEME_EXTERNAL = 9;
/**
* The paged-external scheme.
*/
public static final int SCHEME_EXTERNAL_PAGED = 10;
/**
* The custom class scheme.
*/
public static final int SCHEME_CLASS = 11;
/**
* The read write backing map scheme.
*/
public static final int SCHEME_READ_WRITE_BACKING = 12;
/**
* The versioned backing map scheme.
*/
public static final int SCHEME_VERSIONED_BACKING = 13;
/**
* The invocation service scheme.
*/
public static final int SCHEME_INVOCATION = 14;
/**
* The proxy service scheme.
*/
public static final int SCHEME_PROXY = 15;
/**
* The remote cache scheme.
*/
public static final int SCHEME_REMOTE_CACHE = 16;
/**
* The remote invocation scheme.
*/
public static final int SCHEME_REMOTE_INVOCATION = 17;
/**
* The transactional cache scheme.
*/
public static final int SCHEME_TRANSACTIONAL = 18;
/**
* The flash journal cache scheme.
*/
public static final int SCHEME_FLASHJOURNAL = 19;
/**
* The ram journal cache scheme.
*/
public static final int SCHEME_RAMJOURNAL = 20;
/**
* The paged topic scheme.
*/
public static final int SCHEME_PAGED_TOPIC = 21;
/**
* The configuration XML.
*/
private XmlElement m_xmlConfig;
/**
* The class loader used to load the configuration.
*/
private ClassLoader m_loader;
/**
* Store that holds cache references scoped by class loader and optionally,
* if configured, Subject.
*/
protected ScopedCacheReferenceStore m_store = new ScopedCacheReferenceStore();
/**
* Thread local storage for cache context.
*/
private ThreadLocal m_tlo = new ThreadLocal();
/**
* Scope name associated with this cache factory.
*/
private String m_sScopeName;
/**
* A Set of BackingMapManager instances registered by this factory.
*
* Note: we rely on the BackingMapManager classes *not* to override the
* hashCode() and equals() methods.
*/
protected Set m_setManager = new MapSet(new WeakHashMap());
/**
* The {@link ResourceRegistry} for configuration.
*/
protected ResourceRegistry m_registry;
}