CHAIN-86 - Make CatalogFactory an interface

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/chain/trunk@1508999 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/apps/cookbook-examples/src/main/java/org/apache/commons/chain2/cookbook/mailreader/MailReaderServlet.java b/apps/cookbook-examples/src/main/java/org/apache/commons/chain2/cookbook/mailreader/MailReaderServlet.java
index 73d8abb..90aa443 100644
--- a/apps/cookbook-examples/src/main/java/org/apache/commons/chain2/cookbook/mailreader/MailReaderServlet.java
+++ b/apps/cookbook-examples/src/main/java/org/apache/commons/chain2/cookbook/mailreader/MailReaderServlet.java
@@ -19,6 +19,7 @@
 import org.apache.commons.chain2.Catalog;
 import org.apache.commons.chain2.CatalogFactory;
 import org.apache.commons.chain2.Command;
+import org.apache.commons.chain2.impl.CatalogFactoryBase;
 
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
@@ -44,7 +45,7 @@
         context.setLogger(response.getWriter());
 
         CatalogFactory<String, Object, MailReader> catalogFactory =
-                CatalogFactory.getInstance();
+                CatalogFactoryBase.getInstance();
 
         Catalog<String, Object, MailReader> catalog =
                 catalogFactory.getCatalog();
diff --git a/apps/example2/src/main/java/org/apache/commons/chain2/apps/example/ExampleServlet.java b/apps/example2/src/main/java/org/apache/commons/chain2/apps/example/ExampleServlet.java
index 3e219b1..1f69395 100644
--- a/apps/example2/src/main/java/org/apache/commons/chain2/apps/example/ExampleServlet.java
+++ b/apps/example2/src/main/java/org/apache/commons/chain2/apps/example/ExampleServlet.java
@@ -19,6 +19,7 @@
 import org.apache.commons.chain2.Catalog;
 import org.apache.commons.chain2.CatalogFactory;
 import org.apache.commons.chain2.Command;
+import org.apache.commons.chain2.impl.CatalogFactoryBase;
 import org.apache.commons.chain2.web.servlet.ServletWebContext;
 import org.apache.commons.chain2.web.servlet.ServletWebContextBase;
 import org.apache.commons.logging.Log;
@@ -28,6 +29,7 @@
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+
 import java.io.IOException;
 
 /**
@@ -74,7 +76,7 @@
         throws IOException {
 
         CatalogFactory<String, Object, ServletWebContext<String, Object>> factory =
-                CatalogFactory.getInstance();
+                CatalogFactoryBase.getInstance();
         Catalog<String, Object, ServletWebContext<String, Object>> catalog =
                 factory.getCatalog(servletName);
 
diff --git a/base/src/main/java/org/apache/commons/chain2/CatalogFactory.java b/base/src/main/java/org/apache/commons/chain2/CatalogFactory.java
deleted file mode 100644
index 3ef870d..0000000
--- a/base/src/main/java/org/apache/commons/chain2/CatalogFactory.java
+++ /dev/null
@@ -1,243 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.commons.chain2;
-
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-
-import org.apache.commons.chain2.impl.CatalogFactoryBase;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-/**
- * <p>A {@link CatalogFactory} is a class used to store and retrieve
- * {@link Catalog}s.  The factory allows for a default {@link Catalog}
- * as well as {@link Catalog}s stored with a name key.  Follows the
- * Factory pattern (see GoF).</p>
- *
- * <p>The base <code>CatalogFactory</code> implementation also implements
- * a resolution mechanism which allows lookup of a command based on a single
- * String which encodes both the catalog and command names.</p>
- *
- * @param <K> the type of keys maintained by the context associated with this command
- * @param <V> the type of mapped values
- * @param <C> Type of the context associated with this command
- *
- * @version $Id$
- */
-public abstract class CatalogFactory<K, V, C extends Map<K, V>> {
-
-    /**
-     * <p>Values passed to the <code>getCommand(String)</code> method should
-     * use this as the delimiter between the "catalog" name and the "command"
-     * name.</p>
-     */
-    public static final String DELIMITER = ":";
-
-    // --------------------------------------------------------- Public Methods
-
-    /**
-     * <p>Gets the default instance of Catalog associated with the factory
-     * (if any); otherwise, return <code>null</code>.</p>
-     *
-     * @return the default Catalog instance
-     */
-    public abstract Catalog<K, V, C> getCatalog();
-
-    /**
-     * <p>Sets the default instance of Catalog associated with the factory.</p>
-     *
-     * @param catalog the default Catalog instance
-     */
-    public abstract void setCatalog(Catalog<K, V, C> catalog);
-
-    /**
-     * <p>Retrieves a Catalog instance by name (if any); otherwise
-     * return <code>null</code>.</p>
-     *
-     * @param name the name of the Catalog to retrieve
-     * @return the specified Catalog
-     */
-    public abstract Catalog<K, V, C> getCatalog(String name);
-
-    /**
-     * <p>Adds a named instance of Catalog to the factory (for subsequent
-     * retrieval later).</p>
-     *
-     * @param name the name of the Catalog to add
-     * @param catalog the Catalog to add
-     */
-    public abstract void addCatalog(String name, Catalog<K, V, C> catalog);
-
-    /**
-     * <p>Return an <code>Iterator</code> over the set of named
-     * {@link Catalog}s known to this {@link CatalogFactory}.
-     * If there are no known catalogs, an empty Iterator is returned.</p>
-     * @return An Iterator of the names of the Catalogs known by this factory.
-     */
-    public abstract Iterator<String> getNames();
-
-    /**
-     * <p>Return a <code>Command</code> based on the given commandID.</p>
-     *
-     * <p>At this time, the structure of commandID is relatively simple:  if the
-     * commandID contains a DELIMITER, treat the segment of the commandID
-     * up to (but not including) the DELIMITER as the name of a catalog, and the
-     * segment following the DELIMITER as a command name within that catalog.
-     * If the commandID contains no DELIMITER, treat the commandID as the name
-     * of a command in the default catalog.</p>
-     *
-     * <p>To preserve the possibility of future extensions to this lookup
-     * mechanism, the DELIMITER string should be considered reserved, and
-     * should not be used in command names.  commandID values which contain
-     * more than one DELIMITER will cause an
-     * <code>IllegalArgumentException</code> to be thrown.</p>
-     *
-     * @param <CMD> the expected {@link Command} type to be returned
-     * @param commandID the identifier of the command to return
-     * @return the command located with commandID, or <code>null</code>
-     *  if either the command name or the catalog name cannot be resolved
-     * @throws IllegalArgumentException if the commandID contains more than
-     *  one DELIMITER
-     *
-     * @since Chain 1.1
-     */
-    public <CMD extends Command<K, V, C>> CMD getCommand(String commandID) {
-        String commandName = commandID;
-        String catalogName = null;
-        Catalog<K, V, C> catalog;
-
-        if (commandID != null) {
-            int splitPos = commandID.indexOf(DELIMITER);
-            if (splitPos != -1) {
-                catalogName = commandID.substring(0, splitPos);
-                commandName = commandID.substring(splitPos + DELIMITER.length());
-                if (commandName.contains(DELIMITER)) {
-                    throw new IllegalArgumentException("commandID [" +
-                                                       commandID +
-                                                       "] has too many delimiters (reserved for future use)");
-                }
-            }
-        }
-
-        if (catalogName != null) {
-            catalog = this.getCatalog(catalogName);
-            if (catalog == null) {
-                Log log = LogFactory.getLog(CatalogFactory.class);
-                log.warn("No catalog found for name: " + catalogName + ".");
-                return null;
-            }
-        } else {
-            catalog = this.getCatalog();
-            if (catalog == null) {
-                Log log = LogFactory.getLog(CatalogFactory.class);
-                log.warn("No default catalog found.");
-                return null;
-            }
-        }
-
-        return catalog.<CMD>getCommand(commandName);
-    }
-
-    // ------------------------------------------------------- Static Variables
-
-    /**
-     * <p>The set of registered {@link CatalogFactory} instances,
-     * keyed by the relevant class loader.</p>
-     */
-    private static final Map<ClassLoader, CatalogFactory<?, ?, ? extends Map<?, ?>>> factories =
-            new HashMap<ClassLoader, CatalogFactory<?, ?, ? extends Map<?, ?>>>();
-
-    // -------------------------------------------------------- Static Methods
-
-    /**
-     * <p>Return the singleton {@link CatalogFactory} instance
-     * for the relevant <code>ClassLoader</code>.  For applications
-     * that use a thread context class loader (such as web applications
-     * running inside a servet container), this will return a separate
-     * instance for each application, even if this class is loaded from
-     * a shared parent class loader.</p>
-     *
-     * @param <K> Context key type
-     * @param <V> Context value type
-     * @param <C> Type of the context associated with this command
-     * @return the per-application singleton instance of {@link CatalogFactory}
-     */
-    public static <K, V, C extends Map<K, V>> CatalogFactory<K, V, C> getInstance() {
-        CatalogFactory<?, ?, ? extends Map<?, ?>> factory;
-        ClassLoader cl = getClassLoader();
-        synchronized (factories) {
-            factory = factories.get(cl);
-            if (factory == null) {
-                factory = new CatalogFactoryBase<K, V, C>();
-                factories.put(cl, factory);
-            }
-        }
-
-        /* This should always convert cleanly because we are using the
-         * base most generic for definition. */
-        @SuppressWarnings("unchecked")
-        CatalogFactory<K, V, C> result = (CatalogFactory<K, V, C>) factory;
-
-        return result;
-    }
-
-    /**
-     * <p>Clear all references to registered catalogs, as well as to the
-     * relevant class loader.  This method should be called, for example,
-     * when a web application utilizing this class is removed from
-     * service, to allow for garbage collection.</p>
-     */
-    public static void clear() {
-        synchronized (factories) {
-            factories.remove(getClassLoader());
-        }
-    }
-
-    // ------------------------------------------------------- Private Methods
-
-    /**
-     * <p>Return the relevant <code>ClassLoader</code> to use as a Map key
-     * for this request.  If there is a thread context class loader, return
-     * that; otherwise, return the class loader that loaded this class.</p>
-     */
-    private static ClassLoader getClassLoader() {
-        ClassLoader cl = Thread.currentThread().getContextClassLoader();
-        if (cl == null) {
-            cl = CatalogFactory.class.getClassLoader();
-        }
-        return cl;
-    }
-
-    /**
-     * Check to see if we have an implementation of a valid configuration
-     * parsing class loaded at runtime. If not, we throw an
-     * IllegalStateException.
-     */
-    public static void checkForValidConfigurationModule() {
-        try {
-            ClassLoader cl = getClassLoader();
-            cl.loadClass("org.apache.commons.chain2.config.ConfigParser");
-        } catch (ClassNotFoundException e) {
-            String msg = "Couldn't not find a configuration implementation. " +
-                    "Load a chain configuration module such as xml-configuration " +
-                    "into the classpath and try again.";
-            throw new IllegalStateException(msg, e);
-        }
-    }
-}
diff --git a/base/src/main/java/org/apache/commons/chain2/base/LookupCommand.java b/base/src/main/java/org/apache/commons/chain2/base/LookupCommand.java
index 717a75e..d9d1190 100644
--- a/base/src/main/java/org/apache/commons/chain2/base/LookupCommand.java
+++ b/base/src/main/java/org/apache/commons/chain2/base/LookupCommand.java
@@ -22,6 +22,7 @@
 import org.apache.commons.chain2.Context;
 import org.apache.commons.chain2.Filter;
 import org.apache.commons.chain2.Processing;
