All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.tangosol.net.ScopedCacheFactoryBuilder Maven / Gradle / Ivy

There is a newer version: 24.09
Show newest version
/*
 * 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.tangosol.config.expression.ParameterResolver;

import com.tangosol.net.ExtensibleConfigurableCacheFactory.Dependencies;

import com.tangosol.run.xml.XmlElement;
import com.tangosol.run.xml.XmlHelper;

import com.tangosol.util.ClassHelper;
import com.tangosol.util.CopyOnWriteMap;
import com.tangosol.util.LiteMap;
import com.tangosol.util.Resources;

import java.io.IOException;

import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;

import java.security.AccessController;
import java.security.PrivilegedAction;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;

import static com.tangosol.util.Base.ensureClassLoader;
import static com.tangosol.util.Base.ensureRuntimeException;
import static com.tangosol.util.Base.getOriginalException;

/**
 * Implementation of {@link CacheFactoryBuilder} that manages multiple
 * instances of {@link ConfigurableCacheFactory}.  This implementation
 * supports isolation of cache configurations via the following mechanisms:
 * 
    *
  1. It parses the cache configuration file for the {@code } * attribute. If this element exists, this attribute will be set * on the CCF instance. *
  2. The scope name can be explicitly passed to the {@link #instantiateFactory} * method. *
*

* The scope name may be used by the {@link ConfigurableCacheFactory} instance * as a service name prefix. *

* * @author pp 2010.01.20 * * @since Coherence 3.7 */ public class ScopedCacheFactoryBuilder implements CacheFactoryBuilder { // ----- constructors ------------------------------------------------- /** * Default constructor; reads scope resolver configuration from * operational configuration file (tangosol-coherence.xml). */ public ScopedCacheFactoryBuilder() { this(null); } /** * Constructor to provide a custom scope resolver. * * @param resolver scope resolver */ public ScopedCacheFactoryBuilder(ScopeResolver resolver) { f_scopeResolver = resolver == null ? instantiateScopeResolver() : resolver; } // ----- accessors ---------------------------------------------------- /** * Obtain the scope resolver for this builder. * * @return scope resolver */ public ScopeResolver getScopeResolver() { return f_scopeResolver; } // ----- CacheFactoryBuilder interface -------------------------------- /** * {@inheritDoc} */ public ConfigurableCacheFactory getConfigurableCacheFactory(ClassLoader loader) { return getFactory(URI_DEFAULT, loader, null); } /** * {@inheritDoc} */ public ConfigurableCacheFactory getConfigurableCacheFactory(String sConfigURI, ClassLoader loader) { return getFactory(sConfigURI, loader, null); } /** * {@inheritDoc} */ public ConfigurableCacheFactory getConfigurableCacheFactory(String sConfigURI, ClassLoader loader, ParameterResolver resolver) { return getFactory(sConfigURI, loader, resolver); } /** * {@inheritDoc} */ public void setCacheConfiguration(ClassLoader loader, XmlElement xmlConfig) { setCacheConfiguration(URI_DEFAULT, loader, xmlConfig); } /** * {@inheritDoc} */ public synchronized void setCacheConfiguration(String sConfigURI, ClassLoader loader, XmlElement xmlConfig) { loader = ensureClassLoader(loader); Map mapCCF = m_mapByLoader.get(loader); ConfigurableCacheFactory ccf = mapCCF == null ? null : mapCCF.get(sConfigURI); if (ccf != null) { release(ccf); } URL url = resolveURL(resolveURI(sConfigURI), loader); setXmlConfig(loader, url, xmlConfig); } /** * {@inheritDoc} */ public ConfigurableCacheFactory setConfigurableCacheFactory(ConfigurableCacheFactory ccf, String sConfigURI, ClassLoader loader, boolean fReplace) { loader = ensureClassLoader(loader); Map mapCCF = m_mapByLoader.get(loader); ConfigurableCacheFactory ccfOld = mapCCF == null ? null : mapCCF.get(sConfigURI); if (ccfOld != null) { if (!fReplace) { return ccfOld; } release(ccfOld); } mapCCF = ensureConfigCCFMap(loader); mapCCF.put(sConfigURI, ccf); return ccfOld; } /** * {@inheritDoc} */ public synchronized void releaseAll(ClassLoader loader) { Map map = m_mapByLoader.get(ensureClassLoader(loader)); if (map != null) { List list = new ArrayList<>(map.values()); list.forEach(this::release); list.clear(); } m_mapByLoader.remove(ensureClassLoader(loader)); } /** * {@inheritDoc} */ public synchronized void release(ConfigurableCacheFactory factory) { // track ClassLoaders that no longer have any associated factories; // since the iterator for mapByLoader is read only, these ClassLoaders // must be removed via the Map itself (the "front door") Set setLoader = new HashSet(); Map> mapByLoader = m_mapByLoader; for (Map.Entry> entry : mapByLoader.entrySet()) { Map mapCCF = entry.getValue(); for (Iterator iterCCF = mapCCF.values().iterator(); iterCCF.hasNext(); ) { if (factory.equals(iterCCF.next())) { iterCCF.remove(); } } if (mapCCF.isEmpty()) { // this ClassLoader no longer has any associated factories; track for removal setLoader.add(entry.getKey()); } } Map> mapConfigByLoader = m_mapConfigByLoader; mapByLoader.keySet().removeAll(setLoader); mapConfigByLoader.keySet().removeAll(setLoader); } // ----- helper methods ----------------------------------------------- /** * Instantiate the default {@link ScopeResolver}. *

* If the the {@code scope-resolver} element of the {@code cache-factory-builder} * element of the operational configuration has been specified this will be used * to determine the {@link ScopeResolver} implementation to use otherwise the * {@link ScopeResolver#INSTANCE NullImplementation} resolver will be * used. * * @return the default {@link ScopeResolver} */ protected ScopeResolver instantiateScopeResolver() { ScopeResolver scopeResolver = null; XmlElement xmlConfig = CacheFactory.getCacheFactoryBuilderConfig(); XmlElement xmlResolver = xmlConfig.getElement("scope-resolver"); if (xmlResolver != null) { try { scopeResolver = (ScopeResolver) XmlHelper.createInstance( xmlResolver, ScopedCacheFactoryBuilder.class.getClassLoader(), null); } catch (Exception e) { throw ensureRuntimeException(e, "Could not create scope resolver"); } } if (scopeResolver == null) { scopeResolver = ScopeResolver.INSTANCE; } return scopeResolver; } /** * Helper method to return a {@link ConfigurableCacheFactory} instance for the * specified URI and class loader. * * @param sConfigURI the configuration URI to return a {@link ConfigurableCacheFactory} for * @param loader the loader to return a CCF for * * @return a {@link ConfigurableCacheFactory} instance */ protected ConfigurableCacheFactory getFactory(final String sConfigURI, final ClassLoader loader, final ParameterResolver resolver) { return System.getSecurityManager() == null ? getFactoryInternal(sConfigURI, loader, resolver) : AccessController.doPrivileged((PrivilegedAction) () -> getFactoryInternal(sConfigURI, loader, resolver)); } /** * Implementation of {@link #getFactory(String, ClassLoader, ParameterResolver)}. */ private ConfigurableCacheFactory getFactoryInternal(String sConfigURI, ClassLoader loader, ParameterResolver resolver) { ClassLoader loaderSearch = loader = ensureClassLoader(loader); // most likely code path: retrieve existing factory from provided // ClassLoader or its parents; create if it doesn't exist // Note: Returning a CCF associated with the parent's class loader // may disallow loading classes bound to the given class loader; // however this constraint is introduced to accommodate for the EAR / // WAR / GAR use case Map mapCCF; do { mapCCF = m_mapByLoader.get(loaderSearch); } while ((mapCCF == null || !mapCCF.containsKey(sConfigURI)) && (loaderSearch = loaderSearch.getParent()) != null); ConfigurableCacheFactory ccf = mapCCF == null ? null : mapCCF.get(sConfigURI); if (ccf == null) { synchronized (this) { mapCCF = ensureConfigCCFMap(loader); ccf = mapCCF.get(sConfigURI); if (ccf == null) { ccf = buildFactory(sConfigURI, loader, resolver); } mapCCF.put(sConfigURI, ccf); } } return ccf; } /** * Ensure that a map from URI to ConfigurableCacheFactory for the specified * loader exists (creating it if necessary). * * @param loader the class loader to which the map corresponds * * @return a map from URI to ConfigurableCacheFactory */ protected synchronized Map ensureConfigCCFMap(ClassLoader loader) { Map> mapByLoader = m_mapByLoader; Map mapCCF = mapByLoader.get(loader); if (mapCCF == null) { mapCCF = new LiteMap(); mapByLoader.put(loader, mapCCF); } return mapCCF; } /** * Ensure that a map from URL to ConfigurableCacheFactory for the specified * loader exists (creating it if necessary). * * @param loader the class loader to which the map corresponds * * @return a Map from URL to ConfigurableCacheFactory */ protected synchronized Map ensureConfigMap(ClassLoader loader) { Map> mapConfigByLoader = m_mapConfigByLoader; Map mapConfig = mapConfigByLoader.get(loader); if (mapConfig == null) { mapConfig = new HashMap<>(); mapConfigByLoader.put(loader, mapConfig); } return mapConfig; } /** * Return the {@link XmlElement XML config} relating to the provided * ClassLoader and URL, or null. * * @param loader the ClassLoader the XML was registered with * @param url the URL the XML was registered with * * @return the XML config relating to the provided ClassLoader and URL, * or null */ protected XmlElement getXmlConfig(ClassLoader loader, URL url) { Map mapXml = ensureConfigMap(loader); try { return mapXml.get(url.toURI()); } catch (URISyntaxException e) {} return null; } /** * Register the provided {@link XmlElement XML config} with the ClassLoader * and URL. * * @param loader the ClassLoader the XML is to be registered with * @param url the URL the XML is to be registered with * @param xml the XML config to register */ protected void setXmlConfig(ClassLoader loader, URL url, XmlElement xml) { Map mapXml = ensureConfigMap(loader); try { mapXml.put(url.toURI(), xml); } catch (URISyntaxException e) {} } /** * Load the XML configuration from the specified URI. * * @param sConfigURI the configuration URI; must not be null * @param loader class loader to use * * @return the XML configuration, or null if the config could not be loaded */ protected synchronized XmlElement loadConfigFromURI(String sConfigURI, ClassLoader loader) { URL url = resolveURL(sConfigURI, loader); XmlElement xmlConfig = getXmlConfig(loader, url); if (xmlConfig == null) { try { xmlConfig = XmlHelper.loadResource(url, "cache configuration", loader); setXmlConfig(loader, url, xmlConfig); } catch (Exception e) { if (e instanceof RuntimeException) { Throwable eOrig = getOriginalException((RuntimeException) e); if (eOrig instanceof IOException) { StringBuilder sb = new StringBuilder("Could not load cache configuration resource " + url); Throwable cause = eOrig.getCause(); if (cause != null) { sb.append(", Cause:").append(cause.getMessage()); } e = new IOException(sb.toString()); } } throw ensureRuntimeException(e); } } return xmlConfig; } /** * Return the XML configuration used for the construction of a {@link ConfigurableCacheFactory}. * * @return the {@link XmlElement} that contains construction configuration */ protected XmlElement getConfigurableCacheFactoryConfig() { return CacheFactory.getConfigurableCacheFactoryConfig(); } /** * Construct and configure a {@link ConfigurableCacheFactory} for the specified * cache config URI and {@link ClassLoader}. * * @param sConfigURI the URI to the cache configuration * @param loader the {@link ClassLoader} associated with the factory * * @return a ConfigurableCacheFactory for the specified XML configuration */ protected ConfigurableCacheFactory buildFactory(String sConfigURI, ClassLoader loader) { return buildFactory(sConfigURI, loader, null); } /** * Construct and configure a {@link ConfigurableCacheFactory} for the specified * cache config URI and {@link ClassLoader}. * * @param sConfigURI the URI to the cache configuration * @param loader the {@link ClassLoader} associated with the factory * @param paramResolver an optional {@link ParameterResolver} to use to resolve configuration parameters * * @return a ConfigurableCacheFactory for the specified XML configuration */ protected ConfigurableCacheFactory buildFactory(String sConfigURI, ClassLoader loader, ParameterResolver paramResolver) { ScopeResolver resolver = getScopeResolver(); String sDescopedURI = resolver == null ? sConfigURI : resolver.resolveURI(sConfigURI); String sResolvedURI = resolveURI(sDescopedURI); XmlElement xmlConfig = loadConfigFromURI(sResolvedURI, loader); String sScope = resolver == null ? null : resolver.resolveScopeName(sConfigURI, loader, null); if (sScope != null && !Coherence.DEFAULT_SCOPE.equals(sScope) && !resolver.useScopeInConfig()) { XmlElement xmlDefaults = xmlConfig.getElement("defaults"); if (xmlDefaults != null) { XmlElement xmlScope = xmlDefaults.getElement("scope-name"); if (xmlScope != null) { xmlScope.setString(sScope); } } } return instantiateFactory(loader, xmlConfig, getConfigurableCacheFactoryConfig(), null, sScope, paramResolver); } /** * Create a new instance of {@link ConfigurableCacheFactory} based on a given * {@link ClassLoader} and cache configuration XML. * * @param loader the {@link ClassLoader} used to instantiate the {@link ConfigurableCacheFactory} * @param xmlConfig the {@link XmlElement} containing the cache configuration * @param xmlFactory the {@link XmlElement} containing the factory definition * @param sPofConfigURI the POF configuration URI * @param sScopeName an optional scope name * * @return the {@link ConfigurableCacheFactory} created */ protected ConfigurableCacheFactory instantiateFactory(ClassLoader loader, XmlElement xmlConfig, XmlElement xmlFactory, String sPofConfigURI, String sScopeName, ParameterResolver resolver) { // temporarily allow user to select ECCF via a property String sClass = xmlFactory.getSafeElement("class-name").getString(); try { if (sClass.equals(ExtensibleConfigurableCacheFactory.class.getName())) { Dependencies dependencies = ExtensibleConfigurableCacheFactory.DependenciesHelper. newInstance(xmlConfig, loader, sPofConfigURI, sScopeName, resolver); ExtensibleConfigurableCacheFactory eccf = new ExtensibleConfigurableCacheFactory(dependencies); eccf.setConfigClassLoader(loader); return eccf; } else if (sClass.equals(DefaultConfigurableCacheFactory.class.getName())) { DefaultConfigurableCacheFactory dccf = new DefaultConfigurableCacheFactory(xmlConfig); dccf.setConfigClassLoader(loader); sScopeName = xmlConfig.getSafeElement("scope-name").getString(sScopeName); if (sScopeName != null && sScopeName.length() > 0) { dccf.setScopeName(sScopeName); } return dccf; } else { ConfigurableCacheFactory ccf = (ConfigurableCacheFactory) XmlHelper.createInstance(xmlFactory, loader, null); Method methSetConfig = ClassHelper.findMethod(ccf.getClass(), "setConfig", new Class[]{XmlElement.class}, false); if (methSetConfig != null) { ClassHelper.invoke(ccf, "setConfig", new Object[]{xmlConfig}); } return ccf; } } catch (Exception e) { throw ensureRuntimeException(e, "Failed to instantiate a class from the xmlConfiguration " + xmlConfig); } } /** * Resolve the URI that identifies the cache configuration. The URI provided * may be a normal URL or Resource, or it may be a "special" default URI that * is used when a specific cache configuration file is not indicated (for * example, if the user requests a factory via {@link CacheFactory#getConfigurableCacheFactory()}. * If the "default" URI is requested, the URI is resolved to the default * cache configuration name indicated in the operational configuration file; * otherwise the provided URI is returned. * * @param sConfigURI the passed in URI * * @return the resolved URI * * @see #URI_DEFAULT */ protected String resolveURI(String sConfigURI) { if (sConfigURI.equals(URI_DEFAULT)) { // by convention, the "default URI" is the first parameter // passed to DCCF per the operational configuration // (typically "coherence-cache-config.xml") XmlElement xmlFactory = CacheFactory.getConfigurableCacheFactoryConfig(); Object [] aoParam = XmlHelper.parseInitParams(xmlFactory.getSafeElement("init-params")); return aoParam.length > 0 && aoParam[0] instanceof String ? (String) aoParam[0] : ExtensibleConfigurableCacheFactory.FILE_CFG_CACHE; } else { return sConfigURI; } } /** * Resolve the URL based on the provided configuration URI. The resolution * consists of locating the URI as a resource or a file and the creation * of a corresponding URL. If the URI cannot be located, a "placeholder" * file URL will be created. * * @param sConfigURI the configuration URI to make a URL out of * @param loader the {@link ClassLoader} to use * * @return a {@link URL} for the resource */ protected URL resolveURL(String sConfigURI, ClassLoader loader) { URL url = Resources.findFileOrResourceOrDefault(sConfigURI, loader); if (url == null) { // if the URL does not exist, we will create a file URL so that // we can associate a cache configuration with a URI that doesn't // exist as a file or resource try { url = new URL((sConfigURI.contains(":") ? "" : "file://") + sConfigURI); } catch (MalformedURLException e) { throw ensureRuntimeException(e, "The configuration URI contains illegal characters for a URL " + sConfigURI); } } return url; } // ----- constants and data members ----------------------------------- /** * Scope resolver used to resolve scope name upon CCF construction. */ protected final ScopeResolver f_scopeResolver; /** * Mapping used to associate class loaders with the cache factories that are * configured on them. The map is (weakly) keyed by class loader instances * and holds a maps of URI to ConfigurableCacheFactory as a values * (e.g. Map<ClassLoader, Map<URI, ConfigurableCacheFactory>>). */ protected Map> m_mapByLoader = new CopyOnWriteMap(WeakHashMap.class); /** * Mapping used to associate class loaders with specific configuration elements. * The map is (weakly) keyed by class loader instances and holds a map * of URL to XmlElement as values. */ protected Map> m_mapConfigByLoader = new CopyOnWriteMap(WeakHashMap.class); }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy