From 69c67006faa65ab0b41f1bb915cc706bbb976ab7 Mon Sep 17 00:00:00 2001
From: "Endi S. Dewata" <edewata@redhat.com>
Date: Tue, 24 Sep 2013 10:08:48 -0400
Subject: [PATCH] Refactored TPS configuration resource.

The REST interface for TPS configuration has been modified to provide access
to TPS general configuration as originally designed. The configuration database
has been modified such that it can be reused by other configuration resources.

Ticket #652
---
 .../netscape/certsrv/tps/config/ConfigClient.java  |  12 +-
 .../netscape/certsrv/tps/config/ConfigData.java    |  44 ++---
 .../certsrv/tps/config/ConfigResource.java         |  13 +-
 .../netscape/cmstools/tps/config/ConfigCLI.java    |  18 +-
 .../cmstools/tps/config/ConfigFindCLI.java         |  86 ----------
 .../cmstools/tps/config/ConfigModifyCLI.java       |  15 +-
 .../cmstools/tps/config/ConfigShowCLI.java         |  13 +-
 base/tps-tomcat/src/CMakeLists.txt                 |   9 +-
 .../org/dogtagpki/server/tps/TPSApplication.java   |   6 +-
 .../src/org/dogtagpki/server/tps/TPSSubsystem.java |  24 ++-
 .../server/tps/config/ConfigDatabase.java          | 191 +++++++++++++++++++++
 .../dogtagpki/server/tps/config/ConfigRecord.java  | 117 +++++++++++++
 .../dogtagpki/server/tps/config/ConfigService.java | 140 +++------------
 13 files changed, 399 insertions(+), 289 deletions(-)
 delete mode 100644 base/java-tools/src/com/netscape/cmstools/tps/config/ConfigFindCLI.java
 create mode 100644 base/tps-tomcat/src/org/dogtagpki/server/tps/config/ConfigDatabase.java
 create mode 100644 base/tps-tomcat/src/org/dogtagpki/server/tps/config/ConfigRecord.java

diff --git a/base/common/src/com/netscape/certsrv/tps/config/ConfigClient.java b/base/common/src/com/netscape/certsrv/tps/config/ConfigClient.java
index c3876bf83c98b2275c471b618353e72f4debbce3..9c707e494896182610b391d4bea4981121192f9e 100644
--- a/base/common/src/com/netscape/certsrv/tps/config/ConfigClient.java
+++ b/base/common/src/com/netscape/certsrv/tps/config/ConfigClient.java
@@ -44,17 +44,13 @@ public class ConfigClient extends Client {
         resource = createProxy(ConfigResource.class);
     }
 
-    public ConfigCollection findConfigs() {
-        return resource.findConfigs();
+    public ConfigData getConfig() {
+        return resource.getConfig();
     }
 
-    public ConfigData getConfig(String configID) {
-        return resource.getConfig(configID);
-    }
-
-    public ConfigData updateConfig(String configID, ConfigData configData) {
+    public ConfigData updateConfig(ConfigData configData) {
         @SuppressWarnings("unchecked")
-        ClientResponse<ConfigData> response = (ClientResponse<ConfigData>)resource.updateConfig(configID, configData);
+        ClientResponse<ConfigData> response = (ClientResponse<ConfigData>)resource.updateConfig(configData);
         return client.getEntity(response);
     }
 }
diff --git a/base/common/src/com/netscape/certsrv/tps/config/ConfigData.java b/base/common/src/com/netscape/certsrv/tps/config/ConfigData.java
index 1b3688f5e735968dceb82bbd635396343692178b..2a36f3fa87bea873a3ad436955c5553bce6a8719 100644
--- a/base/common/src/com/netscape/certsrv/tps/config/ConfigData.java
+++ b/base/common/src/com/netscape/certsrv/tps/config/ConfigData.java
@@ -57,27 +57,18 @@ public class ConfigData {
         }
     }
 
-    String id;
-    String displayName;
+    String status;
     Map<String, String> properties = new LinkedHashMap<String, String>();
+
     Link link;
 
