killbill-aplcache

Changes

Details

diff --git a/api/src/main/java/com/ning/billing/util/dao/ObjectType.java b/api/src/main/java/com/ning/billing/util/dao/ObjectType.java
index 18d987c..196cc19 100644
--- a/api/src/main/java/com/ning/billing/util/dao/ObjectType.java
+++ b/api/src/main/java/com/ning/billing/util/dao/ObjectType.java
@@ -21,6 +21,7 @@ public enum ObjectType {
     ACCOUNT_EMAIL("account email"),
     BUNDLE("subscription bundle"),
     INVOICE("invoice"),
+    PAYMENT("payment"),
     RECURRING_INVOICE_ITEM("recurring_invoice_item"),
     SUBSCRIPTION("subscription");
 
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/CustomFieldJson.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/CustomFieldJson.java
index 88d1ae3..6ba6884 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/CustomFieldJson.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/CustomFieldJson.java
@@ -17,6 +17,7 @@ package com.ning.billing.jaxrs.json;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 
+import com.fasterxml.jackson.annotation.JsonProperty;
 import com.ning.billing.util.customfield.CustomField;
 
 public class CustomFieldJson {
@@ -30,7 +31,8 @@ public class CustomFieldJson {
     }
     
     @JsonCreator
-    public CustomFieldJson(String name, String value) {
+    public CustomFieldJson(@JsonProperty("name") String name,
+                           @JsonProperty("value") String value) {
         super();
         this.name = name;
         this.value = value;
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java
index 33be386..bd25d40 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java
@@ -21,10 +21,8 @@ import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
 import java.util.UUID;
 
 import javax.ws.rs.Consumes;
@@ -46,7 +44,6 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Function;
-import com.google.common.base.Preconditions;
 import com.google.common.collect.Collections2;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
@@ -72,31 +69,25 @@ import com.ning.billing.jaxrs.util.TagHelper;
 import com.ning.billing.payment.api.PaymentApi;
 import com.ning.billing.payment.api.PaymentApiException;
 import com.ning.billing.payment.api.PaymentAttempt;
-import com.ning.billing.util.api.TagDefinitionApiException;
 import com.ning.billing.util.api.TagUserApi;
-import com.ning.billing.util.customfield.CustomField;
-import com.ning.billing.util.customfield.StringCustomField;
-import com.ning.billing.util.tag.Tag;
-import com.ning.billing.util.tag.TagDefinition;
-
 
 @Singleton
-@Path(BaseJaxrsResource.ACCOUNTS_PATH)
-public class AccountResource implements BaseJaxrsResource {
+@Path(JaxrsResource.ACCOUNTS_PATH)
+public class AccountResource extends JaxRsResourceBase {
 
     private static final Logger log = LoggerFactory.getLogger(AccountResource.class);
+    private static final String ID_PARAM_NAME = "accountId";
+    private static final String CUSTOM_FIELD_URI = JaxrsResource.CUSTOM_FIELDS + "/{" + ID_PARAM_NAME + ":" + UUID_PATTERN + "}";
+    private static final String TAG_URI = JaxrsResource.TAGS + "/{" + ID_PARAM_NAME + ":" + UUID_PATTERN + "}";
 
     private final AccountUserApi accountApi;
     private final EntitlementUserApi entitlementApi;
-    private final CustomFieldUserApi customFieldApi;
     private final EntitlementTimelineApi timelineApi;
     private final InvoiceUserApi invoiceApi;
     private final PaymentApi paymentApi;
     private final Context context;
-    private final TagUserApi tagUserApi;
     private final JaxrsUriBuilder uriBuilder;
-    private final TagHelper tagHelper;
-    
+
     @Inject
     public AccountResource(final JaxrsUriBuilder uriBuilder,
             final AccountUserApi accountApi,
@@ -108,16 +99,14 @@ public class AccountResource implements BaseJaxrsResource {
             final TagUserApi tagUserApi,
             final TagHelper tagHelper,
             final Context context) {
+        super(uriBuilder, tagUserApi, tagHelper, customFieldUserApi);
         this.uriBuilder = uriBuilder;
     	this.accountApi = accountApi;
-    	this.tagUserApi = tagUserApi;
         this.entitlementApi = entitlementApi;
-        this.customFieldApi = customFieldUserApi;
         this.invoiceApi = invoiceApi;
         this.paymentApi = paymentApi;
         this.timelineApi = timelineApi;
         this.context = context;
-        this.tagHelper = tagHelper;
     }
 
     @GET
@@ -187,8 +176,7 @@ public class AccountResource implements BaseJaxrsResource {
         try {
             AccountData data = json.toAccountData();
             final Account account = accountApi.createAccount(data, null, null, context.createContext(createdBy, reason, comment));
-            Response response = uriBuilder.buildResponse(AccountResource.class, "getAccount", account.getId());
-            return response;
+            return uriBuilder.buildResponse(AccountResource.class, "getAccount", account.getId());
         } catch (AccountApiException e) {
             final String error = String.format("Failed to create account %s", json);
             log.info(error, e);
@@ -284,162 +272,75 @@ public class AccountResource implements BaseJaxrsResource {
         }
     }
     
-    
-    /****************************      TAGS     ******************************/
-    
     @GET
-    @Path(BaseJaxrsResource.TAGS + "/{accountId:" + UUID_PATTERN + "}")
+    @Path(CUSTOM_FIELD_URI)
     @Produces(APPLICATION_JSON)
-    public Response getAccountTags(@PathParam("accountId") String accountId) {
-        try {
-            Account account = accountApi.getAccountById(UUID.fromString(accountId));
-            Map<String, Tag> tags = tagUserApi.getTags(account.getId(), ObjectType.ACCOUNT);
-            Collection<String> tagNameList = (tags.size() == 0) ?
-                    Collections.<String>emptyList() :
-                Collections2.transform(tags.values(), new Function<Tag, String>() {
-                @Override
-                public String apply(Tag input) {
-                    return input.getTagDefinitionName();
-                }
-            });
-            return Response.status(Status.OK).entity(tagNameList).build();
-        } catch (AccountApiException e) {
-            return Response.status(Status.NO_CONTENT).build();
-        }
+    public Response getCustomFields(@PathParam(ID_PARAM_NAME) final String id) {
+        return super.getCustomFields(UUID.fromString(id));
     }
 
-    
     @POST
-    @Path(BaseJaxrsResource.TAGS + "/{accountId:" + UUID_PATTERN + "}")    
+    @Path(CUSTOM_FIELD_URI)
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
-    public Response createAccountTag(@PathParam("accountId") final String accountId,
-            @QueryParam(QUERY_TAGS) final String tagList,
+    public Response createCustomFields(@PathParam(ID_PARAM_NAME) final String id,
+            final List<CustomFieldJson> customFields,
             @HeaderParam(HDR_CREATED_BY) final String createdBy,
             @HeaderParam(HDR_REASON) final String reason,
             @HeaderParam(HDR_COMMENT) final String comment) {
-
-        try {
-            Preconditions.checkNotNull(tagList, "Query % list cannot be null", QUERY_TAGS);
-            
-            Account account = accountApi.getAccountById(UUID.fromString(accountId));
-
-            List<TagDefinition> input = tagHelper.getTagDefinitionFromTagList(tagList);
-            tagUserApi.addTags(account.getId(), ObjectType.ACCOUNT, input, context.createContext(createdBy, reason, comment));
-
-            Response response = uriBuilder.buildResponse(AccountResource.class, "getAccountTags", account.getId());
-            return response;
-        } catch (AccountApiException e) {
-            return Response.status(Status.NO_CONTENT).build();
-        } catch (IllegalArgumentException e) {
-            return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
-        } catch (NullPointerException e) {
-            return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
-        } catch (TagDefinitionApiException e) {
-            return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
-        }
+        return super.createCustomFields(UUID.fromString(id), customFields,
+                context.createContext(createdBy, reason, comment));
     }
     
     @DELETE
-    @Path(BaseJaxrsResource.TAGS +  "/{accountId:" + UUID_PATTERN + "}")    
+    @Path(CUSTOM_FIELD_URI)
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
-    public Response deleteAccountTag(@PathParam("accountId") final String accountId,
-            @QueryParam(QUERY_TAGS) final String tagList,
+    public Response deleteCustomFields(@PathParam(ID_PARAM_NAME) final String id,
+            @QueryParam(QUERY_CUSTOM_FIELDS) final String customFieldList,
             @HeaderParam(HDR_CREATED_BY) final String createdBy,
             @HeaderParam(HDR_REASON) final String reason,
             @HeaderParam(HDR_COMMENT) final String comment) {
-
-        try {
-            Account account = accountApi.getAccountById(UUID.fromString(accountId));
-
-            List<TagDefinition> input = tagHelper.getTagDefinitionFromTagList(tagList);
-            tagUserApi.removeTags(account.getId(), ObjectType.ACCOUNT, input, context.createContext(createdBy, reason, comment));
-
-            return Response.status(Status.OK).build();
-        } catch (AccountApiException e) {
-            return Response.status(Status.NO_CONTENT).build();
-        } catch (IllegalArgumentException e) {
-            return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
-        } catch (NullPointerException e) {
-            return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
-        } catch (TagDefinitionApiException e) {
-            return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
-        }
+        return super.deleteCustomFields(UUID.fromString(id), customFieldList,
+                context.createContext(createdBy, reason, comment));
     }
-    
-    /************************   CUSTOM FIELDS   ******************************/
-    
+
     @GET
-    @Path(BaseJaxrsResource.CUSTOM_FIELDS + "/{accountId:" + UUID_PATTERN + "}")
+    @Path(TAG_URI)
     @Produces(APPLICATION_JSON)
-    public Response getAccountCustomFields(@PathParam("accountId") String accountId) {
-        try {
-            Account account = accountApi.getAccountById(UUID.fromString(accountId));
-            Map<String, CustomField> fields = customFieldApi.getCustomFields(account.getId(), ObjectType.ACCOUNT);
-
-            List<CustomFieldJson> result = new LinkedList<CustomFieldJson>();
-            for (CustomField cur : fields.values()) {
-                result.add(new CustomFieldJson(cur));
-            }
-            return Response.status(Status.OK).entity(result).build();
-        } catch (AccountApiException e) {
-            return Response.status(Status.NO_CONTENT).build();
-        }
+    public Response getTags(@PathParam(ID_PARAM_NAME) String id) {
+        return super.getTags(UUID.fromString(id));
     }
-    
-    
+
     @POST
-    @Path(BaseJaxrsResource.CUSTOM_FIELDS + "/{accountId:" + UUID_PATTERN + "}")    
+    @Path(TAG_URI)
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
-    public Response createCustomField(@PathParam("accountId") final String accountId,
-            List<CustomFieldJson> customFields,
+    public Response createTags(@PathParam(ID_PARAM_NAME) final String id,
+            @QueryParam(QUERY_TAGS) final String tagList,
             @HeaderParam(HDR_CREATED_BY) final String createdBy,
             @HeaderParam(HDR_REASON) final String reason,
             @HeaderParam(HDR_COMMENT) final String comment) {
-
-        try {
-            
-            Account account = accountApi.getAccountById(UUID.fromString(accountId));
-            LinkedList<CustomField> input = new LinkedList<CustomField>();
-            for (CustomFieldJson cur : customFields) {
-                input.add(new StringCustomField(cur.getName(), cur.getValue()));
-            }
-
-            customFieldApi.saveCustomFields(account.getId(), ObjectType.ACCOUNT, input, context.createContext(createdBy, reason, comment));
-            Response response = uriBuilder.buildResponse(AccountResource.class, "getAccountCustomFields", account.getId());            
-            return response;
-        } catch (AccountApiException e) {
-            return Response.status(Status.NO_CONTENT).build();
-        } catch (IllegalArgumentException e) {
-            return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
-        } catch (NullPointerException e) {
-            return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
-        }
+        return super.createTags(UUID.fromString(id), tagList,
+                                context.createContext(createdBy, reason, comment));
     }
-    
+
     @DELETE
-    @Path(BaseJaxrsResource.CUSTOM_FIELDS +  "/{accountId:" + UUID_PATTERN + "}")    
+    @Path(TAG_URI)
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
-    public Response deleteCustomFields(@PathParam("accountId") final String accountId,
-            @QueryParam(QUERY_CUSTOM_FIELDS) final String customFieldList,
+    public Response deleteTags(@PathParam(ID_PARAM_NAME) final String id,
+            @QueryParam(QUERY_TAGS) final String tagList,
             @HeaderParam(HDR_CREATED_BY) final String createdBy,
             @HeaderParam(HDR_REASON) final String reason,
             @HeaderParam(HDR_COMMENT) final String comment) {
 
-        try {
-            Account account = accountApi.getAccountById(UUID.fromString(accountId));
-            // STEPH missing API to delete custom fields
-            return Response.status(Status.OK).build();
-        } catch (AccountApiException e) {
-            return Response.status(Status.NO_CONTENT).build();
-        } catch (IllegalArgumentException e) {
-            return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
-        } catch (NullPointerException e) {
-            return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
-        }
+        return super.deleteTags(UUID.fromString(id), tagList,
+                                context.createContext(createdBy, reason, comment));
+    }
+
+    @Override
+    protected ObjectType getObjectType() {
+        return ObjectType.ACCOUNT;
     }
-    
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java
index 662566a..f2d64a5 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java
@@ -23,6 +23,7 @@ import java.util.List;
 import java.util.UUID;
 
 import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
 import javax.ws.rs.GET;
 import javax.ws.rs.HeaderParam;
 import javax.ws.rs.POST;
@@ -33,6 +34,11 @@ import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 
+import com.ning.billing.jaxrs.json.CustomFieldJson;
+import com.ning.billing.jaxrs.util.TagHelper;
+import com.ning.billing.util.api.CustomFieldUserApi;
+import com.ning.billing.util.api.TagUserApi;
+import com.ning.billing.util.dao.ObjectType;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -49,17 +55,23 @@ import com.ning.billing.jaxrs.json.SubscriptionJsonNoEvents;
 import com.ning.billing.jaxrs.util.Context;
 import com.ning.billing.jaxrs.util.JaxrsUriBuilder;
 
-@Path(BaseJaxrsResource.BUNDLES_PATH)
-public class BundleResource implements BaseJaxrsResource {
+@Path(JaxrsResource.BUNDLES_PATH)
+public class BundleResource extends JaxRsResourceBase {
 
     private static final Logger log = LoggerFactory.getLogger(BundleResource.class);
+    private static final String ID_PARAM_NAME = "bundleId";
+    private static final String CUSTOM_FIELD_URI = JaxrsResource.CUSTOM_FIELDS + "/{" + ID_PARAM_NAME + ":" + UUID_PATTERN + "}";
+    private static final String TAG_URI = JaxrsResource.TAGS + "/{" + ID_PARAM_NAME + ":" + UUID_PATTERN + "}";
 
     private final EntitlementUserApi entitlementApi;
     private final Context context;
-    private final JaxrsUriBuilder uriBuilder;	
+    private final JaxrsUriBuilder uriBuilder;
 
     @Inject
-    public BundleResource(final JaxrsUriBuilder uriBuilder, final EntitlementUserApi entitlementApi, final Context context) {
+    public BundleResource(final JaxrsUriBuilder uriBuilder, final EntitlementUserApi entitlementApi,
+                          TagUserApi tagUserApi, TagHelper tagHelper, CustomFieldUserApi customFieldUserApi,
+                          Context context) {
+        super(uriBuilder, tagUserApi, tagHelper, customFieldUserApi);
         this.uriBuilder = uriBuilder;
         this.entitlementApi = entitlementApi;
         this.context = context;
@@ -147,4 +159,76 @@ public class BundleResource implements BaseJaxrsResource {
 
         }
     }
+
+    @GET
+    @Path(CUSTOM_FIELD_URI)
+    @Produces(APPLICATION_JSON)
+    public Response getCustomFields(@PathParam(ID_PARAM_NAME) final String id) {
+        return super.getCustomFields(UUID.fromString(id));
+    }
+
+    @POST
+    @Path(CUSTOM_FIELD_URI)
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON)
+    public Response createCustomFields(@PathParam(ID_PARAM_NAME) final String id,
+            final List<CustomFieldJson> customFields,
+            @HeaderParam(HDR_CREATED_BY) final String createdBy,
+            @HeaderParam(HDR_REASON) final String reason,
+            @HeaderParam(HDR_COMMENT) final String comment) {
+        return super.createCustomFields(UUID.fromString(id), customFields,
+                context.createContext(createdBy, reason, comment));
+    }
+
+    @DELETE
+    @Path(CUSTOM_FIELD_URI)
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON)
+    public Response deleteCustomFields(@PathParam(ID_PARAM_NAME) final String id,
+            @QueryParam(QUERY_CUSTOM_FIELDS) final String customFieldList,
+            @HeaderParam(HDR_CREATED_BY) final String createdBy,
+            @HeaderParam(HDR_REASON) final String reason,
+            @HeaderParam(HDR_COMMENT) final String comment) {
+        return super.deleteCustomFields(UUID.fromString(id), customFieldList,
+                context.createContext(createdBy, reason, comment));
+    }
+
+    @GET
+    @Path(TAG_URI)
+    @Produces(APPLICATION_JSON)
+    public Response getTags(@PathParam(ID_PARAM_NAME) String id) {
+        return super.getTags(UUID.fromString(id));
+    }
+
+    @POST
+    @Path(TAG_URI)
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON)
+    public Response createTags(@PathParam(ID_PARAM_NAME) final String id,
+            @QueryParam(QUERY_TAGS) final String tagList,
+            @HeaderParam(HDR_CREATED_BY) final String createdBy,
+            @HeaderParam(HDR_REASON) final String reason,
+            @HeaderParam(HDR_COMMENT) final String comment) {
+        return super.createTags(UUID.fromString(id), tagList,
+                                context.createContext(createdBy, reason, comment));
+    }
+
+    @DELETE
+    @Path(TAG_URI)
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON)
+    public Response deleteTags(@PathParam(ID_PARAM_NAME) final String id,
+            @QueryParam(QUERY_TAGS) final String tagList,
+            @HeaderParam(HDR_CREATED_BY) final String createdBy,
+            @HeaderParam(HDR_REASON) final String reason,
+            @HeaderParam(HDR_COMMENT) final String comment) {
+
+        return super.deleteTags(UUID.fromString(id), tagList,
+                                context.createContext(createdBy, reason, comment));
+    }
+
+    @Override
+    protected ObjectType getObjectType() {
+        return ObjectType.BUNDLE;
+    }
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java
index fd2ec57..0081a16 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java
@@ -23,6 +23,7 @@ import java.util.List;
 import java.util.UUID;
 
 import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
 import javax.ws.rs.DefaultValue;
 import javax.ws.rs.GET;
 import javax.ws.rs.HeaderParam;
@@ -35,6 +36,11 @@ import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 
+import com.ning.billing.jaxrs.json.CustomFieldJson;
+import com.ning.billing.jaxrs.util.TagHelper;
+import com.ning.billing.util.api.CustomFieldUserApi;
+import com.ning.billing.util.api.TagUserApi;
+import com.ning.billing.util.dao.ObjectType;
 import org.joda.time.DateTime;
 import org.joda.time.format.DateTimeFormatter;
 import org.joda.time.format.ISODateTimeFormat;
@@ -54,11 +60,12 @@ import com.ning.billing.jaxrs.util.Context;
 import com.ning.billing.jaxrs.util.JaxrsUriBuilder;
 
 
-@Path(BaseJaxrsResource.INVOICES_PATH)
-public class InvoiceResource implements BaseJaxrsResource {
-
-
+@Path(JaxrsResource.INVOICES_PATH)
+public class InvoiceResource extends JaxRsResourceBase {
     private static final Logger log = LoggerFactory.getLogger(InvoiceResource.class);
+    private static final String ID_PARAM_NAME = "invoiceId";
+    private static final String CUSTOM_FIELD_URI = JaxrsResource.CUSTOM_FIELDS + "/{" + ID_PARAM_NAME + ":" + UUID_PATTERN + "}";
+    private static final String TAG_URI = JaxrsResource.TAGS + "/{" + ID_PARAM_NAME + ":" + UUID_PATTERN + "}";
 
     private final DateTimeFormatter DATE_TIME_FORMATTER = ISODateTimeFormat.dateTime();
     
@@ -71,7 +78,11 @@ public class InvoiceResource implements BaseJaxrsResource {
     public InvoiceResource(final AccountUserApi accountApi,
             final InvoiceUserApi invoiceApi,
             final Context context,
-            final JaxrsUriBuilder uriBuilder) {
+            final JaxrsUriBuilder uriBuilder,
+            final TagUserApi tagUserApi,
+            final TagHelper tagHelper,
+            final CustomFieldUserApi customFieldUserApi) {
+        super(uriBuilder, tagUserApi, tagHelper, customFieldUserApi);
         this.accountApi = accountApi;
         this.invoiceApi = invoiceApi;
         this.context = context;
@@ -141,4 +152,76 @@ public class InvoiceResource implements BaseJaxrsResource {
             return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();            
         }
     }
+
+    @GET
+    @Path(CUSTOM_FIELD_URI)
+    @Produces(APPLICATION_JSON)
+    public Response getCustomFields(@PathParam(ID_PARAM_NAME) final String id) {
+        return super.getCustomFields(UUID.fromString(id));
+    }
+
+    @POST
+    @Path(CUSTOM_FIELD_URI)
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON)
+    public Response createCustomFields(@PathParam(ID_PARAM_NAME) final String id,
+            final List<CustomFieldJson> customFields,
+            @HeaderParam(HDR_CREATED_BY) final String createdBy,
+            @HeaderParam(HDR_REASON) final String reason,
+            @HeaderParam(HDR_COMMENT) final String comment) {
+        return super.createCustomFields(UUID.fromString(id), customFields,
+                context.createContext(createdBy, reason, comment));
+    }
+
+    @DELETE
+    @Path(CUSTOM_FIELD_URI)
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON)
+    public Response deleteCustomFields(@PathParam(ID_PARAM_NAME) final String id,
+            @QueryParam(QUERY_CUSTOM_FIELDS) final String customFieldList,
+            @HeaderParam(HDR_CREATED_BY) final String createdBy,
+            @HeaderParam(HDR_REASON) final String reason,
+            @HeaderParam(HDR_COMMENT) final String comment) {
+        return super.deleteCustomFields(UUID.fromString(id), customFieldList,
+                context.createContext(createdBy, reason, comment));
+    }
+
+    @GET
+    @Path(TAG_URI)
+    @Produces(APPLICATION_JSON)
+    public Response getTags(@PathParam(ID_PARAM_NAME) String id) {
+        return super.getTags(UUID.fromString(id));
+    }
+
+    @POST
+    @Path(TAG_URI)
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON)
+    public Response createTags(@PathParam(ID_PARAM_NAME) final String id,
+            @QueryParam(QUERY_TAGS) final String tagList,
+            @HeaderParam(HDR_CREATED_BY) final String createdBy,
+            @HeaderParam(HDR_REASON) final String reason,
+            @HeaderParam(HDR_COMMENT) final String comment) {
+        return super.createTags(UUID.fromString(id), tagList,
+                                context.createContext(createdBy, reason, comment));
+    }
+
+    @DELETE
+    @Path(TAG_URI)
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON)
+    public Response deleteTags(@PathParam(ID_PARAM_NAME) final String id,
+            @QueryParam(QUERY_TAGS) final String tagList,
+            @HeaderParam(HDR_CREATED_BY) final String createdBy,
+            @HeaderParam(HDR_REASON) final String reason,
+            @HeaderParam(HDR_COMMENT) final String comment) {
+
+        return super.deleteTags(UUID.fromString(id), tagList,
+                                context.createContext(createdBy, reason, comment));
+    }
+
+    @Override
+    protected ObjectType getObjectType() {
+        return ObjectType.INVOICE;
+    }
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxRsResourceBase.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxRsResourceBase.java
new file mode 100644
index 0000000..a302336
--- /dev/null
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxRsResourceBase.java
@@ -0,0 +1,151 @@
+/*
+ * 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.jaxrs.resources;
+
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Collections2;
+import com.ning.billing.jaxrs.json.CustomFieldJson;
+import com.ning.billing.jaxrs.util.JaxrsUriBuilder;
+import com.ning.billing.jaxrs.util.TagHelper;
+import com.ning.billing.util.api.CustomFieldUserApi;
+import com.ning.billing.util.api.TagDefinitionApiException;
+import com.ning.billing.util.api.TagUserApi;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.customfield.CustomField;
+import com.ning.billing.util.customfield.StringCustomField;
+import com.ning.billing.util.dao.ObjectType;
+import com.ning.billing.util.tag.Tag;
+import com.ning.billing.util.tag.TagDefinition;
+
+import javax.ws.rs.core.Response;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+public abstract class JaxRsResourceBase implements JaxrsResource {
+    private final JaxrsUriBuilder uriBuilder;
+    private final TagUserApi tagUserApi;
+    private final TagHelper tagHelper;
+    private final CustomFieldUserApi customFieldUserApi;
+
+    protected abstract ObjectType getObjectType();
+
+    public JaxRsResourceBase(final JaxrsUriBuilder uriBuilder,
+                             final TagUserApi tagUserApi,
+                             final TagHelper tagHelper,
+                             final CustomFieldUserApi customFieldUserApi) {
+        this.uriBuilder = uriBuilder;
+        this.tagUserApi = tagUserApi;
+        this.tagHelper = tagHelper;
+        this.customFieldUserApi = customFieldUserApi;
+    }
+
+    protected Response getTags(final UUID id) {
+        Map<String, Tag> tags = tagUserApi.getTags(id, getObjectType());
+        Collection<String> tagNameList = (tags.size() == 0) ?
+                Collections.<String>emptyList() :
+            Collections2.transform(tags.values(), new Function<Tag, String>() {
+                @Override
+                public String apply(Tag input) {
+                    return input.getTagDefinitionName();
+                }
+            });
+        return Response.status(Response.Status.OK).entity(tagNameList).build();
+    }
+
+    protected Response createTags(final UUID id,
+                                  final String tagList,
+                                  final CallContext context) {
+        try {
+            Preconditions.checkNotNull(tagList, "Query % list cannot be null", JaxrsResource.QUERY_TAGS);
+
+            List<TagDefinition> input = tagHelper.getTagDefinitionFromTagList(tagList);
+            tagUserApi.addTags(id, getObjectType(), input, context);
+
+            return uriBuilder.buildResponse(this.getClass(), "createTags", id);
+        } catch (IllegalArgumentException e) {
+            return Response.status(Response.Status.BAD_REQUEST).entity(e.getMessage()).build();
+        } catch (NullPointerException e) {
+            return Response.status(Response.Status.BAD_REQUEST).entity(e.getMessage()).build();
+        } catch (TagDefinitionApiException e) {
+            return Response.status(Response.Status.BAD_REQUEST).entity(e.getMessage()).build();
+        }
+    }
+
+    protected Response deleteTags(final UUID id,
+                                  final String tagList,
+                                  final CallContext context) {
+
+        try {
+            List<TagDefinition> input = tagHelper.getTagDefinitionFromTagList(tagList);
+            tagUserApi.removeTags(id, getObjectType(), input, context);
+
+            return Response.status(Response.Status.OK).build();
+        } catch (IllegalArgumentException e) {
+            return Response.status(Response.Status.BAD_REQUEST).entity(e.getMessage()).build();
+        } catch (NullPointerException e) {
+            return Response.status(Response.Status.BAD_REQUEST).entity(e.getMessage()).build();
+        } catch (TagDefinitionApiException e) {
+            return Response.status(Response.Status.BAD_REQUEST).entity(e.getMessage()).build();
+        }
+    }
+
+    protected Response getCustomFields(final UUID id) {
+        Map<String, CustomField> fields = customFieldUserApi.getCustomFields(id, getObjectType());
+
+        List<CustomFieldJson> result = new LinkedList<CustomFieldJson>();
+        for (CustomField cur : fields.values()) {
+            result.add(new CustomFieldJson(cur));
+        }
+        return Response.status(Response.Status.OK).entity(result).build();
+    }
+
+    protected Response createCustomFields(final UUID id,
+                                          final List<CustomFieldJson> customFields,
+                                          final CallContext context) {
+        try {
+            LinkedList<CustomField> input = new LinkedList<CustomField>();
+            for (CustomFieldJson cur : customFields) {
+                input.add(new StringCustomField(cur.getName(), cur.getValue()));
+            }
+
+            customFieldUserApi.saveCustomFields(id, getObjectType(), input, context);
+            return uriBuilder.buildResponse(this.getClass(), "createCustomFields", id);
+        } catch (IllegalArgumentException e) {
+            return Response.status(Response.Status.BAD_REQUEST).entity(e.getMessage()).build();
+        } catch (NullPointerException e) {
+            return Response.status(Response.Status.BAD_REQUEST).entity(e.getMessage()).build();
+        }
+    }
+
+    protected Response deleteCustomFields(final UUID id,
+                                          final String customFieldList,
+                                          final CallContext context) {
+        try {
+            // STEPH missing API to delete custom fields
+            return Response.status(Response.Status.OK).build();
+        } catch (IllegalArgumentException e) {
+            return Response.status(Response.Status.BAD_REQUEST).entity(e.getMessage()).build();
+        } catch (NullPointerException e) {
+            return Response.status(Response.Status.BAD_REQUEST).entity(e.getMessage()).build();
+        }
+    }
+}
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentResource.java
index 2988f5a..5736bc0 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentResource.java
@@ -19,7 +19,9 @@ package com.ning.billing.jaxrs.resources;
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 
 import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
 import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
@@ -28,12 +30,34 @@ import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 
+import com.google.inject.Inject;
+import com.ning.billing.jaxrs.json.CustomFieldJson;
 import com.ning.billing.jaxrs.json.PaymentJsonSimple;
+import com.ning.billing.jaxrs.util.Context;
+import com.ning.billing.jaxrs.util.JaxrsUriBuilder;
+import com.ning.billing.jaxrs.util.TagHelper;
+import com.ning.billing.util.api.CustomFieldUserApi;
+import com.ning.billing.util.api.TagUserApi;
+import com.ning.billing.util.dao.ObjectType;
 
+import java.util.List;
+import java.util.UUID;
 
 @Path("/1.0/payment")
-public class PaymentResource {
+public class PaymentResource extends JaxRsResourceBase {
+    private static final String ID_PARAM_NAME = "paymentId";
+    private static final String CUSTOM_FIELD_URI = JaxrsResource.CUSTOM_FIELDS + "/{" + ID_PARAM_NAME + ":" + UUID_PATTERN + "}";
+    private static final String TAG_URI = JaxrsResource.TAGS + "/{" + ID_PARAM_NAME + ":" + UUID_PATTERN + "}";
 
+    private final Context context;
+
+    @Inject
+    public PaymentResource(final JaxrsUriBuilder uriBuilder, final TagUserApi tagUserApi,
+                           final TagHelper tagHelper, final CustomFieldUserApi customFieldUserApi,
+                           final Context context) {
+        super(uriBuilder, tagUserApi, tagHelper, customFieldUserApi);
+        this.context = context;
+    }
 
     @GET
     @Path("/{invoiceId:\\w+-\\w+-\\w+-\\w+-\\w+}")
@@ -59,4 +83,76 @@ public class PaymentResource {
             @QueryParam("nameOnCC") String nameOnCC) {
         return Response.status(Status.INTERNAL_SERVER_ERROR).build();
     }
+
+    @GET
+    @Path(CUSTOM_FIELD_URI)
+    @Produces(APPLICATION_JSON)
+    public Response getCustomFields(@PathParam(ID_PARAM_NAME) final String id) {
+        return super.getCustomFields(UUID.fromString(id));
+    }
+
+    @POST
+    @Path(CUSTOM_FIELD_URI)
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON)
+    public Response createCustomFields(@PathParam(ID_PARAM_NAME) final String id,
+            final List<CustomFieldJson> customFields,
+            @HeaderParam(HDR_CREATED_BY) final String createdBy,
+            @HeaderParam(HDR_REASON) final String reason,
+            @HeaderParam(HDR_COMMENT) final String comment) {
+        return super.createCustomFields(UUID.fromString(id), customFields,
+                context.createContext(createdBy, reason, comment));
+    }
+
+    @DELETE
+    @Path(CUSTOM_FIELD_URI)
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON)
+    public Response deleteCustomFields(@PathParam(ID_PARAM_NAME) final String id,
+            @QueryParam(QUERY_CUSTOM_FIELDS) final String customFieldList,
+            @HeaderParam(HDR_CREATED_BY) final String createdBy,
+            @HeaderParam(HDR_REASON) final String reason,
+            @HeaderParam(HDR_COMMENT) final String comment) {
+        return super.deleteCustomFields(UUID.fromString(id), customFieldList,
+                context.createContext(createdBy, reason, comment));
+    }
+
+    @GET
+    @Path(TAG_URI)
+    @Produces(APPLICATION_JSON)
+    public Response getTags(@PathParam(ID_PARAM_NAME) String id) {
+        return super.getTags(UUID.fromString(id));
+    }
+
+    @POST
+    @Path(TAG_URI)
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON)
+    public Response createTags(@PathParam(ID_PARAM_NAME) final String id,
+            @QueryParam(QUERY_TAGS) final String tagList,
+            @HeaderParam(HDR_CREATED_BY) final String createdBy,
+            @HeaderParam(HDR_REASON) final String reason,
+            @HeaderParam(HDR_COMMENT) final String comment) {
+        return super.createTags(UUID.fromString(id), tagList,
+                                context.createContext(createdBy, reason, comment));
+    }
+
+    @DELETE
+    @Path(TAG_URI)
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON)
+    public Response deleteTags(@PathParam(ID_PARAM_NAME) final String id,
+            @QueryParam(QUERY_TAGS) final String tagList,
+            @HeaderParam(HDR_CREATED_BY) final String createdBy,
+            @HeaderParam(HDR_REASON) final String reason,
+            @HeaderParam(HDR_COMMENT) final String comment) {
+
+        return super.deleteTags(UUID.fromString(id), tagList,
+                                context.createContext(createdBy, reason, comment));
+    }
+
+    @Override
+    protected ObjectType getObjectType() {
+        return ObjectType.PAYMENT;
+    }
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/SubscriptionResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/SubscriptionResource.java
index 67c5b5c..88b2de7 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/SubscriptionResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/SubscriptionResource.java
@@ -19,6 +19,7 @@ package com.ning.billing.jaxrs.resources;
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 
 import java.math.BigDecimal;
+import java.util.List;
 import java.util.UUID;
 import java.util.concurrent.TimeoutException;
 
@@ -36,6 +37,11 @@ import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 
+import com.ning.billing.jaxrs.json.CustomFieldJson;
+import com.ning.billing.jaxrs.util.TagHelper;
+import com.ning.billing.util.api.CustomFieldUserApi;
+import com.ning.billing.util.api.TagUserApi;
+import com.ning.billing.util.dao.ObjectType;
 import org.joda.time.DateTime;
 import org.joda.time.format.DateTimeFormatter;
 import org.joda.time.format.ISODateTimeFormat;
@@ -60,13 +66,14 @@ import com.ning.billing.jaxrs.util.KillbillEventHandler;
 import com.ning.billing.payment.api.PaymentErrorEvent;
 import com.ning.billing.payment.api.PaymentInfoEvent;
 import com.ning.billing.util.callcontext.CallContext;
-import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.userrequest.CompletionUserRequestBase;
 
-@Path(BaseJaxrsResource.SUBSCRIPTIONS_PATH)
-public class SubscriptionResource implements BaseJaxrsResource {
-
+@Path(JaxrsResource.SUBSCRIPTIONS_PATH)
+public class SubscriptionResource extends JaxRsResourceBase {
     private static final Logger log = LoggerFactory.getLogger(SubscriptionResource.class);
+    private static final String ID_PARAM_NAME = "subscriptionId";
+    private static final String CUSTOM_FIELD_URI = JaxrsResource.CUSTOM_FIELDS + "/{" + ID_PARAM_NAME + ":" + UUID_PATTERN + "}";
+    private static final String TAG_URI = JaxrsResource.TAGS + "/{" + ID_PARAM_NAME + ":" + UUID_PATTERN + "}";
 
     private final DateTimeFormatter DATE_TIME_FORMATTER = ISODateTimeFormat.dateTime();
 
@@ -77,7 +84,9 @@ public class SubscriptionResource implements BaseJaxrsResource {
     
     @Inject
     public SubscriptionResource(final JaxrsUriBuilder uriBuilder, final EntitlementUserApi entitlementApi,
-            final Clock clock, final Context context, final KillbillEventHandler killbillHandler) {
+            final Context context, final KillbillEventHandler killbillHandler,
+            final TagUserApi tagUserApi, final TagHelper tagHelper, final CustomFieldUserApi customFieldUserApi) {
+        super(uriBuilder, tagUserApi, tagHelper, customFieldUserApi);
         this.uriBuilder = uriBuilder;
         this.entitlementApi = entitlementApi;
         this.context = context;
@@ -342,4 +351,76 @@ public class SubscriptionResource implements BaseJaxrsResource {
             }
         }
     }
+
+    @GET
+    @Path(CUSTOM_FIELD_URI)
+    @Produces(APPLICATION_JSON)
+    public Response getCustomFields(@PathParam(ID_PARAM_NAME) final String id) {
+        return super.getCustomFields(UUID.fromString(id));
+    }
+
+    @POST
+    @Path(CUSTOM_FIELD_URI)
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON)
+    public Response createCustomFields(@PathParam(ID_PARAM_NAME) final String id,
+            final List<CustomFieldJson> customFields,
+            @HeaderParam(HDR_CREATED_BY) final String createdBy,
+            @HeaderParam(HDR_REASON) final String reason,
+            @HeaderParam(HDR_COMMENT) final String comment) {
+        return super.createCustomFields(UUID.fromString(id), customFields,
+                context.createContext(createdBy, reason, comment));
+    }
+
+    @DELETE
+    @Path(CUSTOM_FIELD_URI)
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON)
+    public Response deleteCustomFields(@PathParam(ID_PARAM_NAME) final String id,
+            @QueryParam(QUERY_CUSTOM_FIELDS) final String customFieldList,
+            @HeaderParam(HDR_CREATED_BY) final String createdBy,
+            @HeaderParam(HDR_REASON) final String reason,
+            @HeaderParam(HDR_COMMENT) final String comment) {
+        return super.deleteCustomFields(UUID.fromString(id), customFieldList,
+                context.createContext(createdBy, reason, comment));
+    }
+
+    @GET
+    @Path(TAG_URI)
+    @Produces(APPLICATION_JSON)
+    public Response getTags(@PathParam(ID_PARAM_NAME) String id) {
+        return super.getTags(UUID.fromString(id));
+    }
+
+    @POST
+    @Path(TAG_URI)
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON)
+    public Response createTags(@PathParam(ID_PARAM_NAME) final String id,
+            @QueryParam(QUERY_TAGS) final String tagList,
+            @HeaderParam(HDR_CREATED_BY) final String createdBy,
+            @HeaderParam(HDR_REASON) final String reason,
+            @HeaderParam(HDR_COMMENT) final String comment) {
+        return super.createTags(UUID.fromString(id), tagList,
+                                context.createContext(createdBy, reason, comment));
+    }
+
+    @DELETE
+    @Path(TAG_URI)
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON)
+    public Response deleteTags(@PathParam(ID_PARAM_NAME) final String id,
+            @QueryParam(QUERY_TAGS) final String tagList,
+            @HeaderParam(HDR_CREATED_BY) final String createdBy,
+            @HeaderParam(HDR_REASON) final String reason,
+            @HeaderParam(HDR_COMMENT) final String comment) {
+
+        return super.deleteTags(UUID.fromString(id), tagList,
+                                context.createContext(createdBy, reason, comment));
+    }
+
+    @Override
+    protected ObjectType getObjectType() {
+        return ObjectType.SUBSCRIPTION;
+    }
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/TagResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/TagResource.java
index 3111240..0e3853c 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/TagResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/TagResource.java
@@ -42,8 +42,8 @@ import com.ning.billing.util.api.TagUserApi;
 import com.ning.billing.util.tag.TagDefinition;
 
 @Singleton
-@Path(BaseJaxrsResource.TAG_DEFINITIONS_PATH)
-public class TagResource implements BaseJaxrsResource {
+@Path(JaxrsResource.TAG_DEFINITIONS_PATH)
+public class TagResource implements JaxrsResource {
     
     private final TagUserApi tagUserApi;
     private final Context context;
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/util/Context.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/util/Context.java
index 729f166..f6b7d67 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/util/Context.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/util/Context.java
@@ -19,7 +19,7 @@ import java.util.UUID;
 
 import com.google.common.base.Preconditions;
 import com.google.inject.Inject;
-import com.ning.billing.jaxrs.resources.BaseJaxrsResource;
+import com.ning.billing.jaxrs.resources.JaxrsResource;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.CallContextFactory;
 import com.ning.billing.util.callcontext.CallOrigin;
@@ -42,7 +42,7 @@ public class Context {
     public CallContext createContext(final String createdBy, final String reason, final String comment)
     throws IllegalArgumentException {
         try {
-            Preconditions.checkNotNull(createdBy, String.format("Header %s needs to be set", BaseJaxrsResource.HDR_CREATED_BY));
+            Preconditions.checkNotNull(createdBy, String.format("Header %s needs to be set", JaxrsResource.HDR_CREATED_BY));
             return contextFactory.createCallContext(createdBy, origin, userType, UUID.randomUUID());
         } catch (NullPointerException e) {
             throw new IllegalArgumentException(e.getMessage());
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/util/JaxrsUriBuilder.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/util/JaxrsUriBuilder.java
index b351b4c..0917106 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/util/JaxrsUriBuilder.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/util/JaxrsUriBuilder.java
@@ -16,17 +16,16 @@
 package com.ning.billing.jaxrs.util;
 
 import java.net.URI;
-import java.util.UUID;
 
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriBuilder;
 
-import com.ning.billing.jaxrs.resources.BaseJaxrsResource;
+import com.ning.billing.jaxrs.resources.JaxrsResource;
 
 public class JaxrsUriBuilder {
 
 	
-	public Response buildResponse(final Class<? extends BaseJaxrsResource> theClass, final String getMethodName, final Object objectId) {
+	public Response buildResponse(final Class<? extends JaxrsResource> theClass, final String getMethodName, final Object objectId) {
 		URI uri = UriBuilder.fromPath(objectId.toString()).build();
 		Response.ResponseBuilder ri = Response.created(uri);
 		return ri.entity(new Object() {
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestAccount.java b/server/src/test/java/com/ning/billing/jaxrs/TestAccount.java
index b24d7d7..94f2fb8 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestAccount.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestAccount.java
@@ -23,11 +23,13 @@ import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.UUID;
 
 
 import javax.ws.rs.core.Response.Status;
 
 
+import com.ning.billing.jaxrs.resources.JaxrsResource;
 import org.joda.time.DateTime;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -43,16 +45,11 @@ import com.ning.billing.jaxrs.json.BundleJsonNoSubscriptions;
 import com.ning.billing.jaxrs.json.CustomFieldJson;
 import com.ning.billing.jaxrs.json.SubscriptionJsonNoEvents;
 import com.ning.billing.jaxrs.json.TagDefinitionJson;
-import com.ning.billing.jaxrs.resources.BaseJaxrsResource;
 import com.ning.http.client.Response;
 
 
 public class TestAccount extends TestJaxrsBase {
-
 	private static final Logger log = LoggerFactory.getLogger(TestAccount.class);
-
-
-	
 	
 	@Test(groups="slow", enabled=true)
 	public void testAccountOk() throws Exception {
@@ -61,8 +58,8 @@ public class TestAccount extends TestJaxrsBase {
 		
 		// Retrieves by external key
 		Map<String, String> queryParams = new HashMap<String, String>();
-		queryParams.put(BaseJaxrsResource.QUERY_EXTERNAL_KEY, "shdgfhwe");
-		Response response = doGet(BaseJaxrsResource.ACCOUNTS_PATH, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
+		queryParams.put(JaxrsResource.QUERY_EXTERNAL_KEY, "shdgfhwe");
+		Response response = doGet(JaxrsResource.ACCOUNTS_PATH, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
 		Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
 		String baseJson = response.getResponseBody();
 		AccountJson objFromJson = mapper.readValue(baseJson, AccountJson.class);
@@ -72,7 +69,7 @@ public class TestAccount extends TestJaxrsBase {
 		AccountJson newInput = new AccountJson(objFromJson.getAccountId(),
 				"zozo", 4, objFromJson.getExternalKey(), "rr@google.com", 18, "EUR", "none", "UTC", "bl1", "bh2", "", "ca", "usa", "415-255-2991");
 		baseJson = mapper.writeValueAsString(newInput);
-		final String uri = BaseJaxrsResource.ACCOUNTS_PATH + "/" + objFromJson.getAccountId();
+		final String uri = JaxrsResource.ACCOUNTS_PATH + "/" + objFromJson.getAccountId();
 		response = doPut(uri, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
 		Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
 		baseJson = response.getResponseBody();
@@ -85,7 +82,7 @@ public class TestAccount extends TestJaxrsBase {
 	public void testUpdateNonExistentAccount() throws Exception {
 		AccountJson input = getAccountJson("xoxo", "shghaahwe", "xoxo@yahoo.com");
 		String baseJson = mapper.writeValueAsString(input);
-		final String uri = BaseJaxrsResource.ACCOUNTS_PATH + "/" + input.getAccountId();
+		final String uri = JaxrsResource.ACCOUNTS_PATH + "/" + input.getAccountId();
 		Response response = doPut(uri, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
 		Assert.assertEquals(response.getStatusCode(), Status.NO_CONTENT.getStatusCode());
 		String body = response.getResponseBody();
@@ -95,14 +92,14 @@ public class TestAccount extends TestJaxrsBase {
 	
 	@Test(groups="slow", enabled=true)
 	public void testAccountNonExistent() throws Exception {
-		final String uri = BaseJaxrsResource.ACCOUNTS_PATH + "/99999999-b103-42f3-8b6e-dd244f1d0747";
+		final String uri = JaxrsResource.ACCOUNTS_PATH + "/99999999-b103-42f3-8b6e-dd244f1d0747";
 		Response response = doGet(uri, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
 		Assert.assertEquals(response.getStatusCode(), Status.NO_CONTENT.getStatusCode());
 	}
 	
 	@Test(groups="slow", enabled=true)
 	public void testAccountBadAccountId() throws Exception {
-		final String uri = BaseJaxrsResource.ACCOUNTS_PATH + "/yo";
+		final String uri = JaxrsResource.ACCOUNTS_PATH + "/yo";
 		Response response = doGet(uri, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
 		Assert.assertEquals(response.getStatusCode(), Status.NOT_FOUND.getStatusCode());
 	}
@@ -128,7 +125,7 @@ public class TestAccount extends TestJaxrsBase {
         crappyWaitForLackOfProperSynchonization();
         
         
-        final String uri = BaseJaxrsResource.ACCOUNTS_PATH + "/" + accountJson.getAccountId() + "/" + BaseJaxrsResource.TIMELINE;
+        final String uri = JaxrsResource.ACCOUNTS_PATH + "/" + accountJson.getAccountId() + "/" + JaxrsResource.TIMELINE;
         
         Response response = doGet(uri, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
         Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
@@ -144,58 +141,56 @@ public class TestAccount extends TestJaxrsBase {
         Assert.assertEquals(objFromJson.getBundles().get(0).getSubscriptions().get(0).getEvents().size(), 2);        
  	}
 
-	@Test(groups="slow", enabled=false)
-	public void testAccountWithTags() throws Exception {
+
+
+
+
+    protected String getRootPath() {
+        return JaxrsResource.ACCOUNTS_PATH;
+    }
+
+	@Test(groups="slow", enabled=true)
+	public void testTags() throws Exception {
 	    //Create Tag definition
 	    TagDefinitionJson input = new TagDefinitionJson("yoyo", "nothing more to say");
 	    String baseJson = mapper.writeValueAsString(input);
-	    Response response = doPost(BaseJaxrsResource.TAG_DEFINITIONS_PATH, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+	    Response response = doPost(JaxrsResource.TAG_DEFINITIONS_PATH, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
 	    assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
-	    
-	    AccountJson accountJson = createAccount("couroucoucou", "shdwdsqgfhwe", "couroucoucou@yahoo.com");
-	    assertNotNull(accountJson);
 	        
 	    Map<String, String> queryParams = new HashMap<String, String>();
-        queryParams.put(BaseJaxrsResource.QUERY_TAGS, input.getName());
-        String uri = BaseJaxrsResource.ACCOUNTS_PATH + "/" + BaseJaxrsResource.TAGS + "/" + accountJson.getAccountId() ;
+        queryParams.put(JaxrsResource.QUERY_TAGS, input.getName());
+        String uri = getRootPath() + "/" + JaxrsResource.TAGS + "/" + UUID.randomUUID().toString();
 	    response = doPost(uri, null, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
         assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
-        
+
         /*
          * STEPH Some how Location returns the ID twice (confused) :
          * Location: http://127.0.0.1:8080/1.0/kb/accounts/tags/ebb5f830-6f0a-4521-9553-521d173169be/ebb5f830-6f0a-4521-9553-521d173169be
-         * 
-        String location = response.getHeader("Location");
-        Assert.assertNotNull(location);
+         */
 
         // Retrieves by Id based on Location returned
-        response = doGetWithUrl(location, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        String url = getUrlFromUri(uri);
+        response = doGetWithUrl(url, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
         Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
-        */
+
 
 	}
-	
-    @Test(groups="slow", enabled=false)
-	public void testAccountWithCustomFields() throws Exception {
-        
-        AccountJson accountJson = createAccount("carafe", "shdwhwgaz", "carafe@yahoo.com");
-        assertNotNull(accountJson);
-        
+
+    @Test(groups="slow", enabled=true)
+	public void testCustomFields() throws Exception {
         List<CustomFieldJson> customFields =  new LinkedList<CustomFieldJson>();
         customFields.add(new CustomFieldJson("1", "value1"));
         customFields.add(new CustomFieldJson("2", "value2"));
         customFields.add(new CustomFieldJson("3", "value3"));  
         String baseJson = mapper.writeValueAsString(customFields);
 
-        String uri = BaseJaxrsResource.ACCOUNTS_PATH + "/" + BaseJaxrsResource.CUSTOM_FIELDS + "/" + accountJson.getAccountId() ;
-        Response response = doPost(uri,baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        String uri = getRootPath() + "/" + JaxrsResource.CUSTOM_FIELDS + "/" + UUID.randomUUID().toString();
+        Response response = doPost(uri, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
         assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
-        String location = response.getHeader("Location");
-        Assert.assertNotNull(location);
 
         // Retrieves by Id based on Location returned
-        response = doGetWithUrl(location, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        String url = getUrlFromUri(uri);
+        response = doGetWithUrl(url, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
         Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
-        
 	}
 }
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestBundle.java b/server/src/test/java/com/ning/billing/jaxrs/TestBundle.java
index 3342bad..b15c8ea 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestBundle.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestBundle.java
@@ -31,7 +31,7 @@ import org.testng.annotations.Test;
 import com.fasterxml.jackson.core.type.TypeReference;
 import com.ning.billing.jaxrs.json.AccountJson;
 import com.ning.billing.jaxrs.json.BundleJsonNoSubscriptions;
-import com.ning.billing.jaxrs.resources.BaseJaxrsResource;
+import com.ning.billing.jaxrs.resources.JaxrsResource;
 import com.ning.http.client.Response;
 
 public class TestBundle extends TestJaxrsBase {
@@ -48,8 +48,8 @@ public class TestBundle extends TestJaxrsBase {
 		
 		// Retrieves by external key
 		Map<String, String> queryParams = new HashMap<String, String>();
-		queryParams.put(BaseJaxrsResource.QUERY_EXTERNAL_KEY, "12345");
-		Response response = doGet(BaseJaxrsResource.BUNDLES_PATH, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
+		queryParams.put(JaxrsResource.QUERY_EXTERNAL_KEY, "12345");
+		Response response = doGet(JaxrsResource.BUNDLES_PATH, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
 		Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
 		String baseJson = response.getResponseBody();
 		BundleJsonNoSubscriptions objFromJson = mapper.readValue(baseJson, BundleJsonNoSubscriptions.class);
@@ -64,7 +64,7 @@ public class TestBundle extends TestJaxrsBase {
 		BundleJsonNoSubscriptions bundleJson1 = createBundle(accountJson.getAccountId(), "156567");
 		BundleJsonNoSubscriptions bundleJson2 = createBundle(accountJson.getAccountId(), "265658");
 
-		String uri = BaseJaxrsResource.ACCOUNTS_PATH + "/" + accountJson.getAccountId().toString() + "/" + BaseJaxrsResource.BUNDLES;
+		String uri = JaxrsResource.ACCOUNTS_PATH + "/" + accountJson.getAccountId().toString() + "/" + JaxrsResource.BUNDLES;
 		Response response = doGet(uri, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
 		Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
 		String baseJson = response.getResponseBody();
@@ -84,19 +84,19 @@ public class TestBundle extends TestJaxrsBase {
 	public void testBundleNonExistent() throws Exception {
 		AccountJson accountJson = createAccount("dfdf", "dfdfgfhkkl", "dfdf@yahoo.com");	
 		
-		String uri = BaseJaxrsResource.BUNDLES_PATH + "/99999999-b103-42f3-8b6e-dd244f1d0747";
+		String uri = JaxrsResource.BUNDLES_PATH + "/99999999-b103-42f3-8b6e-dd244f1d0747";
 		Response response = doGet(uri, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
 		Assert.assertEquals(response.getStatusCode(), Status.NO_CONTENT.getStatusCode());
 		
 		
 		// Retrieves by external key
 		Map<String, String> queryParams = new HashMap<String, String>();
-		queryParams.put(BaseJaxrsResource.QUERY_EXTERNAL_KEY, "56566");
-		response = doGet(BaseJaxrsResource.BUNDLES_PATH, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
+		queryParams.put(JaxrsResource.QUERY_EXTERNAL_KEY, "56566");
+		response = doGet(JaxrsResource.BUNDLES_PATH, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
 		Assert.assertEquals(response.getStatusCode(), Status.NO_CONTENT.getStatusCode());
 		
 		
-		uri = BaseJaxrsResource.ACCOUNTS_PATH + "/" + accountJson.getAccountId().toString() + "/" + BaseJaxrsResource.BUNDLES;
+		uri = JaxrsResource.ACCOUNTS_PATH + "/" + accountJson.getAccountId().toString() + "/" + JaxrsResource.BUNDLES;
 		response = doGet(uri, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
 		Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
 		String baseJson = response.getResponseBody();
@@ -107,7 +107,7 @@ public class TestBundle extends TestJaxrsBase {
 
 	@Test(groups="slow", enabled=true)
 	public void testAppNonExistent() throws Exception {
-		String uri = BaseJaxrsResource.ACCOUNTS_PATH + "/99999999-b103-42f3-8b6e-dd244f1d0747/" + BaseJaxrsResource.BUNDLES;
+		String uri = JaxrsResource.ACCOUNTS_PATH + "/99999999-b103-42f3-8b6e-dd244f1d0747/" + JaxrsResource.BUNDLES;
 		Response response = doGet(uri, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
 		Assert.assertEquals(response.getStatusCode(), Status.NO_CONTENT.getStatusCode());	
 	}
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestInvoice.java b/server/src/test/java/com/ning/billing/jaxrs/TestInvoice.java
index 6aa0224..6c0878b 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestInvoice.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestInvoice.java
@@ -24,6 +24,7 @@ import java.util.Map;
 
 import javax.ws.rs.core.Response.Status;
 
+import com.ning.billing.jaxrs.resources.JaxrsResource;
 import org.joda.time.DateTime;
 import org.joda.time.Interval;
 import org.joda.time.format.DateTimeFormatter;
@@ -40,7 +41,6 @@ import com.ning.billing.jaxrs.json.AccountJson;
 import com.ning.billing.jaxrs.json.BundleJsonNoSubscriptions;
 import com.ning.billing.jaxrs.json.InvoiceJsonSimple;
 import com.ning.billing.jaxrs.json.SubscriptionJsonNoEvents;
-import com.ning.billing.jaxrs.resources.BaseJaxrsResource;
 import com.ning.http.client.Response;
 
 public class TestInvoice extends TestJaxrsBase  {
@@ -72,9 +72,9 @@ public class TestInvoice extends TestJaxrsBase  {
 
         crappyWaitForLackOfProperSynchonization();
         
-        String uri = BaseJaxrsResource.INVOICES_PATH;
+        String uri = JaxrsResource.INVOICES_PATH;
         Map<String, String> queryParams = new HashMap<String, String>();
-        queryParams.put(BaseJaxrsResource.QUERY_ACCOUNT_ID, accountJson.getAccountId());
+        queryParams.put(JaxrsResource.QUERY_ACCOUNT_ID, accountJson.getAccountId());
         
         Response response = doGet(uri, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
         Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
@@ -85,7 +85,7 @@ public class TestInvoice extends TestJaxrsBase  {
         assertEquals(objFromJson.size(), 4);
         
         // Check we can retrieve an individual invoice
-        uri = BaseJaxrsResource.INVOICES_PATH + "/" + objFromJson.get(0).getInvoiceId();
+        uri = JaxrsResource.INVOICES_PATH + "/" + objFromJson.get(0).getInvoiceId();
         response = doGet(uri, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
         Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());        
         baseJson = response.getResponseBody();
@@ -95,9 +95,9 @@ public class TestInvoice extends TestJaxrsBase  {
         
         // Then create a dryRun Invoice
         DateTime futureDate = clock.getUTCNow().plusMonths(1).plusDays(3);
-        uri = BaseJaxrsResource.INVOICES_PATH;
-        queryParams.put(BaseJaxrsResource.QUERY_TARGET_DATE, futureDate.toString());
-        queryParams.put(BaseJaxrsResource.QUERY_DRY_RUN, "true");        
+        uri = JaxrsResource.INVOICES_PATH;
+        queryParams.put(JaxrsResource.QUERY_TARGET_DATE, futureDate.toString());
+        queryParams.put(JaxrsResource.QUERY_DRY_RUN, "true");
         response = doPost(uri, null, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
         Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode()); 
         baseJson = response.getResponseBody();
@@ -106,7 +106,7 @@ public class TestInvoice extends TestJaxrsBase  {
         log.info(baseJson);
         
         // The one more time with no DryRun
-        queryParams.remove(BaseJaxrsResource.QUERY_DRY_RUN);
+        queryParams.remove(JaxrsResource.QUERY_DRY_RUN);
         response = doPost(uri, null, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
         Assert.assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
         
@@ -114,7 +114,7 @@ public class TestInvoice extends TestJaxrsBase  {
         Assert.assertNotNull(location);
         
         // Check again # invoices, should be 5 this time
-        uri = BaseJaxrsResource.INVOICES_PATH;
+        uri = JaxrsResource.INVOICES_PATH;
         response = doGet(uri, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
         assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
         baseJson = response.getResponseBody();
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java b/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
index 1abb582..a09bf1a 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
@@ -30,6 +30,7 @@ import java.util.concurrent.TimeUnit;
 
 import javax.ws.rs.core.Response.Status;
 
+import com.ning.billing.jaxrs.resources.JaxrsResource;
 import com.ning.billing.util.email.EmailModule;
 import com.ning.billing.util.email.templates.TemplateModule;
 import com.ning.billing.util.glue.GlobalLockerModule;
@@ -65,7 +66,6 @@ import com.ning.billing.invoice.glue.DefaultInvoiceModule;
 import com.ning.billing.jaxrs.json.AccountJson;
 import com.ning.billing.jaxrs.json.BundleJsonNoSubscriptions;
 import com.ning.billing.jaxrs.json.SubscriptionJsonNoEvents;
-import com.ning.billing.jaxrs.resources.BaseJaxrsResource;
 import com.ning.billing.junction.glue.DefaultJunctionModule;
 import com.ning.billing.payment.provider.MockPaymentProviderPluginModule;
 import com.ning.billing.payment.setup.PaymentModule;
@@ -311,7 +311,7 @@ public class TestJaxrsBase {
     protected AccountJson createAccount(String name, String key, String email) throws Exception {
         AccountJson input = getAccountJson(name, key, email);
         String baseJson = mapper.writeValueAsString(input);
-        Response response = doPost(BaseJaxrsResource.ACCOUNTS_PATH, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        Response response = doPost(JaxrsResource.ACCOUNTS_PATH, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
         Assert.assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
 
         String location = response.getHeader("Location");
@@ -332,7 +332,7 @@ public class TestJaxrsBase {
     protected BundleJsonNoSubscriptions createBundle(String accountId, String key) throws Exception {
         BundleJsonNoSubscriptions input = new BundleJsonNoSubscriptions(null, accountId, key, null);
         String baseJson = mapper.writeValueAsString(input);
-        Response response = doPost(BaseJaxrsResource.BUNDLES_PATH, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        Response response = doPost(JaxrsResource.BUNDLES_PATH, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
         Assert.assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
 
         String location = response.getHeader("Location");
@@ -355,7 +355,7 @@ public class TestJaxrsBase {
 
 
         Map<String, String> queryParams = waitCompletion ? getQueryParamsForCallCompletion("5") : DEFAULT_EMPTY_QUERY;
-        Response response = doPost(BaseJaxrsResource.SUBSCRIPTIONS_PATH, baseJson, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
+        Response response = doPost(JaxrsResource.SUBSCRIPTIONS_PATH, baseJson, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
         Assert.assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
 
         String location = response.getHeader("Location");
@@ -374,8 +374,8 @@ public class TestJaxrsBase {
 
     protected Map<String, String> getQueryParamsForCallCompletion(final String timeoutSec) {
         Map<String, String> queryParams = new HashMap<String, String>();
-        queryParams.put(BaseJaxrsResource.QUERY_CALL_COMPLETION, "true");
-        queryParams.put(BaseJaxrsResource.QUERY_CALL_TIMEOUT, timeoutSec);
+        queryParams.put(JaxrsResource.QUERY_CALL_COMPLETION, "true");
+        queryParams.put(JaxrsResource.QUERY_CALL_TIMEOUT, timeoutSec);
         return queryParams;
     }
 
@@ -422,9 +422,9 @@ public class TestJaxrsBase {
     private Response executeAndWait(final BoundRequestBuilder builder, final int timeoutSec, final boolean addContextHeader) {
         
         if (addContextHeader) {
-            builder.addHeader(BaseJaxrsResource.HDR_CREATED_BY, createdBy);
-            builder.addHeader(BaseJaxrsResource.HDR_REASON, reason);
-            builder.addHeader(BaseJaxrsResource.HDR_COMMENT, comment);            
+            builder.addHeader(JaxrsResource.HDR_CREATED_BY, createdBy);
+            builder.addHeader(JaxrsResource.HDR_REASON, reason);
+            builder.addHeader(JaxrsResource.HDR_COMMENT, comment);
         }
         
         Response response = null;
@@ -444,7 +444,7 @@ public class TestJaxrsBase {
         return response;
     }
 
-    private String getUrlFromUri(final String uri) {
+    protected String getUrlFromUri(final String uri) {
         return String.format("http://%s:%d%s", config.getServerHost(), config.getServerPort(), uri);
     }
 
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestSubscription.java b/server/src/test/java/com/ning/billing/jaxrs/TestSubscription.java
index ad99151..962352b 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestSubscription.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestSubscription.java
@@ -23,6 +23,7 @@ import java.util.UUID;
 
 import javax.ws.rs.core.Response.Status;
 
+import com.ning.billing.jaxrs.resources.JaxrsResource;
 import org.joda.time.DateTime;
 import org.joda.time.Interval;
 import org.slf4j.Logger;
@@ -36,7 +37,6 @@ import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.jaxrs.json.AccountJson;
 import com.ning.billing.jaxrs.json.BundleJsonNoSubscriptions;
 import com.ning.billing.jaxrs.json.SubscriptionJsonNoEvents;
-import com.ning.billing.jaxrs.resources.BaseJaxrsResource;
 import com.ning.http.client.Response;
 
 public class TestSubscription extends TestJaxrsBase {
@@ -62,7 +62,7 @@ public class TestSubscription extends TestJaxrsBase {
         Assert.assertNotNull(subscriptionJson.getChargedThroughDate());
         Assert.assertEquals(subscriptionJson.getChargedThroughDate(), subscriptionJson.getStartDate().plusDays(30));        
         
-        String uri = BaseJaxrsResource.SUBSCRIPTIONS_PATH + "/" + subscriptionJson.getSubscriptionId().toString();
+        String uri = JaxrsResource.SUBSCRIPTIONS_PATH + "/" + subscriptionJson.getSubscriptionId().toString();
  
         
         // Retrieves with GET
@@ -99,19 +99,19 @@ public class TestSubscription extends TestJaxrsBase {
 
         //      
         // Cancel EOT
-        uri = BaseJaxrsResource.SUBSCRIPTIONS_PATH + "/" + subscriptionJson.getSubscriptionId().toString();
+        uri = JaxrsResource.SUBSCRIPTIONS_PATH + "/" + subscriptionJson.getSubscriptionId().toString();
         response = doDelete(uri, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
         assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
 
         // Uncancel
-        uri = BaseJaxrsResource.SUBSCRIPTIONS_PATH + "/" + subscriptionJson.getSubscriptionId().toString() + "/uncancel";
+        uri = JaxrsResource.SUBSCRIPTIONS_PATH + "/" + subscriptionJson.getSubscriptionId().toString() + "/uncancel";
         response = doPut(uri, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
         Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
     }
     
     @Test(groups="slow", enabled=true)
     public void testWithNonExistentSubscription() throws Exception {
-        String uri = BaseJaxrsResource.SUBSCRIPTIONS_PATH + "/" + UUID.randomUUID().toString();
+        String uri = JaxrsResource.SUBSCRIPTIONS_PATH + "/" + UUID.randomUUID().toString();
         SubscriptionJsonNoEvents subscriptionJson = new SubscriptionJsonNoEvents(null, UUID.randomUUID().toString(), null, "Pistol", ProductCategory.BASE.toString(), BillingPeriod.MONTHLY.toString(), PriceListSet.DEFAULT_PRICELIST_NAME, null);
         String baseJson = mapper.writeValueAsString(subscriptionJson);
         
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestTag.java b/server/src/test/java/com/ning/billing/jaxrs/TestTag.java
index c1f460a..da8318f 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestTag.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestTag.java
@@ -22,13 +22,13 @@ import java.util.List;
 
 import javax.ws.rs.core.Response.Status;
 
+import com.ning.billing.jaxrs.resources.JaxrsResource;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.annotations.Test;
 
 import com.fasterxml.jackson.core.type.TypeReference;
 import com.ning.billing.jaxrs.json.TagDefinitionJson;
-import com.ning.billing.jaxrs.resources.BaseJaxrsResource;
 import com.ning.http.client.Response;
 
 public class TestTag extends TestJaxrsBase {
@@ -40,7 +40,7 @@ public class TestTag extends TestJaxrsBase {
     
         TagDefinitionJson input = new TagDefinitionJson("blue", "realxing color");
         String baseJson = mapper.writeValueAsString(input);
-        Response response = doPost(BaseJaxrsResource.TAG_DEFINITIONS_PATH, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        Response response = doPost(JaxrsResource.TAG_DEFINITIONS_PATH, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
         assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
 
         String location = response.getHeader("Location");
@@ -59,7 +59,7 @@ public class TestTag extends TestJaxrsBase {
     @Test(groups="slow", enabled=true)
     public void testMultipleTagDefinitionOk() throws Exception {
     
-        Response response = doGet(BaseJaxrsResource.TAG_DEFINITIONS_PATH, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        Response response = doGet(JaxrsResource.TAG_DEFINITIONS_PATH, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
         assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
         String baseJson = response.getResponseBody();
         
@@ -68,25 +68,25 @@ public class TestTag extends TestJaxrsBase {
         
         TagDefinitionJson input = new TagDefinitionJson("blue", "realxing color");
         baseJson = mapper.writeValueAsString(input);
-        response = doPost(BaseJaxrsResource.TAG_DEFINITIONS_PATH, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        response = doPost(JaxrsResource.TAG_DEFINITIONS_PATH, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
         assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
 
         input = new TagDefinitionJson("red", "hot color");
         baseJson = mapper.writeValueAsString(input);
-        response = doPost(BaseJaxrsResource.TAG_DEFINITIONS_PATH, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        response = doPost(JaxrsResource.TAG_DEFINITIONS_PATH, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
         assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
 
         input = new TagDefinitionJson("yellow", "vibrant color");
         baseJson = mapper.writeValueAsString(input);
-        response = doPost(BaseJaxrsResource.TAG_DEFINITIONS_PATH, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        response = doPost(JaxrsResource.TAG_DEFINITIONS_PATH, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
         assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
 
         input = new TagDefinitionJson("green", "super realxing color");
         baseJson = mapper.writeValueAsString(input);
-        response = doPost(BaseJaxrsResource.TAG_DEFINITIONS_PATH, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        response = doPost(JaxrsResource.TAG_DEFINITIONS_PATH, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
         assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
 
-        response = doGet(BaseJaxrsResource.TAG_DEFINITIONS_PATH, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        response = doGet(JaxrsResource.TAG_DEFINITIONS_PATH, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
         assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
         baseJson = response.getResponseBody();
         
@@ -97,11 +97,11 @@ public class TestTag extends TestJaxrsBase {
         // STEPH currently broken Tag API does not work as expected...
         
         /*
-        String uri = BaseJaxrsResource.TAG_DEFINITIONS_PATH + "/green"; 
+        String uri = JaxrsResource.TAG_DEFINITIONS_PATH + "/green";
         response = doDelete(uri, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
         assertEquals(response.getStatusCode(), Status.NO_CONTENT.getStatusCode());
     
-        response = doGet(BaseJaxrsResource.TAG_DEFINITIONS_PATH, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        response = doGet(JaxrsResource.TAG_DEFINITIONS_PATH, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
         assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
         baseJson = response.getResponseBody();
         
diff --git a/util/src/main/java/com/ning/billing/util/dao/AuditedCollectionDaoBase.java b/util/src/main/java/com/ning/billing/util/dao/AuditedCollectionDaoBase.java
index 10cd646..ba7084c 100644
--- a/util/src/main/java/com/ning/billing/util/dao/AuditedCollectionDaoBase.java
+++ b/util/src/main/java/com/ning/billing/util/dao/AuditedCollectionDaoBase.java
@@ -20,6 +20,7 @@ import com.ning.billing.util.ChangeType;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.entity.Entity;
 import com.ning.billing.util.entity.collection.dao.UpdatableEntityCollectionSqlDao;
+import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
 import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 
 import java.util.ArrayList;