killbill-uncached

Changes

Details

diff --git a/account/src/main/java/com/ning/billing/account/dao/AccountBinder.java b/account/src/main/java/com/ning/billing/account/dao/AccountBinder.java
new file mode 100644
index 0000000..a4a00a8
--- /dev/null
+++ b/account/src/main/java/com/ning/billing/account/dao/AccountBinder.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.account.dao;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.catalog.api.Currency;
+import org.joda.time.DateTimeZone;
+import org.skife.jdbi.v2.SQLStatement;
+import org.skife.jdbi.v2.sqlobject.Binder;
+import org.skife.jdbi.v2.sqlobject.BinderFactory;
+import org.skife.jdbi.v2.sqlobject.BindingAnnotation;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@BindingAnnotation(AccountBinder.AccountBinderFactory.class)
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.PARAMETER})
+public @interface AccountBinder {
+    public static class AccountBinderFactory implements BinderFactory {
+        @Override
+        public Binder<AccountBinder, Account> build(Annotation annotation) {
+            return new Binder<AccountBinder, Account>() {
+                @Override
+                public void bind(@SuppressWarnings("rawtypes") SQLStatement q, AccountBinder bind, Account account) {
+                    q.bind("id", account.getId().toString());
+                    q.bind("externalKey", account.getExternalKey());
+                    q.bind("email", account.getEmail());
+                    q.bind("name", account.getName());
+                    q.bind("firstNameLength", account.getFirstNameLength());
+                    Currency currency = account.getCurrency();
+                    q.bind("currency", (currency == null) ? null : currency.toString());
+                    q.bind("billingCycleDay", account.getBillCycleDay());
+                    q.bind("paymentProviderName", account.getPaymentProviderName());
+                    DateTimeZone timeZone = account.getTimeZone();
+                    q.bind("timeZone", (timeZone == null) ? null : timeZone.toString());
+                    q.bind("locale", account.getLocale());
+                    q.bind("address1", account.getAddress1());
+                    q.bind("address2", account.getAddress2());
+                    q.bind("companyName", account.getCompanyName());
+                    q.bind("city", account.getCity());
+                    q.bind("stateOrProvince", account.getStateOrProvince());
+                    q.bind("country", account.getCountry());
+                    q.bind("postalCode", account.getPostalCode());
+                    q.bind("phone", account.getPhone());
+                }
+            };
+        }
+    }
+}
diff --git a/account/src/main/java/com/ning/billing/account/dao/AccountHistorySqlDao.java b/account/src/main/java/com/ning/billing/account/dao/AccountHistorySqlDao.java
new file mode 100644
index 0000000..8d1a46e
--- /dev/null
+++ b/account/src/main/java/com/ning/billing/account/dao/AccountHistorySqlDao.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.account.dao;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallContextBinder;
+import org.skife.jdbi.v2.sqlobject.Bind;
+import org.skife.jdbi.v2.sqlobject.SqlUpdate;
+import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
+import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
+
+@ExternalizedSqlViaStringTemplate3
+public interface AccountHistorySqlDao extends Transmogrifier {
+    @SqlUpdate
+    public void insertAccountHistoryFromTransaction(@AccountBinder final Account account,
+                                                    @Bind("historyId") final String historyId,
+                                                    @Bind("changeType") String changeType,
+                                                    @CallContextBinder CallContext context);
+}
diff --git a/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java b/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java
index 413dcd8..2c400d5 100644
--- a/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java
+++ b/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java
@@ -16,14 +16,8 @@
 
 package com.ning.billing.account.dao;
 
-import java.lang.annotation.Annotation;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
 import java.sql.ResultSet;
 import java.sql.SQLException;
-import java.util.Date;
 import java.util.UUID;
 
 import com.ning.billing.util.callcontext.CallContext;
@@ -32,12 +26,8 @@ import com.ning.billing.util.entity.MapperBase;
 import com.ning.billing.util.entity.UpdatableEntityDao;
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
-import org.skife.jdbi.v2.SQLStatement;
 import org.skife.jdbi.v2.StatementContext;
 import org.skife.jdbi.v2.sqlobject.Bind;
-import org.skife.jdbi.v2.sqlobject.Binder;
-import org.skife.jdbi.v2.sqlobject.BinderFactory;
-import org.skife.jdbi.v2.sqlobject.BindingAnnotation;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
 import org.skife.jdbi.v2.sqlobject.SqlUpdate;
 import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
@@ -74,7 +64,6 @@ public interface AccountSqlDao extends UpdatableEntityDao<Account>, Transactiona
     public static class AccountMapper extends MapperBase implements ResultSetMapper<Account> {
         @Override
         public Account map(int index, ResultSet result, StatementContext context) throws SQLException {
-
             UUID id = UUID.fromString(result.getString("id"));
             String externalKey = result.getString("external_key");
             String email = result.getString("email");
@@ -121,44 +110,4 @@ public interface AccountSqlDao extends UpdatableEntityDao<Account>, Transactiona
                                          .build();
         }
     }
-
-    @BindingAnnotation(AccountBinder.AccountBinderFactory.class)
-    @Retention(RetentionPolicy.RUNTIME)
-    @Target({ElementType.PARAMETER})
-    public @interface AccountBinder {
-        public static class AccountBinderFactory implements BinderFactory {
-            @Override
-            public Binder<AccountBinder, Account> build(Annotation annotation) {
-                return new Binder<AccountBinder, Account>() {
-                    private Date getDate(DateTime dateTime) {
-                        return dateTime == null ? null : dateTime.toDate();
-                    }
-
-                    @Override
-                    public void bind(@SuppressWarnings("rawtypes") SQLStatement q, AccountBinder bind, Account account) {
-                        q.bind("id", account.getId().toString());
-                        q.bind("externalKey", account.getExternalKey());
-                        q.bind("email", account.getEmail());
-                        q.bind("name", account.getName());
-                        q.bind("firstNameLength", account.getFirstNameLength());
-                        Currency currency = account.getCurrency();
-                        q.bind("currency", (currency == null) ? null : currency.toString());
-                        q.bind("billingCycleDay", account.getBillCycleDay());
-                        q.bind("paymentProviderName", account.getPaymentProviderName());
-                        DateTimeZone timeZone = account.getTimeZone();
-                        q.bind("timeZone", (timeZone == null) ? null : timeZone.toString());
-                        q.bind("locale", account.getLocale());
-                        q.bind("address1", account.getAddress1());
-                        q.bind("address2", account.getAddress2());
-                        q.bind("companyName", account.getCompanyName());
-                        q.bind("city", account.getCity());
-                        q.bind("stateOrProvince", account.getStateOrProvince());
-                        q.bind("country", account.getCountry());
-                        q.bind("postalCode", account.getPostalCode());
-                        q.bind("phone", account.getPhone());
-                    }
-                };
-            }
-        }
-    }
 }
diff --git a/account/src/main/java/com/ning/billing/account/dao/DefaultAccountDao.java b/account/src/main/java/com/ning/billing/account/dao/DefaultAccountDao.java
index 4fc4ad8..7bdf6c1 100644
--- a/account/src/main/java/com/ning/billing/account/dao/DefaultAccountDao.java
+++ b/account/src/main/java/com/ning/billing/account/dao/DefaultAccountDao.java
@@ -20,8 +20,10 @@ import java.sql.DataTruncation;
 import java.util.List;
 import java.util.UUID;
 
+import com.ning.billing.util.audit.dao.AuditSqlDao;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.customfield.dao.AuditedCustomFieldDao;
+import com.ning.billing.util.entity.ChangeType;
 import com.ning.billing.util.entity.EntityPersistenceException;
 import com.ning.billing.util.tag.dao.AuditedTagDao;
 import org.skife.jdbi.v2.IDBI;
@@ -117,8 +119,17 @@ public class DefaultAccountDao implements AccountDao {
                         throw new AccountApiException(ErrorCode.ACCOUNT_ALREADY_EXISTS, key);
                     }
                     transactionalDao.create(account, context);
+                    UUID historyId = UUID.randomUUID();
 