-    @XmlAttribute(name="id")
-    public String getID() {
-        return id;
+    @XmlElement(name="Status")
+    public String getStatus() {
+        return status;
     }
 
-    public void setID(String id) {
-        this.id = id;
-    }
-
-    @XmlElement(name="DisplayName")
-    public String getDisplayName() {
-        return displayName;
-    }
-
-    public void setDisplayName(String displayName) {
-        this.displayName = displayName;
+    public void setStatus(String status) {
+        this.status = status;
     }
 
     @XmlElement(name="Properties")
@@ -156,10 +147,9 @@ public class ConfigData {
     public int hashCode() {
         final int prime = 31;
         int result = 1;
-        result = prime * result + ((displayName == null) ? 0 : displayName.hashCode());
-        result = prime * result + ((id == null) ? 0 : id.hashCode());
         result = prime * result + ((link == null) ? 0 : link.hashCode());
         result = prime * result + ((properties == null) ? 0 : properties.hashCode());
+        result = prime * result + ((status == null) ? 0 : status.hashCode());
         return result;
     }
 
@@ -172,16 +162,6 @@ public class ConfigData {
         if (getClass() != obj.getClass())
             return false;
         ConfigData other = (ConfigData) obj;
-        if (displayName == null) {
-            if (other.displayName != null)
-                return false;
-        } else if (!displayName.equals(other.displayName))
-            return false;
-        if (id == null) {
-            if (other.id != null)
-                return false;
-        } else if (!id.equals(other.id))
-            return false;
         if (link == null) {
             if (other.link != null)
                 return false;
@@ -192,6 +172,11 @@ public class ConfigData {
                 return false;
         } else if (!properties.equals(other.properties))
             return false;
+        if (status == null) {
+            if (other.status != null)
+                return false;
+        } else if (!status.equals(other.status))
+            return false;
         return true;
     }
 
@@ -217,8 +202,7 @@ public class ConfigData {
     public static void main(String args[]) throws Exception {
 
         ConfigData before = new ConfigData();
-        before.setID("test");
-        before.setDisplayName("Test Config");
+        before.setStatus("ENABLED");
         before.setProperty("param1", "value1");
         before.setProperty("param2", "value2");
 
diff --git a/base/common/src/com/netscape/certsrv/tps/config/ConfigResource.java b/base/common/src/com/netscape/certsrv/tps/config/ConfigResource.java
index a15730a4bf2f08f37a6cd132f6ecff24880b5602..e1076a48e4a0c549b43bb6d550268535e6b37795 100644
--- a/base/common/src/com/netscape/certsrv/tps/config/ConfigResource.java
+++ b/base/common/src/com/netscape/certsrv/tps/config/ConfigResource.java
@@ -21,7 +21,6 @@ import javax.ws.rs.Consumes;
 import javax.ws.rs.GET;
 import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
-import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
@@ -32,22 +31,16 @@ import org.jboss.resteasy.annotations.ClientResponseType;
 /**
  * @author Endi S. Dewata
  */
-@Path("configs")
+@Path("config")
 public interface ConfigResource {
 
     @GET
     @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
-    public ConfigCollection findConfigs();
-
-    @GET
-    @Path("{configID}")
-    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
-    public ConfigData getConfig(@PathParam("configID") String configID);
+    public ConfigData getConfig();
 
     @PUT
-    @Path("{configID}")
     @ClientResponseType(entityType=ConfigData.class)
     @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
     @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
-    public Response updateConfig(@PathParam("configID") String configID, ConfigData configData);
+    public Response updateConfig(ConfigData configData);
 }
diff --git a/base/java-tools/src/com/netscape/cmstools/tps/config/ConfigCLI.java b/base/java-tools/src/com/netscape/cmstools/tps/config/ConfigCLI.java
index 9d913517e3732bc717a8bbfa5b33918180787028..01ef0b299bcf59e3c650a593746df5422ba32518 100644
--- a/base/java-tools/src/com/netscape/cmstools/tps/config/ConfigCLI.java
+++ b/base/java-tools/src/com/netscape/cmstools/tps/config/ConfigCLI.java
@@ -20,6 +20,7 @@ package com.netscape.cmstools.tps.config;
 
 import java.io.IOException;
 import java.util.Arrays;
+import java.util.Map;
 
 import org.jboss.resteasy.plugins.providers.atom.Link;
 
@@ -37,7 +38,6 @@ public class ConfigCLI extends CLI {
     public ConfigCLI(CLI parent) {
         super("config", "Configuration management commands", parent);
 
-        addModule(new ConfigFindCLI(this));
         addModule(new ConfigModifyCLI(this));
         addModule(new ConfigShowCLI(this));
     }
@@ -71,17 +71,15 @@ public class ConfigCLI extends CLI {
         }
     }
 
-    public static void printConfigData(ConfigData configData, boolean showProperties) throws IOException {
+    public static void printConfigData(ConfigData configData) throws IOException {
 
-        System.out.println("  Config ID: " + configData.getID());
-        System.out.println("  Display Name: " + configData.getDisplayName());
+        if (configData.getStatus() != null) System.out.println("  Status: " + configData.getStatus());
 
-        if (showProperties) {
-            System.out.println("  Properties:");
-            for (String name : configData.getPropertyNames()) {
-                String value = configData.getProperty(name);
-                System.out.println("    " + name + ": " + value);
-            }
+        System.out.println("  Properties:");
+        Map<String, String> properties = configData.getProperties();
+        for (String name : properties.keySet()) {
+            String value = properties.get(name);
+            System.out.println("    " + name + ": " + value);
         }
 
         Link link = configData.getLink();
diff --git a/base/java-tools/src/com/netscape/cmstools/tps/config/ConfigFindCLI.java b/base/java-tools/src/com/netscape/cmstools/tps/config/ConfigFindCLI.java
deleted file mode 100644
index c7ab18738ea42f07279191c1ba9b3af692a83425..0000000000000000000000000000000000000000
--- a/base/java-tools/src/com/netscape/cmstools/tps/config/ConfigFindCLI.java
+++ /dev/null
@@ -1,86 +0,0 @@
-// --- BEGIN COPYRIGHT BLOCK ---
-// This program is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation; version 2 of the License.
-//
-// This program 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 General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License along
-// with this program; if not, write to the Free Software Foundation, Inc.,
-// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-//
-// (C) 2013 Red Hat, Inc.
-// All rights reserved.
-// --- END COPYRIGHT BLOCK ---
-
-package com.netscape.cmstools.tps.config;
-
-import java.util.Collection;
-
-import org.apache.commons.cli.CommandLine;
-
-import com.netscape.certsrv.tps.config.ConfigCollection;
-import com.netscape.certsrv.tps.config.ConfigData;
-import com.netscape.cmstools.cli.CLI;
-import com.netscape.cmstools.cli.MainCLI;
-
-/**
- * @author Endi S. Dewata
- */
-public class ConfigFindCLI extends CLI {
-
-    public ConfigCLI configCLI;
-
-    public ConfigFindCLI(ConfigCLI configCLI) {
-        super("find", "Find configurations", configCLI);
-        this.configCLI = configCLI;
-    }
-
-    public void printHelp() {
-        formatter.printHelp(getFullName(), options);
-    }
-
-    public void execute(String[] args) throws Exception {
-
-        CommandLine cmd = null;
-
-        try {
-            cmd = parser.parse(options, args);
-
-        } catch (Exception e) {
-            System.err.println("Error: " + e.getMessage());
-            printHelp();
-            System.exit(1);
-        }
-
-        String[] cmdArgs = cmd.getArgs();
-
-        if (cmdArgs.length != 0) {
-            printHelp();
-            System.exit(1);
-        }
-
-        ConfigCollection result = configCLI.configClient.findConfigs();
-
-        Collection<ConfigData> entries = result.getConfigs();
-
-        MainCLI.printMessage(entries.size() + " entries matched");
-        boolean first = true;
-
-        for (ConfigData configData : entries) {
-
-            if (first) {
-                first = false;
-            } else {
-                System.out.println();
-            }
-
-            ConfigCLI.printConfigData(configData, false);
-        }
-
-        MainCLI.printMessage("Number of entries returned " + entries.size());
-    }
-}
diff --git a/base/java-tools/src/com/netscape/cmstools/tps/config/ConfigModifyCLI.java b/base/java-tools/src/com/netscape/cmstools/tps/config/ConfigModifyCLI.java
index 3344a8c9e501d2edeaddd3097132ec0f8843cc06..fb7e2f09f2461f34fbd4df68167a288352edd972 100644
--- a/base/java-tools/src/com/netscape/cmstools/tps/config/ConfigModifyCLI.java
+++ b/base/java-tools/src/com/netscape/cmstools/tps/config/ConfigModifyCLI.java
@@ -39,22 +39,22 @@ public class ConfigModifyCLI extends CLI {
     public ConfigCLI configCLI;
 
     public ConfigModifyCLI(ConfigCLI configCLI) {
-        super("mod", "Modify configuration", configCLI);
+        super("mod", "Modify general properties", configCLI);
         this.configCLI = configCLI;
     }
 
     public void printHelp() {
-        formatter.printHelp(getFullName() + " <Config ID> [OPTIONS...]", options);
+        formatter.printHelp(getFullName() + " [OPTIONS...]", options);
     }
 
     public void execute(String[] args) throws Exception {
 
-        Option option = new Option(null, "input", true, "Input configuration file.");
+        Option option = new Option(null, "input", true, "Input file containing general properties.");
         option.setArgName("file");
         option.setRequired(true);
         options.addOption(option);
 
-        option = new Option(null, "output", true, "Output configuration file.");
+        option = new Option(null, "output", true, "Output file to store general properties.");
         option.setArgName("file");
         options.addOption(option);
 
@@ -71,12 +71,11 @@ public class ConfigModifyCLI extends CLI {
 
         String[] cmdArgs = cmd.getArgs();
 
-        if (cmdArgs.length != 1) {
+        if (cmdArgs.length != 0) {
             printHelp();
             System.exit(1);
         }
 
-        String configID = args[0];
         String input = cmd.getOptionValue("input");
         String output = cmd.getOptionValue("output");
 
@@ -100,12 +99,12 @@ public class ConfigModifyCLI extends CLI {
             configData = ConfigData.valueOf(sw.toString());
         }
 
-        configData = configCLI.configClient.updateConfig(configID, configData);
+        configData = configCLI.configClient.updateConfig(configData);
 
         MainCLI.printMessage("Updated configuration");
 
         if (output == null) {
-            ConfigCLI.printConfigData(configData, true);
+            ConfigCLI.printConfigData(configData);
 
         } else {
             try (PrintWriter out = new PrintWriter(new FileWriter(output))) {
diff --git a/base/java-tools/src/com/netscape/cmstools/tps/config/ConfigShowCLI.java b/base/java-tools/src/com/netscape/cmstools/tps/config/ConfigShowCLI.java
index 77f77c94f2f0d4d255be9e2ae741df5bad24c112..33bc781ac619566d762e89c01fd9ebc3c6f46f42 100644
--- a/base/java-tools/src/com/netscape/cmstools/tps/config/ConfigShowCLI.java
+++ b/base/java-tools/src/com/netscape/cmstools/tps/config/ConfigShowCLI.java
@@ -36,17 +36,17 @@ public class ConfigShowCLI extends CLI {
     public ConfigCLI configCLI;
 
     public ConfigShowCLI(ConfigCLI configCLI) {
-        super("show", "Show configuration", configCLI);
+        super("show", "Show general properties", configCLI);
         this.configCLI = configCLI;
     }
 
     public void printHelp() {
-        formatter.printHelp(getFullName() + " <Config ID>", options);
+        formatter.printHelp(getFullName(), options);
     }
 
     public void execute(String[] args) throws Exception {
 
-        Option option = new Option(null, "output", true, "Output file to store config attributes.");
+        Option option = new Option(null, "output", true, "Output file to store general properties.");
         option.setArgName("file");
         options.addOption(option);
 
@@ -63,19 +63,18 @@ public class ConfigShowCLI extends CLI {
 
         String[] cmdArgs = cmd.getArgs();
 
-        if (cmdArgs.length != 1) {
+        if (cmdArgs.length != 0) {
             printHelp();
             System.exit(1);
         }
 
-        String configID = cmdArgs[0];
         String output = cmd.getOptionValue("output");
 
-        ConfigData configData = configCLI.configClient.getConfig(configID);
+        ConfigData configData = configCLI.configClient.getConfig();
 
         if (output == null) {
             MainCLI.printMessage("Configuration");
-            ConfigCLI.printConfigData(configData, true);
+            ConfigCLI.printConfigData(configData);
 
         } else {
             try (PrintWriter out = new PrintWriter(new FileWriter(output))) {
diff --git a/base/tps-tomcat/src/CMakeLists.txt b/base/tps-tomcat/src/CMakeLists.txt
index 4798d20042bc8c4d6b6a5660402ba531ebb9b187..a0a7ac5052f9a4068ef20f88d939584572be8393 100644
--- a/base/tps-tomcat/src/CMakeLists.txt
+++ b/base/tps-tomcat/src/CMakeLists.txt
@@ -1,5 +1,12 @@
 project(pki-tps_java NONE)
 
+find_file(APACHE_COMMONS_LANG_JAR
+    NAMES
+        apache-commons-lang.jar
+    PATHS
+        /usr/share/java
+)
+
 find_file(LDAPJDK_JAR
     NAMES
         ldapjdk.jar
@@ -100,7 +107,7 @@ javac(pki-tps-classes
     CLASSPATH
         ${PKI_CERTSRV_JAR} ${PKI_CMS_JAR} ${PKI_CMSCORE_JAR}
         ${PKI_CMSUTIL_JAR} ${PKI_NSUTIL_JAR}
-        ${LDAPJDK_JAR} ${JAXRS_API_JAR} ${RESTEASY_ATOM_PROVIDER_JAR}
+        ${APACHE_COMMONS_LANG_JAR} ${LDAPJDK_JAR} ${JAXRS_API_JAR} ${RESTEASY_ATOM_PROVIDER_JAR}
         ${JSS_JAR} ${COMMONS_CODEC_JAR} ${SYMKEY_JAR} ${SERVLET_JAR}
     OUTPUT_DIR
         ${CMAKE_BINARY_DIR}/classes
diff --git a/base/tps-tomcat/src/org/dogtagpki/server/tps/TPSApplication.java b/base/tps-tomcat/src/org/dogtagpki/server/tps/TPSApplication.java
index c3c6195f563b8716e4e100108024122add8a547e..e90d8bbcc9bec2a2a30ae373412cc1359c624e33 100644
--- a/base/tps-tomcat/src/org/dogtagpki/server/tps/TPSApplication.java
+++ b/base/tps-tomcat/src/org/dogtagpki/server/tps/TPSApplication.java
@@ -73,15 +73,15 @@ public class TPSApplication extends Application {
         // authenticators
         classes.add(AuthenticatorService.class);
 
+        // certificates
+        classes.add(TPSCertService.class);
+
         // config
         classes.add(ConfigService.class);
 
         // connections
         classes.add(ConnectionService.class);
 
-        // certificates
-        classes.add(TPSCertService.class);
-
         // tokens
         classes.add(TokenService.class);
 
diff --git a/base/tps-tomcat/src/org/dogtagpki/server/tps/TPSSubsystem.java b/base/tps-tomcat/src/org/dogtagpki/server/tps/TPSSubsystem.java
index d00378a1d0f34abfb35eeed974bfcfddc935188c..58ed122333af7b46536b9b9222a26ebb30e7a7a8 100644
--- a/base/tps-tomcat/src/org/dogtagpki/server/tps/TPSSubsystem.java
+++ b/base/tps-tomcat/src/org/dogtagpki/server/tps/TPSSubsystem.java
@@ -19,6 +19,7 @@ package org.dogtagpki.server.tps;
 
 import org.dogtagpki.server.tps.authenticator.AuthenticatorDatabase;
 import org.dogtagpki.server.tps.cert.TPSCertDatabase;
+import org.dogtagpki.server.tps.config.ConfigDatabase;
 import org.dogtagpki.server.tps.connection.ConnectionDatabase;
 import org.dogtagpki.server.tps.logging.ActivityDatabase;
 import org.dogtagpki.server.tps.token.TokenDatabase;
@@ -53,9 +54,10 @@ public class TPSSubsystem implements IAuthority, ISubsystem {
     public IConfigStore config;
 
     public ActivityDatabase activityDatabase;
-    public AuthenticatorDatabase authenticatorDatabase = new AuthenticatorDatabase();
+    public AuthenticatorDatabase authenticatorDatabase;
     public TPSCertDatabase certDatabase;
-    public ConnectionDatabase connectionDatabase = new ConnectionDatabase();
+    public ConfigDatabase configDatabase;
+    public ConnectionDatabase connectionDatabase;
     public TokenDatabase tokenDatabase;
 
     @Override
@@ -84,7 +86,11 @@ public class TPSSubsystem implements IAuthority, ISubsystem {
 
         String tokenDatabaseDN = cs.getString("tokendb.baseDN");
         tokenDatabase = new TokenDatabase(dbSubsystem, tokenDatabaseDN);
-    }
+
+        configDatabase = new ConfigDatabase();
+        authenticatorDatabase = new AuthenticatorDatabase();
+        connectionDatabase = new ConnectionDatabase();
+}
 
     @Override
     public void startup() throws EBaseException {
@@ -139,14 +145,18 @@ public class TPSSubsystem implements IAuthority, ISubsystem {
         return authenticatorDatabase;
     }
 
-    public ConnectionDatabase getConnectionDatabase() {
-        return connectionDatabase;
-    }
-
     public TPSCertDatabase getCertDatabase() {
         return certDatabase;
     }
 
+    public ConfigDatabase getConfigDatabase() {
+        return configDatabase;
+    }
+
+    public ConnectionDatabase getConnectionDatabase() {
+        return connectionDatabase;
+    }
+
     public TokenDatabase getTokenDatabase() {
         return tokenDatabase;
     }
diff --git a/base/tps-tomcat/src/org/dogtagpki/server/tps/config/ConfigDatabase.java b/base/tps-tomcat/src/org/dogtagpki/server/tps/config/ConfigDatabase.java
new file mode 100644
index 0000000000000000000000000000000000000000..8a3850165e20db3ef727c899f524ae577e11d943
--- /dev/null
+++ b/base/tps-tomcat/src/org/dogtagpki/server/tps/config/ConfigDatabase.java
@@ -0,0 +1,191 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2013 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+
+package org.dogtagpki.server.tps.config;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.apache.commons.lang.StringUtils;
+
+import com.netscape.certsrv.apps.CMS;
+import com.netscape.certsrv.base.BadRequestException;
+import com.netscape.certsrv.base.EBaseException;
+import com.netscape.certsrv.base.IConfigStore;
+import com.netscape.certsrv.base.ResourceNotFoundException;
+import com.netscape.cmscore.dbs.Database;
+
+/**
+ * This class implements in-memory connection database. In the future this
+ * will be replaced with LDAP database.
+ *
+ * @author Endi S. Dewata
+ */
+public class ConfigDatabase extends Database<ConfigRecord> {
+
+    IConfigStore configStore = CMS.getConfigStore();
+    Map<String, String> map;
+
+    public ConfigDatabase() throws EBaseException {
+        super("Configuration");
+
+        map = configStore.getProperties();
+    }
+
+    public String createFilter(ConfigRecord record, String key) {
+        String pattern = record.getPattern();
+        if (key == null) return pattern;
+        return pattern.replace("$name", key);
+    }
+
+    @Override
+    public Collection<ConfigRecord> getRecords() throws Exception {
+
+        CMS.debug("ConfigDatabase.getRecords()");
+
+        Collection<ConfigRecord> result = new ArrayList<ConfigRecord>();
+
+        Collection<String> configIDs = new LinkedHashSet<String>();
+        configIDs.add("Generals");
+
+        String list = map.get("target.configure.list");
+        if (list != null) {
+            configIDs.addAll(Arrays.asList(list.split(",")));
+        }
+
+        list = map.get("target.agent_approve.list");
+        if (list != null) {
+            configIDs.addAll(Arrays.asList(list.split(",")));
+        }
+
+        for (String configID : configIDs) {
+            ConfigRecord configData = getRecord(configID);
+            result.add(configData);
+        }
+
+        return result;
+    }
+
+    @Override
+    public ConfigRecord getRecord(String configID) throws Exception {
+
+        CMS.debug("ConfigDatabase.getRecord(\"" + configID + "\")");
+
+        ConfigRecord record = new ConfigRecord();
+        record.setID(configID);
+
+        String displayName = map.get("target." + configID + ".displayname");
+        if (displayName == null) {
+            throw new ResourceNotFoundException("Configuration " + configID + " not found.");
+        }
+        record.setDisplayName(displayName);
+
+        String pattern = map.get("target." + configID + ".pattern");
+        if (pattern == null) {
+            throw new ResourceNotFoundException("Missing pattern for " + configID + " configuration.");
+        }
+
+        // replace \| with |
+        record.setPattern(pattern.replace("\\|",  "|"));
+
+        String list = map.get("target." + configID + ".list");
+        if (list != null) {
+            record.setKeys(Arrays.asList(list.split(",")));
+        }
+
+        return record;
+    }
+
+
+    @Override
+    public void updateRecord(String configID, ConfigRecord newRecord) throws Exception {
+
+        CMS.debug("ConfigDatabase.updateRecord(\"" + configID + "\")");
+
+        configStore.put("target." + configID + ".displayname", newRecord.getDisplayName());
+        configStore.put("target." + configID + ".pattern", newRecord.getPattern());
+        configStore.put("target." + configID + ".list", StringUtils.join(newRecord.getKeys(), ","));
+
+        configStore.commit(true);
+        map = configStore.getProperties();
+    }
+
+    public Map<String, String> getProperties(ConfigRecord record, String key) {
+
+        CMS.debug("ConfigDatabase.getProperties(\"" + record.getID() + "\")");
+
+        Map<String, String> properties = new TreeMap<String, String>();
+
+        // get properties that match the filter
+        String filter = createFilter(record, key);
+        for (String name : map.keySet()) {
+            if (!name.matches(filter)) continue;
+
+            String value = map.get(name);
+            properties.put(name, value);
+        }
+
+        return properties;
+    }
+
+    public void validateProperties(ConfigRecord record, String key, Map<String, String> properties) throws Exception {
+
+        CMS.debug("ConfigDatabase.validateProperties(\"" + record.getID() + "\")");
+
+        String filter = createFilter(record, key);
+        for (String name : properties.keySet()) {
+            if (name.matches(filter)) continue;
+            throw new BadRequestException("Invalid property: " + name);
+        }
+    }
+
+    public void addProperties(ConfigRecord record, String key, Map<String, String> properties) throws Exception {
+
+        CMS.debug("ConfigDatabase.addProperties(\"" + record.getID() + "\")");
+
+        for (String name : properties.keySet()) {
+            String value = properties.get(name);
+            configStore.put(name, value);
+        }
+    }
+
+    public void removeProperties(ConfigRecord record, String key) throws Exception {
+
+        CMS.debug("ConfigDatabase.removeProperties(\"" + record.getID() + "\")");
+
+        Map<String, String> oldProperties = getProperties(record, key);
+        for (String name : oldProperties.keySet()) {
+            configStore.remove(name);
+        }
+    }
+
+    public void commit() throws Exception {
+
+        CMS.debug("ConfigDatabase.commit()");
+
+        // save configuration
+        configStore.commit(true);
+
+        // reload configuration
+        map = configStore.getProperties();
+    }
+}
diff --git a/base/tps-tomcat/src/org/dogtagpki/server/tps/config/ConfigRecord.java b/base/tps-tomcat/src/org/dogtagpki/server/tps/config/ConfigRecord.java
new file mode 100644
index 0000000000000000000000000000000000000000..25978212ea9986f482fb318a25f8d0eb8a7d2ee9
--- /dev/null
+++ b/base/tps-tomcat/src/org/dogtagpki/server/tps/config/ConfigRecord.java
@@ -0,0 +1,117 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2013 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+
+package org.dogtagpki.server.tps.config;
+
+import java.util.Collection;
+import java.util.LinkedHashSet;
+
+/**
+ * @author Endi S. Dewata
+ */
+public class ConfigRecord {
+
+    String id;
+    String displayName;
+    String pattern;
+    Collection<String> keys = new LinkedHashSet<String>();
+
+    public String getID() {
+        return id;
+    }
+
+    public void setID(String id) {
+        this.id = id;
+    }
+
+    public String getDisplayName() {
+        return displayName;
+    }
+
+    public void setDisplayName(String displayName) {
+        this.displayName = displayName;
+    }
+
+    public String getPattern() {
+        return pattern;
+    }
+
+    public void setPattern(String pattern) {
+        this.pattern = pattern;
+    }
+
+    public Collection<String> getKeys() {
+        return keys;
+    }
+
+    public void setKeys(Collection<String> keys) {
+        this.keys.clear();
+        this.keys.addAll(keys);
+    }
+
+    public void addKey(String key) {
+        keys.add(key);
+    }
+
+    public void removeKey(String key) {
+        keys.remove(key);
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((displayName == null) ? 0 : displayName.hashCode());
+        result = prime * result + ((id == null) ? 0 : id.hashCode());
+        result = prime * result + ((keys == null) ? 0 : keys.hashCode());
+        result = prime * result + ((pattern == null) ? 0 : pattern.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        ConfigRecord other = (ConfigRecord) obj;
+        if (displayName == null) {
+            if (other.displayName != null)
+                return false;
+        } else if (!displayName.equals(other.displayName))
+            return false;
+        if (id == null) {
+            if (other.id != null)
+                return false;
+        } else if (!id.equals(other.id))
+            return false;
+        if (keys == null) {
+            if (other.keys != null)
+                return false;
+        } else if (!keys.equals(other.keys))
+            return false;
+        if (pattern == null) {
+            if (other.pattern != null)
+                return false;
+        } else if (!pattern.equals(other.pattern))
+            return false;
+        return true;
+    }
+}
diff --git a/base/tps-tomcat/src/org/dogtagpki/server/tps/config/ConfigService.java b/base/tps-tomcat/src/org/dogtagpki/server/tps/config/ConfigService.java
index 41a8c171497afb246b0b13f5fca35d362eb7fc9a..8f338e330cc272166c6537d69de9642b8d65bbfa 100644
--- a/base/tps-tomcat/src/org/dogtagpki/server/tps/config/ConfigService.java
+++ b/base/tps-tomcat/src/org/dogtagpki/server/tps/config/ConfigService.java
@@ -20,11 +20,6 @@ package org.dogtagpki.server.tps.config;
 
 import java.io.UnsupportedEncodingException;
 import java.net.URI;
-import java.net.URLEncoder;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.LinkedHashSet;
 import java.util.Map;
 
 import javax.servlet.http.HttpServletRequest;
@@ -38,11 +33,7 @@ import javax.ws.rs.core.UriInfo;
 import org.jboss.resteasy.plugins.providers.atom.Link;
 
 import com.netscape.certsrv.apps.CMS;
-import com.netscape.certsrv.base.BadRequestException;
-import com.netscape.certsrv.base.IConfigStore;
 import com.netscape.certsrv.base.PKIException;
-import com.netscape.certsrv.base.ResourceNotFoundException;
-import com.netscape.certsrv.tps.config.ConfigCollection;
 import com.netscape.certsrv.tps.config.ConfigData;
 import com.netscape.certsrv.tps.config.ConfigResource;
 import com.netscape.cms.servlet.base.PKIService;
@@ -70,107 +61,28 @@ public class ConfigService extends PKIService implements ConfigResource {
         CMS.debug("ConfigService.<init>()");
     }
 
-    public Collection<String> getPatterns(String configID, Map<String, String> map) {
-        Collection<String> patterns = new ArrayList<String>();
-
-        String pattern = map.get("target." + configID + ".pattern");
-        if (pattern != null) {
-            // replace \| with |
-            pattern = pattern.replace("\\|",  "|");
-
-            String list = map.get("target." + configID + ".list");
-            if (list == null) {
-                patterns.add(pattern);
-
-            } else {
-                for (String value : list.split(",")) {
-                    patterns.add(pattern.replace("$name", value));
-                }
-            }
-        }
-
-        return patterns;
-    }
-
-    public ConfigData createConfigData(String configID, Map<String, String> map) throws UnsupportedEncodingException {
-
-        String displayName = map.get("target." + configID + ".displayname");
-        if (displayName == null) {
-            throw new ResourceNotFoundException("Configuration " + configID + " not found.");
-        }
+    public ConfigData createConfigData(Map<String, String> properties) throws UnsupportedEncodingException {
 
         ConfigData configData = new ConfigData();
-        configData.setID(configID);
-        configData.setDisplayName(displayName);
+        configData.setProperties(properties);
 
-        // add properties that fit the patterns
-        Collection<String> patterns = getPatterns(configID, map);
-        for (String pattern : patterns) {
-            for (String name : map.keySet()) {
-                if (!name.matches(pattern)) continue;
-
-                String value = map.get(name);
-                configData.setProperty(name, value);
-            }
-        }
-
-        configID = URLEncoder.encode(configID, "UTF-8");
-        URI uri = uriInfo.getBaseUriBuilder().path(ConfigResource.class).path("{configID}").build(configID);
+        URI uri = uriInfo.getBaseUriBuilder().path(ConfigResource.class).build();
         configData.setLink(new Link("self", uri));
 
         return configData;
     }
 
     @Override
-    public ConfigCollection findConfigs() {
-
-        CMS.debug("ConfigService.findConfigs()");
-
-        try {
-            IConfigStore configStore = CMS.getConfigStore();
-            Map<String, String> map = configStore.getProperties();
-
-            ConfigCollection result = new ConfigCollection();
-
-            Collection<String> configIDs = new LinkedHashSet<String>();
-            configIDs.add("Generals");
-
-            String list = map.get("target.configure.list");
-            if (list != null) {
-                configIDs.addAll(Arrays.asList(list.split(",")));
-            }
-
-            list = map.get("target.agent_approve.list");
-            if (list != null) {
-                configIDs.addAll(Arrays.asList(list.split(",")));
-            }
-
-            for (String configID : configIDs) {
-                ConfigData configData = createConfigData(configID, map);
-                result.addConfig(configData);
-            }
-
-            return result;
-
-        } catch (PKIException e) {
-            throw e;
-
-        } catch (Exception e) {
-            e.printStackTrace();
-            throw new PKIException(e.getMessage());
-        }
-    }
-
-    @Override
-    public ConfigData getConfig(String configID) {
+    public ConfigData getConfig() {
 
         CMS.debug("ConfigService.getConfig()");
 
         try {
-            IConfigStore configStore = CMS.getConfigStore();
-            Map<String, String> map = configStore.getProperties();
+            ConfigDatabase configDatabase = new ConfigDatabase();
+            ConfigRecord configRecord = configDatabase.getRecord("Generals");
 
-            return createConfigData(configID, map);
+            Map<String, String> properties = configDatabase.getProperties(configRecord, null);
+            return createConfigData(properties);
 
         } catch (PKIException e) {
             throw e;
@@ -182,41 +94,31 @@ public class ConfigService extends PKIService implements ConfigResource {
     }
 
     @Override
-    public Response updateConfig(String configID, ConfigData newConfigData) {
+    public Response updateConfig(ConfigData configData) {
 
         CMS.debug("ConfigService.updateConfig()");
 
         try {
-            IConfigStore configStore = CMS.getConfigStore();
-            Map<String, String> map = configStore.getProperties();
+            ConfigDatabase configDatabase = new ConfigDatabase();
+            ConfigRecord configRecord = configDatabase.getRecord("Generals");
 
-            // verify that new properties fit the patterns
-            Collection<String> patterns = getPatterns(configID, map);
-            for (String pattern : patterns) {
-                for (String name : newConfigData.getPropertyNames()) {
-                    if (name.matches(pattern)) continue;
-                    throw new BadRequestException("Invalid property: " + name);
-                }
-            }
+            // validate new properties
+            Map<String, String> properties = configData.getProperties();
+            configDatabase.validateProperties(configRecord, null, properties);
 
             // remove old properties
-            ConfigData configData = createConfigData(configID, map);
-            for (String name : configData.getPropertyNames()) {
-                configStore.remove(name);
-            }
+            configDatabase.removeProperties(configRecord, null);
 
-            // store new properties
-            for (String name : newConfigData.getPropertyNames()) {
-                String value = newConfigData.getProperty(name);
-                configStore.put(name, value);
-            }
+            // add new properties
+            configDatabase.addProperties(configRecord, null, properties);
 
-            configStore.commit(true);
+            configDatabase.commit();
 
-            newConfigData = getConfig(configID);
+            properties = configDatabase.getProperties(configRecord, null);
+            configData = createConfigData(properties);
 
             return Response
-                    .ok(newConfigData)
+                    .ok(configData)
                     .type(MediaType.APPLICATION_XML)
                     .build();
 
-- 
1.8.1.4