+import org.apache.commons.chain2.impl.CatalogFactoryBase;
 
 import java.util.Map;
 
@@ -58,7 +59,7 @@
      * @since Chain 1.1
      */
     public LookupCommand() {
-        this(CatalogFactory.<K, V, C>getInstance());
+        this(CatalogFactoryBase.<K, V, C>getInstance());
     }
 
     /**
@@ -78,7 +79,7 @@
     private CatalogFactory<K, V, C> catalogFactory = null;
 
     /**
-     * <p>Set the {@link CatalogFactory} from which lookups will be
+     * <p>Set the {@link CatalogFactoryBase} from which lookups will be
      * performed.</p>
      *
      * @param catalogFactory The Catalog Factory.
@@ -90,7 +91,7 @@
     }
 
     /**
-     * Return the {@link CatalogFactory} from which lookups will be performed.
+     * Return the {@link CatalogFactoryBase} from which lookups will be performed.
      * @return The Catalog factory.
      *
      * @since Chain 1.1
@@ -323,7 +324,7 @@
     protected Catalog<K, V, C> getCatalog(C context) {
         CatalogFactory<K, V, C> lookupFactory = this.catalogFactory;
         if (lookupFactory == null) {
-            lookupFactory = CatalogFactory.getInstance();
+            lookupFactory = CatalogFactoryBase.getInstance();
         }
 
         String catalogName = getCatalogName();
diff --git a/base/src/main/java/org/apache/commons/chain2/impl/CatalogFactoryBase.java b/base/src/main/java/org/apache/commons/chain2/impl/CatalogFactoryBase.java
index fd34683..b846464 100644
--- a/base/src/main/java/org/apache/commons/chain2/impl/CatalogFactoryBase.java
+++ b/base/src/main/java/org/apache/commons/chain2/impl/CatalogFactoryBase.java
@@ -17,12 +17,16 @@
 
 package org.apache.commons.chain2.impl;
 
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
 import org.apache.commons.chain2.Catalog;
 import org.apache.commons.chain2.CatalogFactory;
+import org.apache.commons.chain2.Command;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 
 /**
  * <p>A simple implementation of {@link CatalogFactory}.</p>
@@ -32,7 +36,7 @@
  * @param <C> Type of the context associated with this command
  * @version $Id$
  */
