killbill-aplcache

Changes

server/pom.xml 11(+5 -6)

Details

diff --git a/api/src/main/java/com/ning/billing/tenant/api/TenantKV.java b/api/src/main/java/com/ning/billing/tenant/api/TenantKV.java
index 3c15086..0103440 100644
--- a/api/src/main/java/com/ning/billing/tenant/api/TenantKV.java
+++ b/api/src/main/java/com/ning/billing/tenant/api/TenantKV.java
@@ -15,11 +15,15 @@
  */
 package com.ning.billing.tenant.api;
 
-import com.ning.billing.util.entity.Entity;
 
-public interface TenantKV extends Entity {
+public interface TenantKV {
 
     public String getKey();
 
     public String getValue();
+
+    public enum TenantKey {
+        // Key for push notification callback
+        PUSH_NOTIFICATION_CB
+    }
 }
diff --git a/api/src/main/java/com/ning/billing/tenant/api/TenantUserApi.java b/api/src/main/java/com/ning/billing/tenant/api/TenantUserApi.java
index b403156..3afc712 100644
--- a/api/src/main/java/com/ning/billing/tenant/api/TenantUserApi.java
+++ b/api/src/main/java/com/ning/billing/tenant/api/TenantUserApi.java
@@ -16,9 +16,11 @@
 
 package com.ning.billing.tenant.api;
 
+import java.util.List;
 import java.util.UUID;
 
 import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.TenantContext;
 
 public interface TenantUserApi {
 
@@ -28,9 +30,9 @@ public interface TenantUserApi {
 
     public Tenant getTenantById(final UUID tenantId) throws TenantApiException;
 
-    public String getTenantValueForKey(final UUID tenantId, final String key) throws TenantApiException;
+    public List<String> getTenantValueForKey(final String key, final TenantContext context) throws TenantApiException;
 
-    public void addTenantKeyValue(final UUID tenantId, final String key, final String value, final CallContext context) throws TenantApiException;
+    public void addTenantKeyValue(final String key, final String value, final CallContext context) throws TenantApiException;
 
-    public void deleteTenantKey(final UUID tenantId, final String key) throws TenantApiException;
+    public void deleteTenantKey(final String key, final CallContext context) throws TenantApiException;
 }
diff --git a/beatrix/src/main/java/com/ning/billing/beatrix/extbus/DefaultBusEvent.java b/beatrix/src/main/java/com/ning/billing/beatrix/extbus/DefaultBusEvent.java
index 16cd94b..6df7129 100644
--- a/beatrix/src/main/java/com/ning/billing/beatrix/extbus/DefaultBusEvent.java
+++ b/beatrix/src/main/java/com/ning/billing/beatrix/extbus/DefaultBusEvent.java
@@ -17,11 +17,11 @@ package com.ning.billing.beatrix.extbus;
 
 import java.util.UUID;
 
-import com.ning.billing.beatrix.bus.api.BusEvent;
+import com.ning.billing.beatrix.bus.api.ExtBusEvent;
 import com.ning.billing.beatrix.bus.api.ExtBusEventType;
 import com.ning.billing.util.dao.ObjectType;
 
-public class DefaultBusEvent implements BusEvent {
+public class DefaultBusEvent implements ExtBusEvent {
 
     private final ExtBusEventType eventType;
     private final ObjectType objectType;
diff --git a/beatrix/src/main/java/com/ning/billing/beatrix/extbus/PersistentExternalBus.java b/beatrix/src/main/java/com/ning/billing/beatrix/extbus/PersistentExternalBus.java
index 6398289..63f1a0a 100644
--- a/beatrix/src/main/java/com/ning/billing/beatrix/extbus/PersistentExternalBus.java
+++ b/beatrix/src/main/java/com/ning/billing/beatrix/extbus/PersistentExternalBus.java
@@ -27,7 +27,7 @@ import org.skife.jdbi.v2.IDBI;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.ning.billing.beatrix.bus.api.BusEvent;
+import com.ning.billing.beatrix.bus.api.ExtBusEvent;
 import com.ning.billing.beatrix.bus.api.ExternalBus;
 import com.ning.billing.beatrix.extbus.dao.ExtBusEventEntry;
 import com.ning.billing.beatrix.extbus.dao.ExtBusSqlDao;
@@ -121,7 +121,7 @@ public class PersistentExternalBus extends PersistentQueueBase implements Extern
             // API_FIX How do we get UUID from recordId ?
             final UUID accountId  = null;
             final UUID tenantId  = null;
-            final BusEvent event = new DefaultBusEvent(cur.getExtBusType(), cur.getObjectType(), cur.getObjectId(), accountId, tenantId);
+            final ExtBusEvent event = new DefaultBusEvent(cur.getExtBusType(), cur.getObjectType(), cur.getObjectId(), accountId, tenantId);
             result++;
             // STEPH exception handling is done by GUAVA-- logged a bug Issue-780
             eventBusDelegate.post(event);
diff --git a/beatrix/src/main/java/com/ning/billing/beatrix/lifecycle/DefaultLifecycle.java b/beatrix/src/main/java/com/ning/billing/beatrix/lifecycle/DefaultLifecycle.java
index 373fb8c..5f20021 100644
--- a/beatrix/src/main/java/com/ning/billing/beatrix/lifecycle/DefaultLifecycle.java
+++ b/beatrix/src/main/java/com/ning/billing/beatrix/lifecycle/DefaultLifecycle.java
@@ -99,7 +99,7 @@ public class DefaultLifecycle implements Lifecycle {
                 final KillbillService instance = injector.getInstance(cur);
                 log.info("got instance {}", instance.getName());
                 result.add(instance);
-            } catch (Exception e) {
+            } catch (final Exception e) {
                 logWarn("Failed to inject " + cur.getName(), e);
             }
 
@@ -132,7 +132,7 @@ public class DefaultLifecycle implements Lifecycle {
                 final KillbillService target = cur.getTarget();
                 log.info("Killbill lifecycle calling handler {} for service {}", cur.getMethod().getName(), target.getName());
                 method.invoke(target);
-            } catch (Exception e) {
+            } catch (final Exception e) {
                 logWarn("Killbill lifecycle failed to invoke lifecycle handler", e);
             }
         }
@@ -142,7 +142,7 @@ public class DefaultLifecycle implements Lifecycle {
 
     // Used to disable valid injection failure from unit tests
     protected void logWarn(final String msg, final Exception e) {
-        log.warn(msg, e);
+        log.warn(msg);
     }
 
     private Multimap<LifecycleLevel, LifecycleHandler<? extends KillbillService>> findAllHandlers(final KillbillService service) {
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestPublicBus.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestPublicBus.java
index 9b8b5c5..4faf255 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestPublicBus.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestPublicBus.java
@@ -27,7 +27,7 @@ import org.testng.annotations.Test;
 
 import com.ning.billing.account.api.Account;
 import com.ning.billing.api.TestApiListener.NextEvent;
-import com.ning.billing.beatrix.bus.api.BusEvent;
+import com.ning.billing.beatrix.bus.api.ExtBusEvent;
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.PlanPhaseSpecifier;
 import com.ning.billing.catalog.api.PriceListSet;
@@ -45,7 +45,7 @@ public class TestPublicBus extends TestIntegrationBase {
 
     public class PublicListener {
         @Subscribe
-        public void handleExternalEvents(final BusEvent event) {
+        public void handleExternalEvents(final ExtBusEvent event) {
             log.info("GOT EXT EVENT " + event.toString());
         }
     }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/NotificationJson.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/NotificationJson.java
new file mode 100644
index 0000000..762a184
--- /dev/null
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/NotificationJson.java
@@ -0,0 +1,66 @@
+/*
+ * 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.json;
+
+import com.ning.billing.beatrix.bus.api.ExtBusEvent;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/*
+ * Use to communicate back with client after they registered a callback
+ */
+public class NotificationJson {
+
+    private final String eventType;
+    private final String accountId;
+    private final String objectType;
+    private final String objectId;
+
+    @JsonCreator
+    public NotificationJson(@JsonProperty("eventType") final String eventType,
+            @JsonProperty("accountId") final String accountId,
+            @JsonProperty("objectType") final String objectType,
+            @JsonProperty("objectId") final String objectId) {
+        this.eventType = eventType;
+        this.accountId = accountId;
+        this.objectType = objectType;
+        this.objectId = objectId;
+    }
+
+
+    public NotificationJson(final ExtBusEvent event) {
+        this(event.getEventType().toString(), event.getAccountId().toString(), event.getObjectType().toString(), event.getObjectId().toString());
+    }
+
+
+    public String getEventType() {
+        return eventType;
+    }
+
+    public String getAccountId() {
+        return accountId;
+    }
+
+    public String getObjectType() {
+        return objectType;
+    }
+
+    public String getObjectId() {
+        return objectId;
+    }
+}
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/TenantKeyJson.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/TenantKeyJson.java
new file mode 100644
index 0000000..686695c
--- /dev/null
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/TenantKeyJson.java
@@ -0,0 +1,44 @@
+/*
+ * 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.json;
+
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class TenantKeyJson {
+
+    private final String key;
+    private final List<String> values;
+
+
+    @JsonCreator
+    public TenantKeyJson(@JsonProperty("key")  final String key,
+            @JsonProperty("values") final List<String> values) {
+        this.key = key;
+        this.values = values;
+    }
+
+    public String getKey() {
+        return key;
+    }
+
+    public List<String> getValues() {
+        return values;
+    }
+}
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java
index fb16a50..8384cf9 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java
@@ -25,6 +25,7 @@ public interface JaxrsResource {
     public static final String PREFIX = API_PREFIX + API_VERSION + API_POSTFIX;
 
     public static final String TIMELINE = "timeline";
+    public static final String REGISTER_NOTIFICATION_CALLBACK = "registerNotificationCallback";
 
     /*
      * Metadata Additional headers
@@ -76,6 +77,8 @@ public interface JaxrsResource {
 
     public static final String QUERY_AUDIT = "audit";
 
+    public static final String QUERY_NOTIFICATION_CALLBACK = "cb";
+
     public static final String ACCOUNTS = "accounts";
     public static final String ACCOUNTS_PATH = PREFIX + "/" + ACCOUNTS;
 
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/TenantResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/TenantResource.java
index e94b49f..d7cbf2a 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/TenantResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/TenantResource.java
@@ -16,10 +16,15 @@
 
 package com.ning.billing.jaxrs.resources;
 
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+
+import java.net.URI;
+import java.util.List;
 import java.util.UUID;
 
 import javax.servlet.http.HttpServletRequest;
 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;
@@ -29,24 +34,27 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.core.UriBuilder;
 
 import com.ning.billing.jaxrs.json.TenantJson;
+import com.ning.billing.jaxrs.json.TenantKeyJson;
 import com.ning.billing.jaxrs.util.Context;
 import com.ning.billing.jaxrs.util.JaxrsUriBuilder;
 import com.ning.billing.tenant.api.Tenant;
 import com.ning.billing.tenant.api.TenantApiException;
 import com.ning.billing.tenant.api.TenantData;
+import com.ning.billing.tenant.api.TenantKV.TenantKey;
 import com.ning.billing.tenant.api.TenantUserApi;
 import com.ning.billing.util.api.AuditUserApi;
 import com.ning.billing.util.api.CustomFieldUserApi;
 import com.ning.billing.util.api.TagUserApi;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.TenantContext;
 import com.ning.billing.util.dao.ObjectType;
 
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 
-import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
-
 @Singleton
 @Path(JaxrsResource.TENANTS_PATH)
 public class TenantResource extends JaxRsResourceBase {
@@ -55,11 +63,11 @@ public class TenantResource extends JaxRsResourceBase {
 
     @Inject
     public TenantResource(final TenantUserApi tenantApi,
-                          final JaxrsUriBuilder uriBuilder,
-                          final TagUserApi tagUserApi,
-                          final CustomFieldUserApi customFieldUserApi,
-                          final AuditUserApi auditUserApi,
-                          final Context context) {
+            final JaxrsUriBuilder uriBuilder,
+            final TagUserApi tagUserApi,
+            final CustomFieldUserApi customFieldUserApi,
+            final AuditUserApi auditUserApi,
+            final Context context) {
         super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, context);
         this.tenantApi = tenantApi;
     }
@@ -83,15 +91,55 @@ public class TenantResource extends JaxRsResourceBase {
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
     public Response createTenant(final TenantJson json,
-                                 @HeaderParam(HDR_CREATED_BY) final String createdBy,
-                                 @HeaderParam(HDR_REASON) final String reason,
-                                 @HeaderParam(HDR_COMMENT) final String comment,
-                                 @javax.ws.rs.core.Context final HttpServletRequest request) throws TenantApiException {
+            @HeaderParam(HDR_CREATED_BY) final String createdBy,
+            @HeaderParam(HDR_REASON) final String reason,
+            @HeaderParam(HDR_COMMENT) final String comment,
+            @javax.ws.rs.core.Context final HttpServletRequest request) throws TenantApiException {
         final TenantData data = json.toTenantData();
         final Tenant tenant = tenantApi.createTenant(data, context.createContext(createdBy, reason, comment, request));
         return uriBuilder.buildResponse(TenantResource.class, "getTenant", tenant.getId());
     }
 
+    @POST
+    @Path("/" + REGISTER_NOTIFICATION_CALLBACK)
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON)
+    public Response registerPushNotificationCallback(@PathParam("tenantId") final String tenantId,
+            @QueryParam(QUERY_NOTIFICATION_CALLBACK) final String notificationCallback,
+            @HeaderParam(HDR_CREATED_BY) final String createdBy,
+            @HeaderParam(HDR_REASON) final String reason,
+            @HeaderParam(HDR_COMMENT) final String comment,
+            @javax.ws.rs.core.Context final HttpServletRequest request) throws TenantApiException {
+        final CallContext callContext = context.createContext(createdBy, reason, comment, request);
+        tenantApi.addTenantKeyValue(TenantKey.PUSH_NOTIFICATION_CB.toString(), notificationCallback, callContext);
+        final URI uri =  UriBuilder.fromResource(TenantResource.class).path(TenantResource.class, "getPushNotificationCallbacks").build();
+        return Response.created(uri).build();
+    }
+
+    @GET
+    @Path("/" + REGISTER_NOTIFICATION_CALLBACK)
+    @Produces(APPLICATION_JSON)
+    public Response getPushNotificationCallbacks(@javax.ws.rs.core.Context final HttpServletRequest request) throws TenantApiException {
+
+        final TenantContext tenatContext = context.createContext(request);
+        final List<String> values = tenantApi.getTenantValueForKey(TenantKey.PUSH_NOTIFICATION_CB.toString(), tenatContext);
+        final TenantKeyJson result = new TenantKeyJson(TenantKey.PUSH_NOTIFICATION_CB.toString(), values);
+        return Response.status(Status.OK).entity(result).build();
+    }
+
+    @DELETE
+    @Path("/REGISTER_NOTIFICATION_CALLBACK")
+    public Response deletePushNotificationCallbacks(@PathParam("tenantId") final String tenantId,
+            @HeaderParam(HDR_CREATED_BY) final String createdBy,
+            @HeaderParam(HDR_REASON) final String reason,
+            @HeaderParam(HDR_COMMENT) final String comment,
+            @javax.ws.rs.core.Context final HttpServletRequest request) throws TenantApiException {
+        final CallContext callContext = context.createContext(createdBy, reason, comment, request);
+        tenantApi.deleteTenantKey(TenantKey.PUSH_NOTIFICATION_CB.toString(), callContext);
+        return Response.status(Status.OK).build();
+    }
+
+
     @Override
     protected ObjectType getObjectType() {
         return ObjectType.TENANT;
diff --git a/overdue/src/main/java/com/ning/billing/overdue/glue/DefaultOverdueModule.java b/overdue/src/main/java/com/ning/billing/overdue/glue/DefaultOverdueModule.java
index 08920d8..e83cbbf 100644
--- a/overdue/src/main/java/com/ning/billing/overdue/glue/DefaultOverdueModule.java
+++ b/overdue/src/main/java/com/ning/billing/overdue/glue/DefaultOverdueModule.java
@@ -31,7 +31,6 @@ import com.ning.billing.overdue.applicator.OverdueEmailGenerator;
 import com.ning.billing.overdue.applicator.formatters.DefaultOverdueEmailFormatterFactory;
 import com.ning.billing.overdue.applicator.formatters.OverdueEmailFormatterFactory;
 import com.ning.billing.overdue.service.DefaultOverdueService;
-import com.ning.billing.overdue.service.ExtendedOverdueService;
 import com.ning.billing.overdue.wrapper.OverdueWrapperFactory;
 
 import com.google.inject.AbstractModule;
@@ -49,7 +48,7 @@ public class DefaultOverdueModule extends AbstractModule implements OverdueModul
 
         final OverdueProperties config = new ConfigurationObjectFactory(System.getProperties()).build(OverdueProperties.class);
         bind(OverdueProperties.class).toInstance(config);
-        bind(ExtendedOverdueService.class).to(DefaultOverdueService.class).asEagerSingleton();
+        //bind(ExtendedOverdueService.class).to(DefaultOverdueService.class).asEagerSingleton();
         bind(OverdueCheckNotifier.class).to(DefaultOverdueCheckNotifier.class).asEagerSingleton();
         bind(OverdueCheckPoster.class).to(DefaultOverdueCheckPoster.class).asEagerSingleton();
     }
diff --git a/overdue/src/main/java/com/ning/billing/overdue/service/DefaultOverdueService.java b/overdue/src/main/java/com/ning/billing/overdue/service/DefaultOverdueService.java
index c0879ad..c3ea860 100644
--- a/overdue/src/main/java/com/ning/billing/overdue/service/DefaultOverdueService.java
+++ b/overdue/src/main/java/com/ning/billing/overdue/service/DefaultOverdueService.java
@@ -22,21 +22,23 @@ import java.net.URISyntaxException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.inject.Inject;
 import com.ning.billing.lifecycle.LifecycleHandlerType;
 import com.ning.billing.lifecycle.LifecycleHandlerType.LifecycleLevel;
 import com.ning.billing.ovedue.notification.OverdueCheckNotifier;
 import com.ning.billing.overdue.OverdueProperties;
+import com.ning.billing.overdue.OverdueService;
 import com.ning.billing.overdue.OverdueUserApi;
 import com.ning.billing.overdue.api.DefaultOverdueUserApi;
 import com.ning.billing.overdue.config.OverdueConfig;
 import com.ning.billing.overdue.listener.OverdueListener;
 import com.ning.billing.overdue.wrapper.OverdueWrapperFactory;
-import com.ning.billing.util.svcsapi.bus.InternalBus.EventBusException;
-import com.ning.billing.util.svcsapi.bus.BusService;
 import com.ning.billing.util.config.XMLLoader;
+import com.ning.billing.util.svcsapi.bus.BusService;
+import com.ning.billing.util.svcsapi.bus.InternalBus.EventBusException;
+
+import com.google.inject.Inject;
 
-public class DefaultOverdueService implements ExtendedOverdueService {
+public class DefaultOverdueService implements OverdueService {
     private static final Logger log = LoggerFactory.getLogger(DefaultOverdueService.class);
 
     public static final String OVERDUE_SERVICE_NAME = "overdue-service";
@@ -48,7 +50,7 @@ public class DefaultOverdueService implements ExtendedOverdueService {
     private final OverdueWrapperFactory factory;
 
     private OverdueConfig overdueConfig;
-    private boolean isInitialized;
+    private boolean isConfigLoaded;
 
     @Inject
     public DefaultOverdueService(
@@ -64,6 +66,7 @@ public class DefaultOverdueService implements ExtendedOverdueService {
         this.busService = busService;
         this.listener = listener;
         this.factory = factory;
+        this.isConfigLoaded = false;
     }
 
     @Override
@@ -76,14 +79,14 @@ public class DefaultOverdueService implements ExtendedOverdueService {
         return userApi;
     }
 
-    @Override
+    //@Override
     public OverdueConfig getOverdueConfig() {
         return overdueConfig;
     }
 
-    @LifecycleHandlerType(LifecycleLevel.INIT_SERVICE)
+    @LifecycleHandlerType(LifecycleLevel.LOAD_CATALOG)
     public synchronized void loadConfig() throws ServiceException {
-        if (!isInitialized) {
+        if (!isConfigLoaded) {
             try {
                 final URI u = new URI(properties.getConfigURI());
                 overdueConfig = XMLLoader.getObjectFromUri(u, OverdueConfig.class);
@@ -93,12 +96,12 @@ public class DefaultOverdueService implements ExtendedOverdueService {
                     overdueConfig = new OverdueConfig();
                 }
 
-                isInitialized = true;
-            } catch (URISyntaxException e) {
+                isConfigLoaded = true;
+            } catch (final URISyntaxException e) {
                 overdueConfig = new OverdueConfig();
-            } catch (IllegalArgumentException e) {
+            } catch (final IllegalArgumentException e) {
                 overdueConfig = new OverdueConfig();
-            } catch (Exception e) {
+            } catch (final Exception e) {
                 throw new ServiceException(e);
             }
 
@@ -121,7 +124,7 @@ public class DefaultOverdueService implements ExtendedOverdueService {
     public void registerForBus() {
         try {
             busService.getBus().register(listener);
-        } catch (EventBusException e) {
+        } catch (final EventBusException e) {
             log.error("Problem encountered registering OverdueListener on the Event Bus", e);
         }
     }
@@ -130,7 +133,7 @@ public class DefaultOverdueService implements ExtendedOverdueService {
     public void unregisterForBus() {
         try {
             busService.getBus().unregister(listener);
-        } catch (EventBusException e) {
+        } catch (final EventBusException e) {
             log.error("Problem encountered registering OverdueListener on the Event Bus", e);
         }
     }

server/pom.xml 11(+5 -6)

diff --git a/server/pom.xml b/server/pom.xml
index a519a55..25b4148 100644
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -127,6 +127,11 @@
             <artifactId>slf4j-api</artifactId>
         </dependency>
         <dependency>
+            <groupId>com.ning</groupId>
+            <artifactId>async-http-client</artifactId>
+            <version>${async-http-client.version}</version>
+        </dependency>
+        <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>jcl-over-slf4j</artifactId>
             <version>${slf4j.version}</version>
@@ -241,12 +246,6 @@
             <artifactId>shiro-web</artifactId>
         </dependency>
         <dependency>
-            <groupId>com.ning</groupId>
-            <artifactId>async-http-client</artifactId>
-            <version>${async-http-client.version}</version>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
             <groupId>org.testng</groupId>
             <artifactId>testng</artifactId>
             <scope>test</scope>
diff --git a/server/src/main/java/com/ning/billing/server/DefaultServerService.java b/server/src/main/java/com/ning/billing/server/DefaultServerService.java
new file mode 100644
index 0000000..98a3c4c
--- /dev/null
+++ b/server/src/main/java/com/ning/billing/server/DefaultServerService.java
@@ -0,0 +1,52 @@
+/*
+ * 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.server;
+
+import javax.inject.Inject;
+
+import com.ning.billing.beatrix.bus.api.ExternalBus;
+import com.ning.billing.lifecycle.LifecycleHandlerType;
+import com.ning.billing.server.notifications.PushNotificationListener;
+
+public class DefaultServerService implements ServerService {
+
+    private final static String SERVER_SERVICE = "server-service";
+
+
+    private final ExternalBus bus;
+    private final PushNotificationListener pushNotificationListener;
+
+    @Inject
+    public DefaultServerService(final ExternalBus bus, final PushNotificationListener pushNotificationListener) {
+        this.bus = bus;
+        this.pushNotificationListener = pushNotificationListener;
+    }
+
+    @Override
+    public String getName() {
+        return SERVER_SERVICE;
+    }
+
+    @LifecycleHandlerType(LifecycleHandlerType.LifecycleLevel.REGISTER_EVENTS)
+    public void registerForNotifications() {
+        bus.register(pushNotificationListener);
+    }
+
+    @LifecycleHandlerType(LifecycleHandlerType.LifecycleLevel.UNREGISTER_EVENTS)
+    public void unregisterForNotifications() {
+        bus.unregister(pushNotificationListener);
+    }
+}
diff --git a/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java b/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java
index 4676cb4..5a25746 100644
--- a/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java
+++ b/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java
@@ -40,6 +40,9 @@ import com.ning.billing.jaxrs.util.KillbillEventHandler;
 import com.ning.billing.junction.glue.DefaultJunctionModule;
 import com.ning.billing.overdue.glue.DefaultOverdueModule;
 import com.ning.billing.payment.glue.PaymentModule;
+import com.ning.billing.server.DefaultServerService;
+import com.ning.billing.server.ServerService;
+import com.ning.billing.server.notifications.PushNotificationListener;
 import com.ning.billing.tenant.glue.TenantModule;
 import com.ning.billing.util.email.EmailModule;
 import com.ning.billing.util.email.templates.TemplateModule;
@@ -62,6 +65,13 @@ public class KillbillServerModule extends AbstractModule {
         configureDao();
         configureResources();
         installKillbillModules();
+        configurePushNotification();
+    }
+
+
+    protected void configurePushNotification() {
+        bind(ServerService.class).to(DefaultServerService.class).asEagerSingleton();
+        bind(PushNotificationListener.class).asEagerSingleton();
     }
 
 
@@ -69,7 +79,7 @@ public class KillbillServerModule extends AbstractModule {
         // Load mysql driver if needed
         try {
             Class.forName ("com.mysql.jdbc.Driver").newInstance ();
-        } catch (Exception ignore) {
+        } catch (final Exception ignore) {
         }
         bind(IDBI.class).to(DBI.class).asEagerSingleton();
         bind(DBI.class).toProvider(DBIProvider.class).asEagerSingleton();
diff --git a/server/src/main/java/com/ning/billing/server/notifications/PushNotificationListener.java b/server/src/main/java/com/ning/billing/server/notifications/PushNotificationListener.java
new file mode 100644
index 0000000..3f6fe43
--- /dev/null
+++ b/server/src/main/java/com/ning/billing/server/notifications/PushNotificationListener.java
@@ -0,0 +1,112 @@
+/*
+ * 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.server.notifications;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+
+import javax.inject.Inject;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.ning.billing.beatrix.bus.api.ExtBusEvent;
+import com.ning.billing.jaxrs.json.NotificationJson;
+import com.ning.billing.tenant.api.TenantApiException;
+import com.ning.billing.tenant.api.TenantKV.TenantKey;
+import com.ning.billing.tenant.api.TenantUserApi;
+import com.ning.billing.util.callcontext.CallContextFactory;
+import com.ning.billing.util.callcontext.TenantContext;
+import com.ning.http.client.AsyncCompletionHandler;
+import com.ning.http.client.AsyncHttpClient;
+import com.ning.http.client.AsyncHttpClient.BoundRequestBuilder;
+import com.ning.http.client.AsyncHttpClientConfig;
+import com.ning.http.client.ListenableFuture;
+import com.ning.http.client.Response;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.eventbus.Subscribe;
+
+public class PushNotificationListener {
+
+    private final static Logger log = LoggerFactory.getLogger(PushNotificationListener.class);
+
+    private final static int TIMEOUT_NOTIFCATION = 15; // 15 seconds
+
+    private final TenantUserApi tenantApi;
+    private final CallContextFactory contextFactory;
+    private final AsyncHttpClient httpClient;
+    private final ObjectMapper mapper;
+
+
+    @Inject
+    public PushNotificationListener(final ObjectMapper mapper, final TenantUserApi tenantApi, final CallContextFactory contextFactory) {
+        this.httpClient = new AsyncHttpClient(new AsyncHttpClientConfig.Builder().setRequestTimeoutInMs(TIMEOUT_NOTIFCATION * 1000).build());
+        this.tenantApi = tenantApi;
+        this.contextFactory = contextFactory;
+        this.mapper = mapper;
+    }
+
+    @Subscribe
+    public void triggerPushNotifications(final ExtBusEvent event) {
+        final TenantContext context = contextFactory.createTenantContext(event.getTenantId());
+        try {
+            final List<String> callbacks = getCallbacksForTenant(context);
+            dispatchCallback(event.getTenantId(), event, callbacks);
+        } catch (final TenantApiException e) {
+            log.warn("Failed to retrieve push notification callback for tenant {}", event.getTenantId());
+        } catch (final IOException e) {
+            log.warn("Failed to retrieve push notification callback for tenant {}", event.getTenantId());
+        }
+    }
+
+    private void dispatchCallback(final UUID tenantId, final ExtBusEvent event, final List<String> callbacks) throws IOException {
+        final NotificationJson notification = new NotificationJson(event);
+        final String body = mapper.writeValueAsString(notification);
+        for (final String cur : callbacks) {
+            doPost(tenantId, cur, body, TIMEOUT_NOTIFCATION);
+        }
+    }
+
+
+    private boolean doPost(final UUID tenantId, final String url, final String body, final int timeoutSec) {
+
+        final BoundRequestBuilder builder = httpClient.preparePost(url);
+        builder.setBody(body == null ? "{}" : body);
+
+        Response response = null;
+        try {
+            final ListenableFuture<Response> futureStatus =
+                    builder.execute(new AsyncCompletionHandler<Response>() {
+                        @Override
+                        public Response onCompleted(final Response response) throws Exception {
+                            return response;
+                        }
+                    });
+            response = futureStatus.get(timeoutSec, TimeUnit.SECONDS);
+        } catch (final Exception e) {
+            log.warn(String.format("Fail to psh notification {} for the tenant {} ", url, tenantId), e);
+            return false;
+        }
+        return response.getStatusCode() >= 200 && response.getStatusCode() < 300;
+    }
+
+    private List<String> getCallbacksForTenant(final TenantContext context) throws TenantApiException {
+        return tenantApi.getTenantValueForKey(TenantKey.PUSH_NOTIFICATION_CB.toString(), context);
+    }
+}
diff --git a/server/src/test/java/com/ning/billing/jaxrs/KillbillClient.java b/server/src/test/java/com/ning/billing/jaxrs/KillbillClient.java
index 53bb288..9062bf8 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/KillbillClient.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/KillbillClient.java
@@ -16,6 +16,13 @@
 
 package com.ning.billing.jaxrs;
 
+import static com.ning.billing.jaxrs.resources.JaxrsResource.ACCOUNTS;
+import static com.ning.billing.jaxrs.resources.JaxrsResource.BUNDLES;
+import static com.ning.billing.jaxrs.resources.JaxrsResource.QUERY_PAYMENT_METHOD_PLUGIN_INFO;
+import static com.ning.billing.jaxrs.resources.JaxrsResource.SUBSCRIPTIONS;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+
 import java.io.IOException;
 import java.math.BigDecimal;
 import java.util.ArrayList;
@@ -76,13 +83,6 @@ import com.google.common.base.Function;
 import com.google.common.collect.Collections2;
 import com.google.common.collect.ImmutableMap;
 
-import static com.ning.billing.jaxrs.resources.JaxrsResource.ACCOUNTS;
-import static com.ning.billing.jaxrs.resources.JaxrsResource.BUNDLES;
-import static com.ning.billing.jaxrs.resources.JaxrsResource.QUERY_PAYMENT_METHOD_PLUGIN_INFO;
-import static com.ning.billing.jaxrs.resources.JaxrsResource.SUBSCRIPTIONS;
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertNotNull;
-
 public abstract class KillbillClient extends ServerTestSuiteWithEmbeddedDB {
 
     protected static final String PLUGIN_NAME = "noop";
@@ -142,10 +142,21 @@ public abstract class KillbillClient extends ServerTestSuiteWithEmbeddedDB {
     // TENANT UTILITIES
     //
 
-    protected void createTenant(final String apiKey, final String apiSecret) throws Exception {
+    protected String createTenant(final String apiKey, final String apiSecret) throws Exception {
         final String baseJson = mapper.writeValueAsString(new TenantJson(null, null, apiKey, apiSecret));
         final Response response = doPost(JaxrsResource.TENANTS_PATH, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
         Assert.assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
+        return response.getHeader("Location");
+    }
+
+
+    protected String registerCallbackNotificationForTenant(final String callback)  throws Exception {
+        final Map<String, String> queryParams = new HashMap<String, String>();
+        queryParams.put(JaxrsResource.QUERY_NOTIFICATION_CALLBACK, callback);
+        final String uri = JaxrsResource.TENANTS_PATH + "/" + JaxrsResource.REGISTER_NOTIFICATION_CALLBACK ;
+        final Response response = doPost(uri, null, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
+        Assert.assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
+        return response.getHeader("Location");
     }
 
     //
@@ -841,7 +852,7 @@ public abstract class KillbillClient extends ServerTestSuiteWithEmbeddedDB {
                         }
                     });
             response = futureStatus.get(timeoutSec, TimeUnit.SECONDS);
-        } catch (Exception e) {
+        } catch (final Exception e) {
             Assert.fail(e.getMessage());
         }
         Assert.assertNotNull(response);
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 eb0b9c1..6384d8f 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
@@ -16,8 +16,9 @@
 
 package com.ning.billing.jaxrs;
 
+import static org.testng.Assert.assertNotNull;
+
 import java.io.IOException;
-import java.lang.reflect.Method;
 import java.net.URL;
 import java.util.EventListener;
 import java.util.HashMap;
@@ -77,8 +78,6 @@ import com.fasterxml.jackson.datatype.joda.JodaModule;
 import com.google.common.collect.ImmutableList;
 import com.google.inject.Module;
 
-import static org.testng.Assert.assertNotNull;
-
 public class TestJaxrsBase extends KillbillClient {
 
     protected static final String PLUGIN_NAME = "noop";
@@ -97,7 +96,7 @@ public class TestJaxrsBase extends KillbillClient {
         assertNotNull(url);
         try {
             System.getProperties().load(url.openStream());
-        } catch (IOException e) {
+        } catch (final IOException e) {
             throw new RuntimeException(e);
         }
     }
@@ -202,7 +201,7 @@ public class TestJaxrsBase extends KillbillClient {
     }
 
     @BeforeMethod(groups = "slow")
-    public void cleanupBeforeMethod(final Method method) {
+    public void cleanupBeforeMethod() {
         busHandler.reset();
         clock.reset();
         clock.setDay(new LocalDate(2012, 8, 25));
@@ -264,7 +263,7 @@ public class TestJaxrsBase extends KillbillClient {
     public void tearDown() {
         try {
             server.stop();
-        } catch (Exception ignored) {
+        } catch (final Exception ignored) {
         }
     }
 }
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestJetty.java b/server/src/test/java/com/ning/billing/jaxrs/TestJetty.java
new file mode 100644
index 0000000..75186e0
--- /dev/null
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestJetty.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.jaxrs;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+
+import com.google.common.io.CharStreams;
+
+public class TestJetty {
+
+    public TestJetty() {
+
+    }
+
+    public static void main(final String []  args) throws Exception {
+
+        final Server server = new Server(8080);
+
+        final ServletContextHandler context = new ServletContextHandler();
+        context.setContextPath("/");
+        server.setHandler(context);
+
+        context.addServlet(new ServletHolder(new CallmebackServlet()),"/callmeback");
+
+        server.start();
+        server.join();
+    }
+
+
+    public static class CallmebackServlet extends HttpServlet
+    {
+        public CallmebackServlet() {
+        }
+
+        @Override
+        protected void doPost(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException
+        {
+            final String body = CharStreams.toString( new InputStreamReader(request.getInputStream(), "UTF-8" ));
+            System.out.print("Got " + body);
+
+
+            response.setContentType("application/json");
+            response.setStatus(HttpServletResponse.SC_OK);
+            response.getWriter().println("{\"key\"=12}");
+        }
+    }
+
+}
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestTenant.java b/server/src/test/java/com/ning/billing/jaxrs/TestTenant.java
new file mode 100644
index 0000000..0dfd744
--- /dev/null
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestTenant.java
@@ -0,0 +1,161 @@
+/*
+ * 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;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.io.CharStreams;
+
+public class TestTenant extends TestJaxrsBase {
+
+
+    private CallbackServer callbackServer;
+
+    private final static int SERVER_PORT = 8087;
+    private final static String CALLBACK_ENDPPOINT = "/callmeback";
+
+
+    @BeforeMethod(groups = "slow")
+    public void startServer() throws Exception {
+        callbackServer = new CallbackServer(SERVER_PORT, CALLBACK_ENDPPOINT, null);
+        //callbackServer.startServer();
+    }
+
+    @AfterMethod(groups = "slow")
+    public void stopServer() throws Exception {
+        //callbackServer.stopServer();
+    }
+
+    @Test(groups = "slow")
+    public void testTenant() throws Exception {
+
+        /*
+        final String apiKeyTenant = "yoyo";
+        final String apiSecretTenant = "yoyoisf3ommars";
+
+        final String location = createTenant(apiKeyTenant, apiSecretTenant);
+        Assert.assertNotNull(location);
+
+        final String tenantId = extractTenantIdFromLocation(location);
+
+        // Retrieves by Id based on Location returned
+        final Response response = doGetWithUrl(location, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
+*/
+
+        registerCallbackNotificationForTenant("http://127.0.0.1:" + SERVER_PORT + CALLBACK_ENDPPOINT);
+
+        // Excellent, now create an account and check that callback is called
+        createAccount();
+
+    }
+
+    private String extractTenantIdFromLocation(final String location) {
+        final String [] parts = location.split("/");
+        return parts[parts.length - 1];
+    }
+
+
+    public class CallbackServer {
+
+        private final Server server;
+        private final String callbackEndpoint;
+        private final String expectedTenantId;
+
+        public CallbackServer(final int port, final String callbackEndpoint, final String expectedTenantId) {
+            this.callbackEndpoint =  callbackEndpoint;
+            this.expectedTenantId = expectedTenantId;
+            this.server = new Server(port);
+        }
+
+        public void startServer() throws Exception {
+            final ServletContextHandler context = new ServletContextHandler();
+            context.setContextPath("/");
+            server.setHandler(context);
+            context.addServlet(new ServletHolder(new CallmebackServlet(server, expectedTenantId, 1)), callbackEndpoint);
+            server.start();
+            server.join();
+        }
+
+        public void stopServer() throws Exception {
+            server.stop();
+        }
+    }
+
+
+    public static class CallmebackServlet extends HttpServlet {
+
+        private static final long serialVersionUID = -5181211514918217301L;
+
+        private final static Logger log = LoggerFactory.getLogger(CallmebackServlet.class);
+
+        private final Server server;
+        private final String expectedTenantId;
+        private final int expectedNbCalls;
+        private final AtomicInteger receivedCalls;
+
+        public CallmebackServlet(final Server server, final String expectedTenantId, final int expectedNbCalls) {
+            this.server = server;
+            this.expectedTenantId = expectedTenantId;
+            this.expectedNbCalls = expectedNbCalls;
+            this.receivedCalls = new AtomicInteger(0);
+        }
+
+        @Override
+        protected void doPost(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException
+        {
+
+            final int current = receivedCalls.incrementAndGet();
+
+            final String body = CharStreams.toString( new InputStreamReader(request.getInputStream(), "UTF-8" ));
+
+            log.info("CallmebackServlet received {} calls , current = {}", current, body);
+
+            response.setContentType("application/json");
+            response.setStatus(HttpServletResponse.SC_OK);
+
+            stopServerWhenComplete(current);
+        }
+
+
+        private void stopServerWhenComplete(final int current) {
+            if (current == expectedNbCalls) {
+                try {
+                    server.stop();
+                    log.info("Callmeback server stopped succesfully");
+                } catch (final Exception e) {
+                    log.warn("Failed to stop jetty Callmeback server");
+                }
+            }
+        }
+    }
+}
diff --git a/tenant/src/main/java/com/ning/billing/tenant/api/DefaultTenantKV.java b/tenant/src/main/java/com/ning/billing/tenant/api/DefaultTenantKV.java
index cbf185d..ece3445 100644
--- a/tenant/src/main/java/com/ning/billing/tenant/api/DefaultTenantKV.java
+++ b/tenant/src/main/java/com/ning/billing/tenant/api/DefaultTenantKV.java
@@ -15,19 +15,13 @@
  */
 package com.ning.billing.tenant.api;
 
-import java.util.UUID;
 
-import org.joda.time.DateTime;
-
-import com.ning.billing.util.entity.EntityBase;
-
-public class DefaultTenantKV extends EntityBase implements TenantKV {
+public class DefaultTenantKV  implements TenantKV {
 
     private final String key;
     private final String value;
 
-    public DefaultTenantKV(final UUID tenantId, final String key, final String value, final DateTime createdDate, final DateTime updatedDate) {
-        super(tenantId, createdDate, updatedDate);
+    public DefaultTenantKV(final String key, final String value) {
         this.key = key;
         this.value = value;
     }
diff --git a/tenant/src/main/java/com/ning/billing/tenant/api/user/DefaultTenantUserApi.java b/tenant/src/main/java/com/ning/billing/tenant/api/user/DefaultTenantUserApi.java
index 7273fd5..5c00812 100644
--- a/tenant/src/main/java/com/ning/billing/tenant/api/user/DefaultTenantUserApi.java
+++ b/tenant/src/main/java/com/ning/billing/tenant/api/user/DefaultTenantUserApi.java
@@ -16,6 +16,7 @@
 
 package com.ning.billing.tenant.api.user;
 
+import java.util.List;
 import java.util.UUID;
 
 import com.ning.billing.ErrorCode;
@@ -29,6 +30,7 @@ import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.callcontext.InternalTenantContext;
+import com.ning.billing.util.callcontext.TenantContext;
 import com.ning.billing.util.entity.EntityPersistenceException;
 
 import com.google.inject.Inject;
@@ -44,6 +46,7 @@ public class DefaultTenantUserApi implements TenantUserApi {
         this.internalCallContextFactory = internalCallContextFactory;
     }
 
+
     @Override
     public Tenant createTenant(final TenantData data, final CallContext context) throws TenantApiException {
         final Tenant tenant = new DefaultTenant(data);
@@ -77,35 +80,39 @@ public class DefaultTenantUserApi implements TenantUserApi {
     }
 
     @Override
-    public String getTenantValueForKey(final UUID tenantId, final String key)
+    public List<String> getTenantValueForKey(final String key, final TenantContext context)
             throws TenantApiException {
-        final String value = tenantDao.getTenantValueForKey(tenantId, key);
-        if (value == null) {
-            throw new TenantApiException(ErrorCode.TENANT_NO_SUCH_KEY, tenantId, key);
-        }
+        final InternalTenantContext internalContext =  internalCallContextFactory.createInternalTenantContext(context);
+        final List<String> value = tenantDao.getTenantValueForKey(key, internalContext);
         return value;
     }
 
     @Override
-    public void addTenantKeyValue(final UUID tenantId, final String key, final String value, final CallContext context)
+    public void addTenantKeyValue(final String key, final String value, final CallContext context)
             throws TenantApiException {
 
-        final InternalCallContext internalContext = new InternalCallContext(null, null, context);
-        final Tenant tenant = tenantDao.getById(tenantId, internalContext);
+        final InternalCallContext internalContext =  internalCallContextFactory.createInternalCallContext(context);
+        // TODO Figure out the exact verification if nay
+        /*
+        final Tenant tenant = tenantDao.getById(context.getTenantId(), internalContext);
         if (tenant == null) {
             throw new TenantApiException(ErrorCode.TENANT_DOES_NOT_EXIST_FOR_ID, tenantId);
         }
-        tenantDao.addTenantKeyValue(tenantId, key, value, internalContext);
+        */
+        tenantDao.addTenantKeyValue(key, value, internalContext);
     }
 
 
     @Override
-    public void deleteTenantKey(final UUID tenantId, final String key)
+    public void deleteTenantKey(final String key, final CallContext context)
             throws TenantApiException {
+        /*
         final Tenant tenant = tenantDao.getById(tenantId, new InternalTenantContext(null, null));
         if (tenant == null) {
             throw new TenantApiException(ErrorCode.TENANT_DOES_NOT_EXIST_FOR_ID, tenantId);
         }
-        tenantDao.deleteTenantKey(tenantId, key);
+        */
+        final InternalCallContext internalContext =  internalCallContextFactory.createInternalCallContext(context);
+        tenantDao.deleteTenantKey(key, internalContext);
     }
 }
diff --git a/tenant/src/main/java/com/ning/billing/tenant/dao/DefaultTenantDao.java b/tenant/src/main/java/com/ning/billing/tenant/dao/DefaultTenantDao.java
index 6890832..6f59f17 100644
--- a/tenant/src/main/java/com/ning/billing/tenant/dao/DefaultTenantDao.java
+++ b/tenant/src/main/java/com/ning/billing/tenant/dao/DefaultTenantDao.java
@@ -19,6 +19,8 @@ package com.ning.billing.tenant.dao;
 import java.util.List;
 import java.util.UUID;
 
+import javax.annotation.Nullable;
+
 import org.apache.shiro.authc.AuthenticationInfo;
 import org.apache.shiro.crypto.RandomNumberGenerator;
 import org.apache.shiro.crypto.SecureRandomNumberGenerator;
@@ -36,6 +38,9 @@ import com.ning.billing.util.entity.EntityPersistenceException;
 import com.ning.billing.util.svcsapi.bus.InternalBus;
 
 import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
 import com.google.inject.Inject;
 
 public class DefaultTenantDao implements TenantDao {
@@ -97,21 +102,24 @@ public class DefaultTenantDao implements TenantDao {
     }
 
     @Override
-    public String getTenantValueForKey(final UUID tenantId, final String key) {
-        final TenantKV tenantKV = tenantKVSqlDao.getTenantValueForKey(tenantId.toString(), key);
-        if (tenantKV == null) {
-            return null;
-        }
-        return tenantKV.getValue();
+    public List<String> getTenantValueForKey(final String key, final InternalTenantContext context) {
+        final List<TenantKV> tenantKV = tenantKVSqlDao.getTenantValueForKey(key, context.getTenantRecordId());
+        return ImmutableList.copyOf(Collections2.transform(tenantKV, new Function<TenantKV, String>() {
+            @Override
+            @Nullable
+            public String apply(final @Nullable TenantKV in) {
+                return in.getValue();
+            }
+        }));
     }
 
     @Override
-    public void addTenantKeyValue(final UUID tenantId, final String key, final String value, final InternalCallContext context) {
-        tenantKVSqlDao.insertTenantKeyValue(tenantId.toString(), key, value, context);
+    public void addTenantKeyValue(final String key, final String value, final InternalCallContext context) {
+        tenantKVSqlDao.insertTenantKeyValue(key, value, context.getTenantRecordId(), context);
     }
 
     @Override
-    public void deleteTenantKey(final UUID tenantId, final String key) {
-        tenantKVSqlDao.deleteTenantKey(tenantId.toString(), key);
+    public void deleteTenantKey(final String key, final InternalCallContext context) {
+        tenantKVSqlDao.deleteTenantKey(key, context.getTenantRecordId());
     }
 }
diff --git a/tenant/src/main/java/com/ning/billing/tenant/dao/TenantDao.java b/tenant/src/main/java/com/ning/billing/tenant/dao/TenantDao.java
index c73b52f..0d0add0 100644
--- a/tenant/src/main/java/com/ning/billing/tenant/dao/TenantDao.java
+++ b/tenant/src/main/java/com/ning/billing/tenant/dao/TenantDao.java
@@ -16,19 +16,20 @@
 
 package com.ning.billing.tenant.dao;
 
-import java.util.UUID;
+import java.util.List;
 
 import com.ning.billing.tenant.api.Tenant;
 import com.ning.billing.util.callcontext.InternalCallContext;
+import com.ning.billing.util.callcontext.InternalTenantContext;
 import com.ning.billing.util.entity.dao.EntityDao;
 
 public interface TenantDao extends EntityDao<Tenant> {
 
     public Tenant getTenantByApiKey(final String key);
 
-    public String getTenantValueForKey(final UUID tenantId, final String key);
+    public List<String> getTenantValueForKey(final String key, final InternalTenantContext context);
 
-    public void addTenantKeyValue(final UUID tenantId, final String key, final String value, final InternalCallContext context);
+    public void addTenantKeyValue(final String key, final String value, final InternalCallContext context);
 
-    public void deleteTenantKey(final UUID tenantId, final String key);
+    public void deleteTenantKey(final String key, final InternalCallContext context);
 }
diff --git a/tenant/src/main/java/com/ning/billing/tenant/dao/TenantKVSqlDao.java b/tenant/src/main/java/com/ning/billing/tenant/dao/TenantKVSqlDao.java
index 1535ffd..83b3d79 100644
--- a/tenant/src/main/java/com/ning/billing/tenant/dao/TenantKVSqlDao.java
+++ b/tenant/src/main/java/com/ning/billing/tenant/dao/TenantKVSqlDao.java
@@ -17,9 +17,8 @@ package com.ning.billing.tenant.dao;
 
 import java.sql.ResultSet;
 import java.sql.SQLException;
-import java.util.UUID;
+import java.util.List;
 
-import org.joda.time.DateTime;
 import org.skife.jdbi.v2.StatementContext;
 import org.skife.jdbi.v2.sqlobject.Bind;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
@@ -36,33 +35,28 @@ import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContextBinder;
 import com.ning.billing.util.dao.MapperBase;
 import com.ning.billing.util.dao.UuidMapper;
-import com.ning.billing.util.entity.dao.EntitySqlDao;
 
 @ExternalizedSqlViaStringTemplate3
 @RegisterMapper({UuidMapper.class, TenantKVMapper.class})
-public interface TenantKVSqlDao extends EntitySqlDao<TenantKV>, Transactional<TenantKVSqlDao> {
+public interface TenantKVSqlDao extends Transactional<TenantKVSqlDao> {
 
     @SqlQuery
-    public TenantKV getTenantValueForKey(@Bind("id") final String tenantId, @Bind("key") final String key);
+    public List<TenantKV> getTenantValueForKey(@Bind("key") final String key, @Bind("tenantRecordId") Long tenantRecordId);
 
     @SqlUpdate
-    public void insertTenantKeyValue(@Bind("id") final String tenantId, @Bind("key") final String key, @Bind("value") final String value,
-            @InternalTenantContextBinder final InternalCallContext context);
+    public void insertTenantKeyValue(@Bind("key") final String key, @Bind("value") final String value, @Bind("tenantRecordId") Long tenantRecordId, @InternalTenantContextBinder final InternalCallContext context);
 
     @SqlUpdate
-    public void deleteTenantKey(@Bind("id") final String tenantId, @Bind("key") final String key);
+    public void deleteTenantKey(@Bind("key") final String key, @Bind("tenantRecordId") Long tenantRecordId);
 
 
     public class TenantKVMapper extends MapperBase implements ResultSetMapper<TenantKV> {
 
         @Override
         public TenantKV map(final int index, final ResultSet result, final StatementContext context) throws SQLException {
-            final UUID id = getUUID(result, "id");
             final String key = result.getString("t_key");
             final String value = result.getString("t_value");
-            final DateTime createdDate = getDateTime(result, "created_date");
-            final DateTime updatedDate = getDateTime(result, "updated_date");
-            return new DefaultTenantKV(id, key, value, createdDate, updatedDate);
+            return new DefaultTenantKV(key, value);
         }
     }
 }
diff --git a/tenant/src/main/resources/com/ning/billing/tenant/dao/TenantKVSqlDao.sql.stg b/tenant/src/main/resources/com/ning/billing/tenant/dao/TenantKVSqlDao.sql.stg
index 3e2d0cb..432267f 100644
--- a/tenant/src/main/resources/com/ning/billing/tenant/dao/TenantKVSqlDao.sql.stg
+++ b/tenant/src/main/resources/com/ning/billing/tenant/dao/TenantKVSqlDao.sql.stg
@@ -3,7 +3,7 @@ group TenantKVSqlDao;
 
 tenantKVFields(prefix) ::= <<
     <prefix>record_id,
-    <prefix>id,
+    <prefix>tenant_record_id,
     <prefix>t_key,
     <prefix>t_value,
     <prefix>created_date,
@@ -14,7 +14,7 @@ tenantKVFields(prefix) ::= <<
 
 insertTenantKeyValue() ::= <<
    INSERT INTO tenant_kvs (
-        id
+        tenant_record_id
       , t_key
       , t_value
       , created_date
@@ -22,7 +22,7 @@ insertTenantKeyValue() ::= <<
       , updated_date
       , updated_by
     ) VALUES (
-        :id
+        :tenantRecordId
       , :key
       , :value
       , :createdDate
@@ -37,7 +37,7 @@ getTenantValueForKey() ::= <<
     SELECT <tenantKVFields()>
     FROM tenant_kvs
     WHERE
-        id = :id AND t_key = :key
+        tenant_record_id = :tenantRecordId AND t_key = :key
     ;
 >>
 
@@ -45,7 +45,7 @@ getTenantValueForKey() ::= <<
 deleteTenantKey() ::= <<
     DELETE FROM tenant_kvs
     WHERE
-        id = :id AND t_key = :key
+        tenant_record_id = :tenantRecordId AND t_key = :key
     ;
 >>
 
diff --git a/tenant/src/main/resources/com/ning/billing/tenant/ddl.sql b/tenant/src/main/resources/com/ning/billing/tenant/ddl.sql
index ac9d885..a8a7911 100644
--- a/tenant/src/main/resources/com/ning/billing/tenant/ddl.sql
+++ b/tenant/src/main/resources/com/ning/billing/tenant/ddl.sql
@@ -19,14 +19,13 @@ CREATE UNIQUE INDEX tenants_api_key ON tenants(api_key);
 DROP TABLE IF EXISTS tenant_kvs;
 CREATE TABLE tenant_kvs (
    record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
-   -- This is the tenant id, no id is generated as this is not a customer facing object
-   id char(36) NOT NULL,
+   tenant_record_id int(11) unsigned default null,
    t_key varchar(64) NOT NULL,
-   t_value varchar(512) NOT NULL,
+   t_value varchar(1024) NOT NULL,
    created_date datetime NOT NULL,
    created_by varchar(50) NOT NULL,
    updated_date datetime DEFAULT NULL,
    updated_by varchar(50) DEFAULT NULL,
    PRIMARY KEY(record_id)
 ) ENGINE=innodb;
-CREATE INDEX tenant_kvs_key ON tenant_kvs(id, t_key);
\ No newline at end of file
+CREATE INDEX tenant_kvs_key ON tenant_kvs(tenant_record_id, t_key);
\ No newline at end of file
diff --git a/tenant/src/test/java/com/ning/billing/tenant/dao/TestDefaultTenantDao.java b/tenant/src/test/java/com/ning/billing/tenant/dao/TestDefaultTenantDao.java
index 9a8cc58..f069f59 100644
--- a/tenant/src/test/java/com/ning/billing/tenant/dao/TestDefaultTenantDao.java
+++ b/tenant/src/test/java/com/ning/billing/tenant/dao/TestDefaultTenantDao.java
@@ -16,6 +16,7 @@
 
 package com.ning.billing.tenant.dao;
 
+import java.util.List;
 import java.util.UUID;
 
 import org.apache.shiro.authc.AuthenticationInfo;
@@ -63,11 +64,18 @@ public class TestDefaultTenantDao extends TenantTestSuiteWithEmbeddedDb {
                 UUID.randomUUID().toString(), UUID.randomUUID().toString());
         tenantDao.create(tenant, internalCallContext);
 
-        tenantDao.addTenantKeyValue(tenant.getId(), "TheKey", "TheValue", internalCallContext);
+        tenantDao.addTenantKeyValue("TheKey", "TheValue", internalCallContext);
 
-        final String value  = tenantDao.getTenantValueForKey(tenant.getId(), "TheKey");
-        Assert.assertEquals(value, "TheValue");
+        List<String> value  = tenantDao.getTenantValueForKey("TheKey", internalCallContext);
+        Assert.assertEquals(value.size(), 1);
+        Assert.assertEquals(value.get(0), "TheValue");
 
-        tenantDao.deleteTenantKey(tenant.getId(), "TheKey");
+        tenantDao.addTenantKeyValue("TheKey", "TheSecondValue", internalCallContext);
+        value  = tenantDao.getTenantValueForKey("TheKey", internalCallContext);
+        Assert.assertEquals(value.size(), 2);
+
+        tenantDao.deleteTenantKey("TheKey", internalCallContext);
+        value = tenantDao.getTenantValueForKey("TheKey", internalCallContext);
+        Assert.assertEquals(value.size(), 0);
     }
 }
diff --git a/util/src/test/java/com/ning/billing/dbi/MysqlTestingHelper.java b/util/src/test/java/com/ning/billing/dbi/MysqlTestingHelper.java
index 6432a26..bcf65bc 100644
--- a/util/src/test/java/com/ning/billing/dbi/MysqlTestingHelper.java
+++ b/util/src/test/java/com/ning/billing/dbi/MysqlTestingHelper.java
@@ -70,7 +70,7 @@ public class MysqlTestingHelper {
                 socket = new ServerSocket(0);
                 port = socket.getLocalPort();
                 socket.close();
-            } catch (IOException e) {
+            } catch (final IOException e) {
                 Assert.fail();
             }
         }
@@ -172,7 +172,7 @@ public class MysqlTestingHelper {
                 deleteRecursive(dbDir);
                 log.info("MySQLd stopped");
             }
-        } catch (Exception ex) {
+        } catch (final Exception ex) {
             //fail silently
         }
     }
@@ -231,7 +231,7 @@ public class MysqlTestingHelper {
             final String ddl;
             try {
                 ddl = IOUtils.toString(Resources.getResource("com/ning/billing/" + pack + "/ddl.sql").openStream());
-            } catch (IllegalArgumentException ignored) {
+            } catch (final IllegalArgumentException ignored) {
                 // The test doesn't have this module ddl in the classpath - that's fine
                 continue;
             }
@@ -247,7 +247,7 @@ public class MysqlTestingHelper {
         dbi.withHandle(new HandleCallback<Void>() {
             @Override
             public Void withHandle(final Handle handle) throws Exception {
-                log.debug("Executing DDL script: " + ddl);
+                log.info("Executing DDL script: " + ddl);
                 handle.createScript(ddl).execute();
                 return null;
             }