-                    saveTagsFromWithinTransaction(account, transactionalDao , context);
+                    AccountHistorySqlDao historyDao = accountSqlDao.become(AccountHistorySqlDao.class);
+                    historyDao.insertAccountHistoryFromTransaction(account, historyId.toString(),
+                            ChangeType.INSERT.toString(), context);
+
+                    AuditSqlDao auditDao = accountSqlDao.become(AuditSqlDao.class);
+                    auditDao.insertAuditFromTransaction("account_history", historyId.toString(),
+                                                         ChangeType.INSERT.toString(), context);
+
+                    saveTagsFromWithinTransaction(account, transactionalDao, context);
                     saveCustomFieldsFromWithinTransaction(account, transactionalDao, context);
                     AccountCreationNotification creationEvent = new DefaultAccountCreationEvent(account);
                     eventBus.post(creationEvent);
@@ -155,6 +166,14 @@ public class DefaultAccountDao implements AccountDao {
 
                     accountSqlDao.update(account, context);
 
+                    UUID historyId = UUID.randomUUID();
+                    AccountHistorySqlDao historyDao = accountSqlDao.become(AccountHistorySqlDao.class);
+                    historyDao.insertAccountHistoryFromTransaction(account, historyId.toString(), ChangeType.UPDATE.toString(), context);
+
+                    AuditSqlDao auditDao = accountSqlDao.become(AuditSqlDao.class);
+                    auditDao.insertAuditFromTransaction("account_history" ,historyId.toString(),
+                                                        ChangeType.INSERT.toString(), context);
+
                     saveTagsFromWithinTransaction(account, accountSqlDao, context);
                     saveCustomFieldsFromWithinTransaction(account, accountSqlDao, context);
 
@@ -180,8 +199,18 @@ public class DefaultAccountDao implements AccountDao {
             accountSqlDao.inTransaction(new Transaction<Void, AccountSqlDao>() {
                 @Override
                 public Void inTransaction(final AccountSqlDao accountSqlDao, final TransactionStatus status) throws AccountApiException, Bus.EventBusException {
+                    Account account = accountSqlDao.getAccountByKey(externalKey);
                     accountSqlDao.deleteByKey(externalKey);
 
+                    // TODO: ensure tags and fields aren't orphaned
+                    UUID historyId = UUID.randomUUID();
+                    AccountHistorySqlDao historyDao = accountSqlDao.become(AccountHistorySqlDao.class);
+                    historyDao.insertAccountHistoryFromTransaction(account, historyId.toString(), ChangeType.UPDATE.toString(), context);
+
+                    AuditSqlDao auditDao = accountSqlDao.become(AuditSqlDao.class);
+                    auditDao.insertAuditFromTransaction("account_history", historyId.toString(),
+                                                        ChangeType.INSERT.toString(), context);
+
                     return null;
                 }
             });
diff --git a/account/src/main/resources/com/ning/billing/account/dao/AccountHistorySqlDao.sql.stg b/account/src/main/resources/com/ning/billing/account/dao/AccountHistorySqlDao.sql.stg
new file mode 100644
index 0000000..c64c29f
--- /dev/null
+++ b/account/src/main/resources/com/ning/billing/account/dao/AccountHistorySqlDao.sql.stg
@@ -0,0 +1,15 @@
+group AccountHistorySqlDao;
+
+insertAccountHistoryFromTransaction() ::= <<
+    INSERT INTO account_history
+    (history_id, id, external_key, email, name, first_name_length, currency,
+    billing_cycle_day, payment_provider_name, time_zone, locale,
+    address1, address2, company_name, city, state_or_province,
+    country, postal_code, phone, change_type, updated_by, date)
+    VALUES
+    (:historyId, :id, :externalKey, :email, :name, :firstNameLength, :currency,
+     :billingCycleDay, :paymentProviderName, :timeZone, :locale,
+     :address1, :address2, :companyName, :city, :stateOrProvince,
+     :country, :postalCode, :phone, :changeType, :userName, :createdDate);
+>>
+;
\ No newline at end of file
diff --git a/account/src/main/resources/com/ning/billing/account/ddl.sql b/account/src/main/resources/com/ning/billing/account/ddl.sql
index ca10f6b..f5e597c 100644
--- a/account/src/main/resources/com/ning/billing/account/ddl.sql
+++ b/account/src/main/resources/com/ning/billing/account/ddl.sql
@@ -30,6 +30,7 @@ CREATE UNIQUE INDEX accounts_email ON accounts(email);
 
 DROP TABLE IF EXISTS account_history;
 CREATE TABLE account_history (
+    history_id char(36) NOT NULL,
     id char(36) NOT NULL,
     external_key varchar(128) NULL,
     email varchar(50) NOT NULL,
diff --git a/util/src/main/java/com/ning/billing/util/audit/dao/AuditSqlDao.java b/util/src/main/java/com/ning/billing/util/audit/dao/AuditSqlDao.java
new file mode 100644
index 0000000..9a45533
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/audit/dao/AuditSqlDao.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.util.audit.dao;
+
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallContextBinder;
+import org.skife.jdbi.v2.sqlobject.Bind;
+import org.skife.jdbi.v2.sqlobject.SqlUpdate;
+import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
+
+@ExternalizedSqlViaStringTemplate3
+public interface AuditSqlDao {
+    @SqlUpdate
+    public void insertAuditFromTransaction(@Bind("tableName") final String tableName,
+                                           @Bind("recordId") final String recordId,
+                                           @Bind("changeType") String changeType,
+                                           @CallContextBinder CallContext context);
+}
diff --git a/util/src/main/java/com/ning/billing/util/customfield/CustomFieldHistory.java b/util/src/main/java/com/ning/billing/util/customfield/CustomFieldHistory.java
new file mode 100644
index 0000000..af3e929
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/customfield/CustomFieldHistory.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.util.customfield;
+
+import com.ning.billing.util.entity.ChangeType;
+import org.joda.time.DateTime;
+
+import java.util.UUID;
+
+public class CustomFieldHistory implements CustomField {
+    private final UUID historyId = UUID.randomUUID();
+    private final CustomField field;
+    private final ChangeType changeType;
+
+    public CustomFieldHistory(CustomField field, ChangeType changeType) {
+        this.field = field;
+        this.changeType = changeType;
+    }
+
+    public UUID getHistoryId() {
+        return historyId;
+    }
+
+    public ChangeType getChangeType() {
+        return changeType;
+    }
+
+    @Override
+    public String getName() {
+        return field.getName();
+    }
+
+    @Override
+    public String getValue() {
+        return field.getValue();
+    }
+
+    @Override
+    public void setValue(String value) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public UUID getId() {
+        return field.getId();
+    }
+
+    @Override
+    public String getCreatedBy() {
+        return field.getCreatedBy();
+    }
+
+    @Override
+    public DateTime getCreatedDate() {
+        return field.getCreatedDate();
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/customfield/CustomFieldHistoryBinder.java b/util/src/main/java/com/ning/billing/util/customfield/CustomFieldHistoryBinder.java
new file mode 100644
index 0000000..51e3621
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/customfield/CustomFieldHistoryBinder.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.util.customfield;
+
+import org.skife.jdbi.v2.SQLStatement;
+import org.skife.jdbi.v2.sqlobject.Binder;
+import org.skife.jdbi.v2.sqlobject.BinderFactory;
+import org.skife.jdbi.v2.sqlobject.BindingAnnotation;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@BindingAnnotation(CustomFieldHistoryBinder.CustomFieldBinderFactory.class)
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.PARAMETER})
+public @interface CustomFieldHistoryBinder {
+    public static class CustomFieldBinderFactory implements BinderFactory {
+        @Override
+        public Binder build(Annotation annotation) {
+            return new Binder<CustomFieldHistoryBinder, CustomFieldHistory>() {
+                @Override
+                public void bind(SQLStatement q, CustomFieldHistoryBinder bind, CustomFieldHistory customFieldHistory) {
+                    q.bind("historyId", customFieldHistory.getHistoryId().toString());
+                    q.bind("changeType", customFieldHistory.getChangeType().toString());
+                    q.bind("id", customFieldHistory.getId().toString());
+                    q.bind("fieldName", customFieldHistory.getName());
+                    q.bind("fieldValue", customFieldHistory.getValue());
+                }
+            };
+        }
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/customfield/dao/AuditedCustomFieldDao.java b/util/src/main/java/com/ning/billing/util/customfield/dao/AuditedCustomFieldDao.java
index 5b5b864..7a3e94d 100644
--- a/util/src/main/java/com/ning/billing/util/customfield/dao/AuditedCustomFieldDao.java
+++ b/util/src/main/java/com/ning/billing/util/customfield/dao/AuditedCustomFieldDao.java
@@ -17,7 +17,9 @@
 package com.ning.billing.util.customfield.dao;
 
 import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.entity.ChangeType;
 import com.ning.billing.util.customfield.CustomField;
+import com.ning.billing.util.customfield.CustomFieldHistory;
 import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 
 import java.util.ArrayList;
@@ -54,9 +56,25 @@ public class AuditedCustomFieldDao {
         customFieldSqlDao.batchUpdateFromTransaction(objectId.toString(), objectType, fieldsToUpdate, context);
         customFieldSqlDao.batchDeleteFromTransaction(objectId.toString(), objectType, existingFields, context);
 
+        List<CustomFieldHistory> fieldHistories = new ArrayList<CustomFieldHistory>();
+        fieldHistories.addAll(convertToHistoryEntry(fields, ChangeType.INSERT));
+        fieldHistories.addAll(convertToHistoryEntry(fieldsToUpdate, ChangeType.UPDATE));
+        fieldHistories.addAll(convertToHistoryEntry(existingFields, ChangeType.DELETE));
+
+        CustomFieldHistorySqlDao historyDao = dao.become(CustomFieldHistorySqlDao.class);
+        historyDao.batchAddHistoryFromTransaction(objectId.toString(), objectType, fieldHistories, context);
+
         CustomFieldAuditSqlDao auditDao = dao.become(CustomFieldAuditSqlDao.class);
-        auditDao.batchInsertFromTransaction(objectId.toString(), objectType, fields, context);
-        auditDao.batchUpdateFromTransaction(objectId.toString(), objectType, fieldsToUpdate, context);
-        auditDao.batchDeleteFromTransaction(objectId.toString(), objectType, existingFields, context);
+        auditDao.batchInsertAuditLogFromTransaction(objectId.toString(), objectType, fieldHistories, context);
+    }
+
+    private List<CustomFieldHistory> convertToHistoryEntry(List<CustomField> fields, ChangeType changeType) {
+        List<CustomFieldHistory> result = new ArrayList<CustomFieldHistory>();
+
+        for (CustomField field : fields) {
+            result.add(new CustomFieldHistory(field, changeType));
+        }
+
+        return result;
     }
 }
diff --git a/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldAuditSqlDao.java b/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldAuditSqlDao.java
index 5cbf937..471cdca 100644
--- a/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldAuditSqlDao.java
+++ b/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldAuditSqlDao.java
@@ -18,8 +18,8 @@ package com.ning.billing.util.customfield.dao;
 
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.CallContextBinder;
-import com.ning.billing.util.customfield.CustomField;
-import com.ning.billing.util.customfield.CustomFieldBinder;
+import com.ning.billing.util.customfield.CustomFieldHistory;
+import com.ning.billing.util.customfield.CustomFieldHistoryBinder;
 import org.skife.jdbi.v2.sqlobject.Bind;
 import org.skife.jdbi.v2.sqlobject.SqlBatch;
 import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
@@ -30,20 +30,8 @@ import java.util.List;
 @ExternalizedSqlViaStringTemplate3
 public interface CustomFieldAuditSqlDao extends Transactional<CustomFieldAuditSqlDao> {
     @SqlBatch(transactional=false)
-    public void batchInsertFromTransaction(@Bind("objectId") final String objectId,
-                                           @Bind("objectType") final String objectType,
-                                           @CustomFieldBinder final List<CustomField> entities,
-                                           @CallContextBinder final CallContext context);
-
-    @SqlBatch(transactional=false)
-    public void batchUpdateFromTransaction(@Bind("objectId") final String objectId,
-                                           @Bind("objectType") final String objectType,
-                                           @CustomFieldBinder final List<CustomField> entities,
-                                           @CallContextBinder final CallContext context);
-
-    @SqlBatch(transactional=false)
-    public void batchDeleteFromTransaction(@Bind("objectId") final String objectId,
-                                           @Bind("objectType") final String objectType,
-                                           @CustomFieldBinder final List<CustomField> entities,
-                                           @CallContextBinder final CallContext context);
+    public void batchInsertAuditLogFromTransaction(@Bind("objectId") final String objectId,
+                                                   @Bind("objectType") final String objectType,
+                                                   @CustomFieldHistoryBinder final List<CustomFieldHistory> entities,
+                                                   @CallContextBinder final CallContext context);
 }
diff --git a/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldHistorySqlDao.java b/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldHistorySqlDao.java
new file mode 100644
index 0000000..6c08532
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/customfield/dao/CustomFieldHistorySqlDao.java
@@ -0,0 +1,20 @@
+package com.ning.billing.util.customfield.dao;
+
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallContextBinder;
+import com.ning.billing.util.customfield.CustomFieldHistory;
+import com.ning.billing.util.customfield.CustomFieldHistoryBinder;
+import org.skife.jdbi.v2.sqlobject.Bind;
+import org.skife.jdbi.v2.sqlobject.SqlBatch;
+import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
+
+import java.util.List;
+
+@ExternalizedSqlViaStringTemplate3
+public interface CustomFieldHistorySqlDao {
+    @SqlBatch(transactional=false)
+    public void batchAddHistoryFromTransaction(@Bind("objectId") final String objectId,
+                                               @Bind("objectType") final String objectType,
+                                               @CustomFieldHistoryBinder final List<CustomFieldHistory> entities,
+                                               @CallContextBinder final CallContext context);
+}
diff --git a/util/src/main/java/com/ning/billing/util/entity/ChangeType.java b/util/src/main/java/com/ning/billing/util/entity/ChangeType.java
new file mode 100644
index 0000000..bbac42c
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/entity/ChangeType.java
@@ -0,0 +1,7 @@
+package com.ning.billing.util.entity;
+
+public enum ChangeType {
+    INSERT,
+    UPDATE,
+    DELETE
+}
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/TagMapper.java b/util/src/main/java/com/ning/billing/util/tag/dao/TagMapper.java
index e134f9c..74e6f04 100644
--- a/util/src/main/java/com/ning/billing/util/tag/dao/TagMapper.java
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/TagMapper.java
@@ -18,9 +18,13 @@ package com.ning.billing.util.tag.dao;
 
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.List;
 import java.util.UUID;
 
 import com.ning.billing.util.entity.MapperBase;
+import com.ning.billing.util.tag.ControlTag;
 import org.joda.time.DateTime;
 import org.skife.jdbi.v2.StatementContext;
 import org.skife.jdbi.v2.tweak.ResultSetMapper;
@@ -35,15 +39,21 @@ public class TagMapper extends MapperBase implements ResultSetMapper<Tag> {
     public Tag map(final int index, final ResultSet result, final StatementContext context) throws SQLException {
         String name = result.getString("tag_definition_name");
 
-        try {
-            ControlTagType controlTagType = ControlTagType.valueOf(name);
-            return new DefaultControlTag(controlTagType);
-        } catch (Throwable t) {
+        ControlTagType thisTagType = null;
+        for (ControlTagType controlTagType : ControlTagType.values()) {
+            if (name.equals(controlTagType.toString())) {
+                thisTagType = controlTagType;
+            }
+        }
+
+        if (thisTagType == null) {
             UUID id = UUID.fromString(result.getString("id"));
             String createdBy = result.getString("created_by");
             DateTime createdDate = new DateTime(result.getTimestamp("created_date"));
 
             return new DescriptiveTag(id, createdBy, createdDate, name);
+        } else {
+            return new DefaultControlTag(thisTagType);
         }
     }
 }
diff --git a/util/src/main/resources/com/ning/billing/util/audit/dao/AuditSqlDao.sql.stg b/util/src/main/resources/com/ning/billing/util/audit/dao/AuditSqlDao.sql.stg
new file mode 100644
index 0000000..25cc9d4
--- /dev/null
+++ b/util/src/main/resources/com/ning/billing/util/audit/dao/AuditSqlDao.sql.stg
@@ -0,0 +1,16 @@
+group AuditSqlDao;
+
+fields(prefix) ::= <<
+    <prefix>table_name,
+    <prefix>record_id,
+    <prefix>change_type,
+    <prefix>change_date,
+    <prefix>changed_by,
+    <prefix>reason_code,
+    <prefix>comments
+>>
+
+insertAuditFromTransaction() ::= <<
+    INSERT INTO audit_log(<fields()>)
+    VALUES(:tableName, :recordId, :changeType, :createdDate, :userName, NULL, NULL);
+>>
\ No newline at end of file
diff --git a/util/src/main/resources/com/ning/billing/util/customfield/dao/CustomFieldAuditSqlDao.sql.stg b/util/src/main/resources/com/ning/billing/util/customfield/dao/CustomFieldAuditSqlDao.sql.stg
index f8233e5..99a2830 100644
--- a/util/src/main/resources/com/ning/billing/util/customfield/dao/CustomFieldAuditSqlDao.sql.stg
+++ b/util/src/main/resources/com/ning/billing/util/customfield/dao/CustomFieldAuditSqlDao.sql.stg
@@ -10,18 +10,8 @@ fields(prefix) ::= <<
     <prefix>comments
 >>
 
-batchInsertFromTransaction() ::= <<
+batchInsertAuditLogFromTransaction() ::= <<
     INSERT INTO audit_log(<fields()>)
-    VALUES('custom_field', :id, 'INSERT', :createdDate, :userName, NULL, NULL);
->>
-
-batchDeleteFromTransaction() ::= <<
-    INSERT INTO audit_log(<fields()>)
-    VALUES('custom_field', :id, 'DELETE', :updatedDate, :userName, NULL, NULL);
->>
-
-batchUpdateFromTransaction() ::= <<
-    INSERT INTO audit_log(<fields()>)
-    VALUES('custom_field', :id, 'UPDATE', :updatedDate, :userName, NULL, NULL);
+    VALUES('custom_field_history', :id, :changeType, :createdDate, :userName, NULL, NULL);
 >>
 ;
\ No newline at end of file
diff --git a/util/src/main/resources/com/ning/billing/util/customfield/dao/CustomFieldHistorySqlDao.sql.stg b/util/src/main/resources/com/ning/billing/util/customfield/dao/CustomFieldHistorySqlDao.sql.stg
new file mode 100644
index 0000000..c9e2ab0
--- /dev/null
+++ b/util/src/main/resources/com/ning/billing/util/customfield/dao/CustomFieldHistorySqlDao.sql.stg
@@ -0,0 +1,18 @@
+group CustomFieldHistorySqlDao;
+
+fields(prefix) ::= <<
+  <prefix>history_id,
+  <prefix>id,
+  <prefix>object_id,
+  <prefix>object_type,
+  <prefix>field_name,
+  <prefix>field_value,
+  <prefix>updated_by,
+  <prefix>date,
+  <prefix>change_type
+>>
+
+batchAddHistoryFromTransaction() ::= <<
+    INSERT INTO custom_field_history(<fields()>)
+    VALUES(:historyId, :id, :objectId, :objectType, :fieldName, :fieldValue, :userName, :updatedDate, :changeType);
+>>
\ No newline at end of file
diff --git a/util/src/main/resources/com/ning/billing/util/ddl.sql b/util/src/main/resources/com/ning/billing/util/ddl.sql
index 8d78d54..7cf98f2 100644
--- a/util/src/main/resources/com/ning/billing/util/ddl.sql
+++ b/util/src/main/resources/com/ning/billing/util/ddl.sql
@@ -16,13 +16,14 @@ CREATE UNIQUE INDEX custom_fields_unique ON custom_fields(object_id, object_type
 
 DROP TABLE IF EXISTS custom_field_history;
 CREATE TABLE custom_field_history (
+  history_id char(36) NOT NULL,
   id char(36) NOT NULL,
   object_id char(36) NOT NULL,
   object_type varchar(30) NOT NULL,
   field_name varchar(30),
   field_value varchar(255),
-  date datetime NOT NULL,
   updated_by varchar(30) NOT NULL,
+  date datetime NOT NULL,
   change_type char(6) NOT NULL
 ) ENGINE=innodb;
 CREATE INDEX custom_field_history_object_id_object_type ON custom_fields(object_id, object_type);