Author: bstansberry(a)jboss.com
Date: 2007-10-25 01:59:01 -0400 (Thu, 25 Oct 2007)
New Revision: 4684
Added:
core/trunk/src/main/java/org/jboss/cache/registry/
core/trunk/src/main/java/org/jboss/cache/registry/CacheConfigsXmlParser.java
core/trunk/src/main/java/org/jboss/cache/registry/CacheRegistry.java
core/trunk/src/main/java/org/jboss/cache/registry/CacheRegistryImpl.java
core/trunk/src/main/java/org/jboss/cache/registry/ConfigurationRegistry.java
core/trunk/src/main/java/org/jboss/cache/registry/XmlParsingConfigurationRegistry.java
Log:
[JBCACHE-1156] Registry for caches and configurations
Added: core/trunk/src/main/java/org/jboss/cache/registry/CacheConfigsXmlParser.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/registry/CacheConfigsXmlParser.java
(rev 0)
+++
core/trunk/src/main/java/org/jboss/cache/registry/CacheConfigsXmlParser.java 2007-10-25
05:59:01 UTC (rev 4684)
@@ -0,0 +1,122 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2006, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+
+package org.jboss.cache.registry;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.jboss.cache.config.Configuration;
+import org.jboss.cache.config.ConfigurationException;
+import org.jboss.cache.factories.XmlConfigurationParser;
+import org.jboss.cache.xml.XmlHelper;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+public class CacheConfigsXmlParser
+ {
+ /** Name of the root element in a cache configs XML document*/
+ public static final String DOCUMENT_ROOT = "cache-configs";
+ /**
+ * Name of the element that represents an individual cache configuration
+ * in a cache configs XML document.
+ */
+ public static final String CONFIG_ROOT = "cache-config";
+ /**
+ * Name of the attribute in a {@link #CONFIG_ROOT cache-config} element that
specifies
+ * the name of the configuration.
+ */
+ public static final String CONFIG_NAME = "name";
+
+ private static final Log log = LogFactory.getLog(CacheConfigsXmlParser.class);
+
+ private final XmlConfigurationParser parser = new XmlConfigurationParser();
+
+ public Map<String, Configuration> parseConfigs(String fileName) throws
CloneNotSupportedException
+ {
+ InputStream is = getAsInputStreamFromClassLoader(fileName);
+ if (is == null)
+ {
+ if (log.isDebugEnabled())
+ log.debug("Unable to find configuration file " + fileName + "
in classpath; searching for this file on the filesystem instead.");
+ try
+ {
+ is = new FileInputStream(fileName);
+ }
+ catch (FileNotFoundException e)
+ {
+ throw new ConfigurationException("Unable to find config file " +
fileName + " either in classpath or on the filesystem!", e);
+ }
+ }
+
+ return parseConfigs(is);
+ }
+
+ public Map<String, Configuration> parseConfigs(InputStream stream) throws
CloneNotSupportedException
+ {
+ // loop through all elements in XML.
+ Element root = XmlHelper.getDocumentRoot(stream);
+ NodeList list = root.getElementsByTagName(CONFIG_ROOT);
+ if (list == null || list.getLength() == 0)
+ throw new ConfigurationException("Can't find " + CONFIG_ROOT +
" tag");
+
+ Map<String, Configuration> result = new HashMap<String,
Configuration>();
+
+ for (int i = 0; i < list.getLength(); i++)
+ {
+ Node node = list.item(i);
+ if (node.getNodeType() != Node.ELEMENT_NODE)
+ {
+ continue;
+ }
+
+ Element element = (Element) node;
+ String name = element.getAttribute(CONFIG_NAME);
+ if (name == null || name.trim().length() == 0)
+ throw new ConfigurationException("Element " + element + "
has no name attribute");
+
+ Configuration c = parser.parseConfiguration(element);
+ // Prove that we can successfully clone it
+ c = c.clone();
+ result.put(name.trim(), c);
+ }
+
+ return result;
+ }
+
+ protected InputStream getAsInputStreamFromClassLoader(String filename)
+ {
+ InputStream is =
Thread.currentThread().getContextClassLoader().getResourceAsStream(filename);
+ if (is == null)
+ {
+ // check system class loader
+ is = getClass().getClassLoader().getResourceAsStream(filename);
+ }
+ return is;
+ }
+ }
\ No newline at end of file
Property changes on:
core/trunk/src/main/java/org/jboss/cache/registry/CacheConfigsXmlParser.java
___________________________________________________________________
Name: svn:executable
+ *
Added: core/trunk/src/main/java/org/jboss/cache/registry/CacheRegistry.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/registry/CacheRegistry.java
(rev 0)
+++ core/trunk/src/main/java/org/jboss/cache/registry/CacheRegistry.java 2007-10-25
05:59:01 UTC (rev 4684)
@@ -0,0 +1,95 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Distributable under LGPL license.
+ * See terms of license at
gnu.org.
+ */
+
+package org.jboss.cache.registry;
+
+import java.util.Set;
+
+import org.jboss.cache.Cache;
+import org.jboss.cache.config.Configuration;
+import org.jgroups.ChannelFactory;
+import org.jgroups.JChannelFactory;
+
+/**
+ * Factory and registry for JBoss Cache instances configured using
+ * named configurations.
+ *
+ * @author <a href="brian.stansberry(a)jboss.com">Brian
Stansberry</a>
+ * @version $Revision: 1 $
+ */
+public interface CacheRegistry {
+
+ /**
+ * Gets the names of all the configurations of which this object
+ * is aware.
+ *
+ * @return a set of names. Will not be <code>null</code>.
+ */
+ Set<String> getConfigurationNames();
+
+ /**
+ * Gets the name of all caches that are registered, i.e. that can be
+ * by a call to {@link #getCache(String, boolean) getCache(name, false)}.
+ *
+ * @return a set of names. Will not be <code>null</code>.
+ */
+ Set<String> getCacheNames();
+
+ /**
+ * Gets the JGroups <code>ChannelFactory</code> that will be injected
+ * into any {@link Configuration} that has a
+ * {@link Configuration#getMultiplexerStack() multiplexer stack}
+ * configured.
+ *
+ * @return
+ */
+ ChannelFactory getChannelFactory();
+
+ /**
+ * Get a cache configured according to the given configuration name,
+ * optionally instantiating the cache if it hasn't already been
+ * instantiated.
+ * <p>
+ * The caller is free to invoke the {@link Cache#create()} and
+ * {@link Cache#start()} lifecycle methods on the returned cache, but
+ * the @link Cache#stop()} and {@link Cache#destroy()} methods should not
+ * be invoked, since it is quite possible other users are still using the
+ * cache. Use {@link #releaseCache(String)} to notify this
+ * registry that the caller is no longer using a cache; let the registry
+ * control stopping and destroying the cache.
+ * </p>
+ * <p>
+ * If invoking this method leads to the instantiation of the cache,
+ * <code>create()</code> and <code>start()</code> will not be
invoked
+ * on the cache before it is returned.
+ * </p>
+ *
+ * @param configName the name of the configuration
+ * @param create should the cache be instantiated if it
+ * hasn't already been?
+ * @return the cache, or <code>null</code> if
+ * <code>create</code> is false and the cache
hasn't
+ * been created previously.
+ *
+ * @throws IllegalArgumentException if this object is unaware of
+ * <code>configName</code>; i.e. the
set
+ * returned from {@link #getConfigurationNames()}
+ * does not include
<code>configName</code>
+ * @throws Exception if there is a problem instantiating the cache
+ */
+ Cache<Object, Object> getCache(String configName, boolean create) throws
Exception;
+
+ /**
+ * Notifies the registry that the caller is no longer using the given
+ * cache. The registry may perform cleanup operations, such as
+ * stopping and destroying the cache.
+ *
+ * @param configName
+ */
+ void releaseCache(String configName);
+
+}
\ No newline at end of file
Property changes on: core/trunk/src/main/java/org/jboss/cache/registry/CacheRegistry.java
___________________________________________________________________
Name: svn:executable
+ *
Added: core/trunk/src/main/java/org/jboss/cache/registry/CacheRegistryImpl.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/registry/CacheRegistryImpl.java
(rev 0)
+++ core/trunk/src/main/java/org/jboss/cache/registry/CacheRegistryImpl.java 2007-10-25
05:59:01 UTC (rev 4684)
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 2007, Red Hat Middleware, LLC. All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, v. 2.1. This program is distributed in the
+ * hope that it will be useful, but WITHOUT A WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details. You should have received a
+ * copy of the GNU Lesser General Public License, v.2.1 along with this
+ * distribution; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Red Hat Author(s): Brian Stansberry
+ */
+
+package org.jboss.cache.registry;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.jboss.cache.Cache;
+import org.jboss.cache.CacheStatus;
+import org.jboss.cache.DefaultCacheFactory;
+import org.jboss.cache.config.Configuration;
+import org.jgroups.ChannelFactory;
+
+/**
+ * Basic implementation of {@link CacheRegistry}.
+ *
+ * @author <a href="brian.stansberry(a)jboss.com">Brian
Stansberry</a>
+ * @version $Revision: 1 $
+ */
+public class CacheRegistryImpl implements CacheRegistry
+{
+ private ConfigurationRegistry configRegistry;
+
+ private boolean configRegistryInjected;
+
+ private Map<String, Cache<Object, Object>> caches = new HashMap<String,
Cache<Object, Object>>();
+
+ private Map<String, Integer> checkouts = new HashMap<String, Integer>();
+
+ private ChannelFactory channelFactory;
+
+ private boolean started;
+
+ /**
+ * Create a new CacheRegistryImpl.
+ */
+ public CacheRegistryImpl()
+ {
+ }
+
+ /**
+ * Create a new CacheRegistryImpl using the provided ConfigurationRegistry
+ * and ChannelFactory.
+ */
+ public CacheRegistryImpl(ConfigurationRegistry configRegistry, ChannelFactory
factory)
+ {
+ this.configRegistry = configRegistry;
+ this.configRegistryInjected = true;
+ this.channelFactory = factory;
+ }
+
+ /**
+ * Create a new CacheRegistryImpl using the provided ChannelFactory and
+ * using the provided file name to create an
+ * {@link XmlParsingConfigurationRegistry}.
+ */
+ public CacheRegistryImpl(String configFileName, ChannelFactory factory)
+ {
+ configRegistry = new XmlParsingConfigurationRegistry(configFileName);
+ this.channelFactory = factory;
+ }
+
+ // ---------------------------------------------------------- CacheRegistry
+
+ public ChannelFactory getChannelFactory()
+ {
+ return channelFactory;
+ }
+
+ @SuppressWarnings("unchecked")
+ public Set<String> getConfigurationNames()
+ {
+ synchronized (caches)
+ {
+ Set<String> configNames = configRegistry == null ? new
HashSet<String>()
+ :
configRegistry.getConfigurationNames();
+ configNames.addAll(getCacheNames());
+ return configNames;
+ }
+ }
+
+ public Set<String> getCacheNames()
+ {
+ synchronized (caches)
+ {
+ return new HashSet<String>(caches.keySet());
+ }
+ }
+
+ public Cache<Object, Object> getCache(String configName, boolean create) throws
Exception
+ {
+ Cache<Object, Object> cache = null;
+ synchronized (caches)
+ {
+ cache = (Cache<Object, Object>) caches.get(configName);
+ if (cache == null && create)
+ {
+ Configuration config = configRegistry.getConfiguration(configName);
+ if (channelFactory != null && config.getMultiplexerStack() != null)
{
+ config.getRuntimeConfig().setMuxChannelFactory(channelFactory);
+ }
+ cache = DefaultCacheFactory.getInstance().createCache(config, false);
+ registerCache(cache, configName);
+ }
+ else if (cache != null)
+ {
+ incrementCheckout(configName);
+ }
+ }
+
+ return cache;
+ }
+
+ public void releaseCache(String configName)
+ {
+ synchronized (caches)
+ {
+ if (!caches.containsKey(configName))
+ throw new IllegalStateException(configName + " not registered");
+ if (decrementCheckout(configName) == 0)
+ {
+ Cache<Object, Object> cache = caches.remove(configName);
+ destroyCache(cache);
+ }
+ }
+ }
+
+ // ----------------------------------------------------------------- Public
+
+ public ConfigurationRegistry getConfigurationRegistry()
+ {
+ return configRegistry;
+ }
+
+ public void setConfigurationRegistry(ConfigurationRegistry configRegistry)
+ {
+ this.configRegistry = configRegistry;
+ this.configRegistryInjected = true;
+ }
+
+ public void setChannelFactory(ChannelFactory channelFactory)
+ {
+ this.channelFactory = channelFactory;
+ }
+
+ public void registerCache(Cache<Object, Object> cache, String configName)
+ {
+ synchronized (caches)
+ {
+ if (caches.containsKey(configName))
+ throw new IllegalStateException(configName + " already
registered");
+ caches.put(configName, cache);
+ incrementCheckout(configName);
+ }
+ }
+
+ public void start() throws Exception
+ {
+ if (!started)
+ {
+ if (configRegistry == null)
+ throw new IllegalStateException("Must configure a ConfigurationRegistry
before calling start()");
+ if (channelFactory == null)
+ throw new IllegalStateException("Must provide a ChannelFactory before
calling start()");
+
+ if (!configRegistryInjected)
+ {
+ ((XmlParsingConfigurationRegistry) configRegistry).start();
+ }
+
+ started = true;
+ }
+ }
+
+ public void stop()
+ {
+ if (started)
+ {
+ synchronized (caches)
+ {
+ for (Iterator<Map.Entry<String, Cache<Object, Object>>> it
= caches.entrySet().iterator(); it.hasNext();)
+ {
+ Map.Entry<String, Cache<Object, Object>> entry = it.next();
+ destroyCache(entry.getValue());
+ it.remove();
+ }
+ caches.clear();
+ checkouts.clear();
+ }
+
+ if (!configRegistryInjected)
+ {
+ ((XmlParsingConfigurationRegistry) configRegistry).stop();
+ }
+
+ started = false;
+ }
+ }
+
+ // ---------------------------------------------------------------- Private
+
+ private int incrementCheckout(String configName)
+ {
+ synchronized (checkouts)
+ {
+ Integer count = (Integer) checkouts.get(configName);
+ if (count == null)
+ count = new Integer(0);
+ Integer newVal = new Integer(count.intValue() + 1);
+ checkouts.put(configName, newVal);
+ return newVal.intValue();
+ }
+ }
+
+ private int decrementCheckout(String configName)
+ {
+ synchronized (checkouts)
+ {
+ Integer count = (Integer) checkouts.get(configName);
+ if (count == null || count.intValue() < 1)
+ throw new IllegalStateException("invalid count of " + count +
" for " + configName);
+
+ Integer newVal = new Integer(count.intValue() - 1);
+ checkouts.put(configName, newVal);
+ return newVal.intValue();
+ }
+ }
+
+ private void destroyCache(Cache<Object, Object> cache)
+ {
+ if (cache.getCacheStatus() == CacheStatus.STARTED)
+ {
+ cache.stop();
+ }
+ if (cache.getCacheStatus() != CacheStatus.DESTROYED &&
cache.getCacheStatus() != CacheStatus.INSTANTIATED)
+ {
+ cache.destroy();
+ }
+ }
+}
Property changes on:
core/trunk/src/main/java/org/jboss/cache/registry/CacheRegistryImpl.java
___________________________________________________________________
Name: svn:executable
+ *
Added: core/trunk/src/main/java/org/jboss/cache/registry/ConfigurationRegistry.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/registry/ConfigurationRegistry.java
(rev 0)
+++
core/trunk/src/main/java/org/jboss/cache/registry/ConfigurationRegistry.java 2007-10-25
05:59:01 UTC (rev 4684)
@@ -0,0 +1,67 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Distributable under LGPL license.
+ * See terms of license at
gnu.org.
+ */
+package org.jboss.cache.registry;
+
+import java.util.Set;
+
+import org.jboss.cache.config.Configuration;
+
+/**
+ * A registry for {@link Configuration}s.
+ *
+ * @author Brian Stansberry
+ * @version $Revision$
+ */
+public interface ConfigurationRegistry
+{
+ /**
+ * Gets a {@link Configuration#clone() clone} of the {@link Configuration}
+ * registered under the given name.
+ * <p>
+ * The returned object is a clone of the internally held configuration,
+ * so any changes made to it by the caller will not affect the internal
+ * state of this registry.
+ *
+ * @param configName the name of the configuration
+ * @return a <code>Configuration</code>. Will not be
<code>null</code>.
+ * @throws IllegalArgumentException if no configuration is registered
+ * under <code>configName</code>
+ */
+ Configuration getConfiguration(String configName) throws Exception;
+
+ /**
+ * Register the given configuration under the given name.
+ * <p>
+ * The configuration will be cloned before being stored internally,
+ * so the copy under the control of the registry will not be affected
+ * by any external changes.
+ *
+ * @param configName the name of the configuration
+ * @param config the configuration
+ *
+ * @throws CloneNotSupportedException
+ * @throws IllegalStateException if a configuration is already registered
+ * under <code>configName</code>
+ */
+ void registerConfiguration(String configName, Configuration config) throws
CloneNotSupportedException;
+
+ /**
+ * Unregisters the named configuration.
+ *
+ * @param configName the name of the configuration
+ * @throws IllegalStateException if no configuration is registered
+ * under <code>configName</code>
+ */
+ void unregisterConfiguration(String configName);
+
+ /**
+ * Gets the names of all registered configurations.
+ *
+ * @return
+ */
+ Set<String> getConfigurationNames();
+}
\ No newline at end of file
Property changes on:
core/trunk/src/main/java/org/jboss/cache/registry/ConfigurationRegistry.java
___________________________________________________________________
Name: svn:executable
+ *
Added:
core/trunk/src/main/java/org/jboss/cache/registry/XmlParsingConfigurationRegistry.java
===================================================================
---
core/trunk/src/main/java/org/jboss/cache/registry/XmlParsingConfigurationRegistry.java
(rev 0)
+++
core/trunk/src/main/java/org/jboss/cache/registry/XmlParsingConfigurationRegistry.java 2007-10-25
05:59:01 UTC (rev 4684)
@@ -0,0 +1,108 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Distributable under LGPL license.
+ * See terms of license at
gnu.org.
+ */
+
+package org.jboss.cache.registry;
+
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Set;
+
+import org.jboss.cache.config.Configuration;
+
+/**
+ * {@link ConfigurationRegistry} that obtains its initial set of configurations
+ * by parsing an XML document.
+ *
+ * @author <a href="brian.stansberry(a)jboss.com">Brian
Stansberry</a>
+ * @version $Revision: 1 $
+ */
+public class XmlParsingConfigurationRegistry implements ConfigurationRegistry
+{
+ private CacheConfigsXmlParser parser;
+ private String configResource;
+ private Map<String, Configuration> configs = new Hashtable<String,
Configuration>();
+ private boolean started;
+
+ public XmlParsingConfigurationRegistry(String configResource)
+ {
+ parser = new CacheConfigsXmlParser();
+ this.configResource = configResource;
+ }
+
+ public void start() throws Exception
+ {
+ if (!started)
+ {
+ configs.putAll(parser.parseConfigs(configResource));
+ started = true;
+ }
+ }
+
+ public void stop()
+ {
+ if (started)
+ {
+ synchronized (configs)
+ {
+ configs.clear();
+ }
+ started = false;
+ }
+ }
+
+ public String getConfigResource()
+ {
+ return configResource;
+ }
+
+ public Set<String> getConfigurationNames()
+ {
+ return new HashSet<String>(configs.keySet());
+ }
+
+ public void registerConfiguration(String configName, Configuration config)
+ throws CloneNotSupportedException
+ {
+ synchronized (configs) {
+ if (configs.containsKey(configName))
+ throw new IllegalStateException(configName + " already
registered");
+ configs.put(configName, config.clone());
+ }
+ }
+
+ public void unregisterConfiguration(String configName)
+ {
+ synchronized (configs) {
+ if (configs.remove(configName) == null)
+ throw new IllegalStateException(configName + " not
registered");
+ }
+ }
+
+ public Configuration getConfiguration(String configName)
+ {
+ Configuration config = null;
+ synchronized (configs)
+ {
+ config = configs.get(configName);
+ }
+
+ if (config == null)
+ throw new IllegalArgumentException("unknown config " + configName);
+
+ // Don't hand out a ref to our master copy
+ try
+ {
+ return config.clone();
+ }
+ catch (CloneNotSupportedException e)
+ {
+ // This should not happen, as we already cloned the config
+ throw new RuntimeException("Could not clone configuration " +
configName, e);
+ }
+ }
+}
Property changes on:
core/trunk/src/main/java/org/jboss/cache/registry/XmlParsingConfigurationRegistry.java
___________________________________________________________________
Name: svn:executable
+ *