-public class CatalogFactoryBase<K, V, C extends Map<K, V>> extends CatalogFactory<K, V, C> {
+public class CatalogFactoryBase<K, V, C extends Map<K, V>> implements CatalogFactory<K, V, C> {
 
     // ----------------------------------------------------------- Constructors
 
@@ -46,7 +50,7 @@
     // ----------------------------------------------------- Instance Variables
 
     /**
-     * <p>The default {@link Catalog} for this {@link CatalogFactory}.</p>
+     * <p>The default {@link Catalog} for this {@link CatalogFactoryBase}.</p>
      */
     private Catalog<K, V, C> catalog = null;
 
@@ -104,7 +108,7 @@
 
     /**
      * <p>Return an <code>Iterator</code> over the set of named
-     * {@link Catalog}s known to this {@link CatalogFactory}.
+     * {@link Catalog}s known to this {@link CatalogFactoryBase}.
      * If there are no known catalogs, an empty Iterator is returned.</p>
      *
      * @return An Iterator of the names of the Catalogs known by this factory.
@@ -114,4 +118,129 @@
         return catalogs.keySet().iterator();
     }
 
+
+    public <CMD extends Command<K, V, C>> CMD getCommand(String commandID) {
+        String commandName = commandID;
+        String catalogName = null;
+        Catalog<K, V, C> catalog;
+
+        if (commandID != null) {
+            int splitPos = commandID.indexOf(DELIMITER);
+            if (splitPos != -1) {
+                catalogName = commandID.substring(0, splitPos);
+                commandName = commandID.substring(splitPos + DELIMITER.length());
+                if (commandName.contains(DELIMITER)) {
+                    throw new IllegalArgumentException("commandID [" +
+                                                       commandID +
+                                                       "] has too many delimiters (reserved for future use)");
+                }
+            }
+        }
+
+        if (catalogName != null) {
+            catalog = this.getCatalog(catalogName);
+            if (catalog == null) {
+                Log log = LogFactory.getLog(CatalogFactoryBase.class);
+                log.warn("No catalog found for name: " + catalogName + ".");
+                return null;
+            }
+        } else {
+            catalog = this.getCatalog();
+            if (catalog == null) {
+                Log log = LogFactory.getLog(CatalogFactoryBase.class);
+                log.warn("No default catalog found.");
+                return null;
+            }
+        }
+
+        return catalog.<CMD>getCommand(commandName);
+    }
+
+    // ------------------------------------------------------- Static Variables
+
+    /**
+     * <p>The set of registered {@link CatalogFactoryBase} instances,
+     * keyed by the relevant class loader.</p>
+     */
+    private static final Map<ClassLoader, CatalogFactoryBase<?, ?, ? extends Map<?, ?>>> factories =
+            new HashMap<ClassLoader, CatalogFactoryBase<?, ?, ? extends Map<?, ?>>>();
+
+    // -------------------------------------------------------- Static Methods
+
+    /**
+     * <p>Return the singleton {@link CatalogFactoryBase} instance
+     * for the relevant <code>ClassLoader</code>.  For applications
+     * that use a thread context class loader (such as web applications
+     * running inside a servet container), this will return a separate
+     * instance for each application, even if this class is loaded from
+     * a shared parent class loader.</p>
+     *
+     * @param <K> Context key type
+     * @param <V> Context value type
+     * @param <C> Type of the context associated with this command
+     * @return the per-application singleton instance of {@link CatalogFactoryBase}
+     */
+    public static <K, V, C extends Map<K, V>> CatalogFactory<K, V, C> getInstance() {
+        CatalogFactoryBase<?, ?, ? extends Map<?, ?>> factory;
+        ClassLoader cl = getClassLoader();
+        synchronized (factories) {
+            factory = factories.get(cl);
+            if (factory == null) {
+                factory = new CatalogFactoryBase<K, V, C>();
+                factories.put(cl, factory);
+            }
+        }
+
+        /* This should always convert cleanly because we are using the
+         * base most generic for definition. */
+        @SuppressWarnings("unchecked")
+        CatalogFactory<K, V, C> result = (CatalogFactory<K, V, C>) factory;
+
+        return result;
+    }
+
+    /**
+     * <p>Clear all references to registered catalogs, as well as to the
+     * relevant class loader.  This method should be called, for example,
+     * when a web application utilizing this class is removed from
+     * service, to allow for garbage collection.</p>
+     */
+    public static void clear() {
+        synchronized (factories) {
+            factories.remove(getClassLoader());
+        }
+    }
+
+    // ------------------------------------------------------- Private Methods
+
+    /**
+     * <p>Return the relevant <code>ClassLoader</code> to use as a Map key
+     * for this request.  If there is a thread context class loader, return
+     * that; otherwise, return the class loader that loaded this class.</p>
+     */
+    private static ClassLoader getClassLoader() {
+        ClassLoader cl = Thread.currentThread().getContextClassLoader();
+        if (cl == null) {
+            cl = getClassLoader();
+        }
+        return cl;
+    }
+
+    /**
+     * Check to see if we have an implementation of a valid configuration
+     * parsing class loaded at runtime. If not, we throw an
+     * IllegalStateException.
+     */
+    public static void checkForValidConfigurationModule() {
+        try {
+            ClassLoader cl = getClassLoader();
+            cl.loadClass("org.apache.commons.chain2.config.ConfigParser");
+        } catch (ClassNotFoundException e) {
+            String msg = "Couldn't not find a configuration implementation. " +
+                    "Load a chain configuration module such as xml-configuration " +
+                    "into the classpath and try again.";
+            throw new IllegalStateException(msg, e);
+        }
+    }
+
 }
diff --git a/base/src/test/java/org/apache/commons/chain2/impl/CatalogFactoryBaseTestCase.java b/base/src/test/java/org/apache/commons/chain2/impl/CatalogFactoryBaseTestCase.java
index 8a9f913..eda1296 100644
--- a/base/src/test/java/org/apache/commons/chain2/impl/CatalogFactoryBaseTestCase.java
+++ b/base/src/test/java/org/apache/commons/chain2/impl/CatalogFactoryBaseTestCase.java
@@ -47,7 +47,7 @@
 
 
     /**
-     * <p>The {@link CatalogFactory} instance under test.</p>
+     * <p>The {@link CatalogFactoryBase} instance under test.</p>
      */
     protected CatalogFactory<String, Object, Context<String, Object>> factory = null;
 
@@ -60,8 +60,8 @@
      */
     @Before
     public void setUp() {
-        CatalogFactory.clear();
-        factory = CatalogFactory.getInstance();
+        CatalogFactoryBase.clear();
+        factory = CatalogFactoryBase.getInstance();
     }
 
     /**
@@ -70,7 +70,7 @@
     @After
     public void tearDown() {
         factory = null;
-        CatalogFactory.clear();
+        CatalogFactoryBase.clear();
     }
 
 
@@ -78,7 +78,7 @@
 
 
     /**
-     * <p>Test a pristine instance of {@link CatalogFactory}.</p>
+     * <p>Test a pristine instance of {@link CatalogFactoryBase}.</p>
      */
     @Test
     public void testPristine() {
@@ -120,8 +120,8 @@
         factory.addCatalog("foo", new CatalogBase<String, Object, Context<String, Object>>());
         assertEquals(1, getCatalogCount());
         assertTrue(!(catalog == factory.getCatalog("foo")));
-        CatalogFactory.clear();
-        factory = CatalogFactory.getInstance();
+        CatalogFactoryBase.clear();
+        factory = CatalogFactoryBase.getInstance();
         assertEquals(0, getCatalogCount());
 
     }
@@ -180,7 +180,7 @@
 
     /**
      * <p>Return the number of {@link Catalog}s defined in our
-     * {@link CatalogFactory}.</p>
+     * {@link CatalogFactoryBase}.</p>
      */
     private int getCatalogCount() {
 
diff --git a/configuration/xml/src/main/java/org/apache/commons/chain2/config/xml/ConfigCatalogRule.java b/configuration/xml/src/main/java/org/apache/commons/chain2/config/xml/ConfigCatalogRule.java
index 7654e4b..6f26fd7 100644
--- a/configuration/xml/src/main/java/org/apache/commons/chain2/config/xml/ConfigCatalogRule.java
+++ b/configuration/xml/src/main/java/org/apache/commons/chain2/config/xml/ConfigCatalogRule.java
@@ -18,6 +18,7 @@
 
 import org.apache.commons.chain2.Catalog;
 import org.apache.commons.chain2.CatalogFactory;
+import org.apache.commons.chain2.impl.CatalogFactoryBase;
 import org.apache.commons.digester3.Rule;
 import org.xml.sax.Attributes;
 
@@ -26,7 +27,7 @@
 /**
  * <p>Digester rule that will cause the top-most element on the Digester
  * stack (if it is a {@link org.apache.commons.chain2.Catalog} to be registered with the
- * {@link org.apache.commons.chain2.CatalogFactory} instance for our application.  If the attribute
+ * {@link org.apache.commons.chain2.impl.CatalogFactoryBase} instance for our application.  If the attribute
  * specified to our constructor has a value, that will be used as the name
  * under which to register this {@link org.apache.commons.chain2.Catalog}.  Otherwise, this will
  * become the default {@link org.apache.commons.chain2.Catalog} for this application.</p>
@@ -85,7 +86,7 @@
     public void begin(String namespace, String name, Attributes attributes) throws Exception {
         // Retrieve any current Catalog with the specified name
         Catalog<Object, Object, Map<Object, Object>> catalog;
-        CatalogFactory<Object, Object, Map<Object, Object>> factory = CatalogFactory.getInstance();
+        CatalogFactory<Object, Object, Map<Object, Object>> factory = CatalogFactoryBase.getInstance();
         String nameValue = attributes.getValue(nameAttribute);
         if (nameValue == null) {
             catalog = factory.getCatalog();
diff --git a/configuration/xml/src/main/java/org/apache/commons/chain2/config/xml/ConfigRuleSet.java b/configuration/xml/src/main/java/org/apache/commons/chain2/config/xml/ConfigRuleSet.java
index fa2a8b5..ba4a009 100644
--- a/configuration/xml/src/main/java/org/apache/commons/chain2/config/xml/ConfigRuleSet.java
+++ b/configuration/xml/src/main/java/org/apache/commons/chain2/config/xml/ConfigRuleSet.java
@@ -35,7 +35,7 @@
  * <li><strong>catalogElement</strong> -- Name of the XML element representing
  *     the addition of a {@link org.apache.commons.chain2.Catalog}.
  *     Any such catalog that is created will be registered with the
- *     {@link org.apache.commons.chain2.CatalogFactory} instance for our
+ *     {@link org.apache.commons.chain2.impl.CatalogFactoryBase} instance for our
  *     application, under the name specified by the <code>nameAttribute</code>
  *     attribute (if present), or as the default {@link org.apache.commons.chain2.Catalog}.
  *     If not specified, the default value is <code>catalog</code>.</li>
diff --git a/configuration/xml/src/main/java/org/apache/commons/chain2/config/xml/XmlConfigParser.java b/configuration/xml/src/main/java/org/apache/commons/chain2/config/xml/XmlConfigParser.java
index f9f3a0c..7190618 100644
--- a/configuration/xml/src/main/java/org/apache/commons/chain2/config/xml/XmlConfigParser.java
+++ b/configuration/xml/src/main/java/org/apache/commons/chain2/config/xml/XmlConfigParser.java
@@ -19,6 +19,7 @@
 import org.apache.commons.chain2.CatalogFactory;
 import org.apache.commons.chain2.config.ChainConfigurationException;
 import org.apache.commons.chain2.config.ConfigParser;
+import org.apache.commons.chain2.impl.CatalogFactoryBase;
 import org.apache.commons.digester3.Digester;
 import org.apache.commons.digester3.RuleSet;
 
@@ -161,7 +162,7 @@
             throw new ChainConfigurationException(msg, e);
         }
         // FIXME get rid of singleton pattern and create a new instance here
-        return CatalogFactory.getInstance();
+        return CatalogFactoryBase.getInstance();
     }
 
 }
diff --git a/configuration/xml/src/test/java/org/apache/commons/chain2/config/xml/XmlConfigParserTestCase.java b/configuration/xml/src/test/java/org/apache/commons/chain2/config/xml/XmlConfigParserTestCase.java
index 8cf9e3f..b9c5131 100644
--- a/configuration/xml/src/test/java/org/apache/commons/chain2/config/xml/XmlConfigParserTestCase.java
+++ b/configuration/xml/src/test/java/org/apache/commons/chain2/config/xml/XmlConfigParserTestCase.java
@@ -36,6 +36,7 @@
 import org.apache.commons.chain2.Processing;
 import org.apache.commons.chain2.testutils.AddingCommand;
 import org.apache.commons.chain2.impl.CatalogBase;
+import org.apache.commons.chain2.impl.CatalogFactoryBase;
 import org.apache.commons.chain2.impl.ChainBase;
 import org.apache.commons.chain2.impl.ContextBase;
 import org.apache.commons.chain2.testutils.DelegatingCommand;
@@ -88,7 +89,7 @@
     }
 
     private void init() {
-        CatalogFactory.clear();
+        CatalogFactoryBase.clear();
         catalog = new CatalogBase<String, Object, Context<String, Object>>();
         context = new ContextBase();
         parser = new XmlConfigParser();
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index aa9ed7c..83ef2b8 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -41,6 +41,9 @@
 
   <body>
     <release version="2.0" description="Major release">
+      <action issue="CHAIN-86" dev="britter" type="update" due-to="Jonas Sprenger">
+          Make CatalogFactory an interface
+      </action>
       <action issue="CHAIN-108" dev="britter" type="update">
           Build fails with Java 6
       </action>
diff --git a/web/src/main/java/org/apache/commons/chain2/web/ChainListener.java b/web/src/main/java/org/apache/commons/chain2/web/ChainListener.java
index 87d0ac6..2a269d5 100644
--- a/web/src/main/java/org/apache/commons/chain2/web/ChainListener.java
+++ b/web/src/main/java/org/apache/commons/chain2/web/ChainListener.java
@@ -29,8 +29,8 @@
 import org.apache.commons.chain2.Catalog;
 import org.apache.commons.chain2.CatalogFactory;
 import org.apache.commons.chain2.config.xml.XmlConfigParser;
-import org.apache.commons.chain2.config.xml.XmlConfigParser;
 import org.apache.commons.chain2.impl.CatalogBase;
+import org.apache.commons.chain2.impl.CatalogFactoryBase;
 import org.apache.commons.chain2.web.servlet.ServletWebContext;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -56,7 +56,7 @@
  *     If not specified, it is expected that parsed resources will
  *     contain <code>&lt;catalog&gt;</code> elements (which will
  *     cause registration of the created {@link Catalog}s into
- *     the {@link CatalogFactory} for this application, and no
+ *     the {@link CatalogFactoryBase} for this application, and no
  *     servlet context attribute will be created.
  *     <strong>NOTE</strong> - This parameter is deprecated.</p>
  * <li><strong>org.apache.commons.chain2.RULE_SET</strong> -
@@ -82,7 +82,7 @@
  *
  * <p>If no attribute key is specified, on the other hand, parsed configuration
  * resources are expected to contain <code>&lt;catalog&gt;</code> elements,
- * and the catalogs will be registered with the {@link CatalogFactory}
+ * and the catalogs will be registered with the {@link CatalogFactoryBase}
  * for this web application.</p>
  *
  * <p>This class requires Servlet 2.3 or later.  If you are running on
@@ -96,7 +96,7 @@
  */
 public class ChainListener implements ServletContextListener {
     {
-        CatalogFactory.checkForValidConfigurationModule();
+        CatalogFactoryBase.checkForValidConfigurationModule();
     }
 
     // ------------------------------------------------------ Manifest Constants
@@ -145,7 +145,7 @@
         if (attr != null) {
             context.removeAttribute(attr);
         }
-        CatalogFactory.clear();
+        CatalogFactoryBase.clear();
     }
 
     /**
diff --git a/web/src/main/java/org/apache/commons/chain2/web/ChainServlet.java b/web/src/main/java/org/apache/commons/chain2/web/ChainServlet.java
index 91f1f32..d55611d 100644
--- a/web/src/main/java/org/apache/commons/chain2/web/ChainServlet.java
+++ b/web/src/main/java/org/apache/commons/chain2/web/ChainServlet.java
@@ -26,9 +26,9 @@
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.commons.chain2.Catalog;
-import org.apache.commons.chain2.CatalogFactory;
 import org.apache.commons.chain2.config.xml.XmlConfigParser;
 import org.apache.commons.chain2.impl.CatalogBase;
+import org.apache.commons.chain2.impl.CatalogFactoryBase;
 import org.apache.commons.chain2.web.servlet.ServletWebContext;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -53,7 +53,7 @@
  *     If not specified, it is expected that parsed resources will
  *     contain <code>&lt;catalog&gt;</code> elements (which will
  *     cause registration of the created {@link Catalog}s into
- *     the {@link CatalogFactory} for this application, and no
+ *     the {@link CatalogFactoryBase} for this application, and no
  *     servet context attribute will be created.
  *     <strong>NOTE</strong> - This parameter is deprecated.</p>
  * <li><strong>org.apache.commons.chain2.RULE_SET</strong> -
@@ -77,7 +77,7 @@
  *
  * <p>If no attribute key is specified, on the other hand, parsed configuration
  * resources are expected to contain <code>&lt;catalog&gt;</code> elements,
- * and the catalogs will be registered with the {@link CatalogFactory}
+ * and the catalogs will be registered with the {@link CatalogFactoryBase}
  * for this web application.</p>
  *
  * <p>This class runs on Servlet 2.2 or later.  If you are running on a
@@ -92,7 +92,7 @@
  */
 public class ChainServlet extends HttpServlet {
     {
-        CatalogFactory.checkForValidConfigurationModule();
+        CatalogFactoryBase.checkForValidConfigurationModule();
     }
 
     // ------------------------------------------------------ Manifest Constants
@@ -145,7 +145,7 @@
         if (attr != null) {
             context.removeAttribute(attr);
         }
-        CatalogFactory.clear();
+        CatalogFactoryBase.clear();
     }
 
     /**
diff --git a/web/src/main/java/org/apache/commons/chain2/web/servlet/ChainProcessor.java b/web/src/main/java/org/apache/commons/chain2/web/servlet/ChainProcessor.java
index dc9aaa3..78e8387 100644
--- a/web/src/main/java/org/apache/commons/chain2/web/servlet/ChainProcessor.java
+++ b/web/src/main/java/org/apache/commons/chain2/web/servlet/ChainProcessor.java
@@ -17,14 +17,15 @@
 package org.apache.commons.chain2.web.servlet;
 
 import org.apache.commons.chain2.Catalog;
-import org.apache.commons.chain2.CatalogFactory;
 import org.apache.commons.chain2.Command;
+import org.apache.commons.chain2.impl.CatalogFactoryBase;
 import org.apache.commons.chain2.web.ChainServlet;
 
 import javax.servlet.ServletContext;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+
 import java.io.IOException;
 
 /**
@@ -98,13 +99,13 @@
      * is stored.  This value is also used as the name of the
      * context attribute under which the catalog is exposed to commands.
      * If not specified, we will look up commands in the appropriate
-     * {@link Catalog} retrieved from our {@link CatalogFactory}.</p>
+     * {@link Catalog} retrieved from our {@link CatalogFactoryBase}.</p>
      */
     private String attribute = null;
 
     /**
      * <p>The name of the {@link Catalog} to retrieve from the
-     * {@link CatalogFactory} for this application, or <code>null</code>
+     * {@link CatalogFactoryBase} for this application, or <code>null</code>
      * to select the default {@link Catalog}.</p>
      */
     private String catalog = null;
@@ -184,10 +185,10 @@
                 throw new IllegalArgumentException(msg);
             }
         } else if (catalog != null) {
-            theCatalog = CatalogFactory.<String, Object, ServletWebContext<String, Object>>getInstance()
+            theCatalog = CatalogFactoryBase.<String, Object, ServletWebContext<String, Object>>getInstance()
                     .getCatalog(catalog);
         } else {
-            theCatalog = CatalogFactory.<String, Object, ServletWebContext<String, Object>>getInstance()
+            theCatalog = CatalogFactoryBase.<String, Object, ServletWebContext<String, Object>>getInstance()
                     .getCatalog();
         }
         if (attribute == null) {