killbill-uncached

Details

diff --git a/account/src/main/java/com/ning/billing/account/api/svcs/DefaultAccountInternalApi.java b/account/src/main/java/com/ning/billing/account/api/svcs/DefaultAccountInternalApi.java
index f0189dd..b29bc91 100644
--- a/account/src/main/java/com/ning/billing/account/api/svcs/DefaultAccountInternalApi.java
+++ b/account/src/main/java/com/ning/billing/account/api/svcs/DefaultAccountInternalApi.java
@@ -46,7 +46,7 @@ public class DefaultAccountInternalApi implements AccountInternalApi {
     }
 
     @Override
-    public Account getAccountById(UUID accountId, InternalTenantContext context)
+    public Account getAccountById(final UUID accountId, final InternalTenantContext context)
             throws AccountApiException {
         final Account account = accountDao.getById(accountId, context);
         if (account == null) {
@@ -56,25 +56,31 @@ public class DefaultAccountInternalApi implements AccountInternalApi {
     }
 
     @Override
-    public void updateAccount(String externalKey, AccountData accountData,
-            InternalCallContext context) throws AccountApiException {
+    public Account getAccountByRecordId(final Long recordId,
+            final InternalTenantContext context) throws AccountApiException {
+        return accountDao.getByRecordId(recordId, context);
+    }
+
+    @Override
+    public void updateAccount(final String externalKey, final AccountData accountData,
+            final InternalCallContext context) throws AccountApiException {
         final Account account = getAccountByKey(externalKey, context);
         try {
             final Account updatedAccount = new DefaultAccount(account.getId(), accountData);
             accountDao.update(updatedAccount,context);
-        } catch (EntityPersistenceException e) {
+        } catch (final EntityPersistenceException e) {
             throw new AccountApiException(e, ErrorCode.ACCOUNT_UPDATE_FAILED);
         }
     }
 
     @Override
-    public List<AccountEmail> getEmails(UUID accountId,
-            InternalTenantContext context)  {
+    public List<AccountEmail> getEmails(final UUID accountId,
+            final InternalTenantContext context)  {
         return accountEmailDao.getEmails(accountId, context);
     }
 
     @Override
-    public Account getAccountByKey(String key, InternalTenantContext context)
+    public Account getAccountByKey(final String key, final InternalTenantContext context)
             throws AccountApiException {
         final Account account = accountDao.getAccountByKey(key, context);
         if (account == null) {
@@ -84,17 +90,17 @@ public class DefaultAccountInternalApi implements AccountInternalApi {
     }
 
     @Override
-    public void removePaymentMethod(UUID accountId, InternalCallContext context)
+    public void removePaymentMethod(final UUID accountId, final InternalCallContext context)
             throws AccountApiException {
         updatePaymentMethod(accountId, null, context);
     }
 
     @Override
-    public void updatePaymentMethod(UUID accountId, UUID paymentMethodId,
-            InternalCallContext context) throws AccountApiException {
+    public void updatePaymentMethod(final UUID accountId, final UUID paymentMethodId,
+            final InternalCallContext context) throws AccountApiException {
         try {
             accountDao.updatePaymentMethod(accountId, paymentMethodId, context);
-        } catch (EntityPersistenceException e) {
+        } catch (final EntityPersistenceException e) {
             throw new AccountApiException(e, e.getCode(), e.getMessage());
         }
     }
diff --git a/account/src/main/java/com/ning/billing/account/dao/AuditedAccountDao.java b/account/src/main/java/com/ning/billing/account/dao/AuditedAccountDao.java
index 808a78d..33da659 100644
--- a/account/src/main/java/com/ning/billing/account/dao/AuditedAccountDao.java
+++ b/account/src/main/java/com/ning/billing/account/dao/AuditedAccountDao.java
@@ -80,6 +80,12 @@ public class AuditedAccountDao implements AccountDao {
     }
 
     @Override
+    public Account getByRecordId(final Long recordId, final InternalTenantContext context) {
+        return accountSqlDao.getByRecordId(recordId, context);
+    }
+
+
+    @Override
     public List<Account> get(final InternalTenantContext context) {
         return accountSqlDao.get(context);
     }
@@ -122,13 +128,13 @@ public class AuditedAccountDao implements AccountDao {
                             context.getTenantRecordId());
                     try {
                         eventBus.postFromTransaction(creationEvent, transactionalDao, rehydratedContext);
-                    } catch (EventBusException e) {
+                    } catch (final EventBusException e) {
                         log.warn("Failed to post account creation event for account " + account.getId(), e);
                     }
                     return null;
                 }
             });
-        } catch (RuntimeException re) {
+        } catch (final RuntimeException re) {
             if (re.getCause() instanceof EntityPersistenceException) {
                 throw (EntityPersistenceException) re.getCause();
             } else if (re.getCause() instanceof DataTruncation) {
@@ -173,14 +179,14 @@ public class AuditedAccountDao implements AccountDao {
                     if (changeEvent.hasChanges()) {
                         try {
                             eventBus.postFromTransaction(changeEvent, transactional, context);
-                        } catch (EventBusException e) {
+                        } catch (final EventBusException e) {
                             log.warn("Failed to post account change event for account " + accountId, e);
                         }
                     }
                     return null;
                 }
             });
-        } catch (RuntimeException re) {
+        } catch (final RuntimeException re) {
             if (re.getCause() instanceof EntityPersistenceException) {
                 throw (EntityPersistenceException) re.getCause();
             } else {
@@ -224,14 +230,14 @@ public class AuditedAccountDao implements AccountDao {
                     if (changeEvent.hasChanges()) {
                         try {
                             eventBus.postFromTransaction(changeEvent, transactional, context);
-                        } catch (EventBusException e) {
+                        } catch (final EventBusException e) {
                             log.warn("Failed to post account change event for account " + accountId, e);
                         }
                     }
                     return null;
                 }
             });
-        } catch (RuntimeException re) {
+        } catch (final RuntimeException re) {
             if (re.getCause() instanceof EntityPersistenceException) {
                 throw (EntityPersistenceException) re.getCause();
             } else {
diff --git a/account/src/main/java/com/ning/billing/account/dao/AuditedAccountEmailDao.java b/account/src/main/java/com/ning/billing/account/dao/AuditedAccountEmailDao.java
index a3428f4..c161b31 100644
--- a/account/src/main/java/com/ning/billing/account/dao/AuditedAccountEmailDao.java
+++ b/account/src/main/java/com/ning/billing/account/dao/AuditedAccountEmailDao.java
@@ -65,6 +65,12 @@ public class AuditedAccountEmailDao extends AuditedCollectionDaoBase<AccountEmai
     }
 
     @Override
+    public AccountEmail getByRecordId(final Long recordId, final InternalTenantContext context) {
+        throw new UnsupportedOperationException();
+    }
+
+
+    @Override
     public List<AccountEmail> get(final InternalTenantContext context) {
         throw new UnsupportedOperationException();
     }
diff --git a/account/src/main/resources/com/ning/billing/account/dao/AccountSqlDao.sql.stg b/account/src/main/resources/com/ning/billing/account/dao/AccountSqlDao.sql.stg
index 45e945f..0329a2f 100644
--- a/account/src/main/resources/com/ning/billing/account/dao/AccountSqlDao.sql.stg
+++ b/account/src/main/resources/com/ning/billing/account/dao/AccountSqlDao.sql.stg
@@ -98,6 +98,12 @@ getRecordId() ::= <<
     WHERE id = :id <AND_CHECK_TENANT()>;
 >>
 
+getByRecordId() ::= <<
+    SELECT <accountFields()>
+    FROM accounts
+    WHERE record_id = :recordId <AND_CHECK_TENANT()>;
+>>
+
 getHistoryRecordId() ::= <<
     SELECT MAX(history_record_id)
     FROM account_history
diff --git a/account/src/test/java/com/ning/billing/account/dao/MockAccountDao.java b/account/src/test/java/com/ning/billing/account/dao/MockAccountDao.java
index 53d3f2e..fddfbc1 100644
--- a/account/src/test/java/com/ning/billing/account/dao/MockAccountDao.java
+++ b/account/src/test/java/com/ning/billing/account/dao/MockAccountDao.java
@@ -55,7 +55,7 @@ public class MockAccountDao implements AccountDao {
 
         try {
             eventBus.post(new DefaultAccountCreationEvent(account, null, 1L, 1L), context);
-        } catch (EventBusException ex) {
+        } catch (final EventBusException ex) {
             throw new RuntimeException(ex);
         }
     }
@@ -98,7 +98,7 @@ public class MockAccountDao implements AccountDao {
         if (changeEvent.hasChanges()) {
             try {
                 eventBus.post(changeEvent, context);
-            } catch (EventBusException ex) {
+            } catch (final EventBusException ex) {
                 throw new RuntimeException(ex);
             }
         }
@@ -107,4 +107,9 @@ public class MockAccountDao implements AccountDao {
     @Override
     public void updatePaymentMethod(final UUID accountId, final UUID paymentMethodId, final InternalCallContext context) throws EntityPersistenceException {
     }
+
+    @Override
+    public Account getByRecordId(final Long recordId, final InternalTenantContext context) {
+        return null;
+    }
 }
diff --git a/account/src/test/java/com/ning/billing/account/dao/MockAccountEmailDao.java b/account/src/test/java/com/ning/billing/account/dao/MockAccountEmailDao.java
index e6951ae..8139bf0 100644
--- a/account/src/test/java/com/ning/billing/account/dao/MockAccountEmailDao.java
+++ b/account/src/test/java/com/ning/billing/account/dao/MockAccountEmailDao.java
@@ -94,4 +94,10 @@ public class MockAccountEmailDao implements AccountEmailDao {
     @Override
     public void test(final InternalTenantContext context) {
     }
+
+    @Override
+    public AccountEmail getByRecordId(final Long recordId,
+            final InternalTenantContext context) {
+        return null;
+    }
 }
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 6a37476..81c832a 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,6 +27,8 @@ import org.skife.jdbi.v2.IDBI;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.beatrix.bus.api.ExtBusEvent;
 import com.ning.billing.beatrix.bus.api.ExternalBus;
 import com.ning.billing.beatrix.extbus.dao.ExtBusEventEntry;
@@ -40,6 +42,7 @@ import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.callcontext.UserType;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.queue.PersistentQueueBase;
+import com.ning.billing.util.svcapi.account.AccountInternalApi;
 import com.ning.billing.util.svcsapi.bus.InternalBus.EventBusException;
 
 import com.google.common.eventbus.EventBus;
@@ -58,6 +61,7 @@ public class PersistentExternalBus extends PersistentQueueBase implements Extern
     private final Clock clock;
     private final String hostname;
     private final InternalCallContextFactory internalCallContextFactory;
+    private final AccountInternalApi accountApi;
 
     private static final class EventBusDelegate extends EventBus {
 
@@ -67,7 +71,7 @@ public class PersistentExternalBus extends PersistentQueueBase implements Extern
     }
 
     @Inject
-    public PersistentExternalBus(final IDBI dbi, final Clock clock, final PersistentBusConfig config, final InternalCallContextFactory internalCallContextFactory) {
+    public PersistentExternalBus(final AccountInternalApi accountApi, final IDBI dbi, final Clock clock, final PersistentBusConfig config, final InternalCallContextFactory internalCallContextFactory) {
         super("Bus", Executors.newFixedThreadPool(config.getNbThreads(), new ThreadFactory() {
             @Override
             public Thread newThread(final Runnable r) {
@@ -81,6 +85,7 @@ public class PersistentExternalBus extends PersistentQueueBase implements Extern
         this.eventBusDelegate = new EventBusDelegate("Killbill EventBus");
         this.hostname = Hostname.get();
         this.internalCallContextFactory = internalCallContextFactory;
+        this.accountApi = accountApi;
     }
 
     public void start() {
@@ -103,11 +108,8 @@ public class PersistentExternalBus extends PersistentQueueBase implements Extern
 
         int result = 0;
         for (final ExtBusEventEntry cur : events) {
-
-            // API_FIX How do we get UUID from recordId ?
-            final UUID accountId  = null;
-            final UUID tenantId  = null;
-            final ExtBusEvent event = new DefaultBusEvent(cur.getExtBusType(), cur.getObjectType(), cur.getObjectId(), accountId, tenantId);
+            final UUID accountId = getAccountIdFromRecordId(cur.getAccountRecordId(), context);
+            final ExtBusEvent event = new DefaultBusEvent(cur.getExtBusType(), cur.getObjectType(), cur.getObjectId(), accountId, null);
             result++;
             // STEPH exception handling is done by GUAVA-- logged a bug Issue-780
             eventBusDelegate.post(event);
@@ -117,6 +119,16 @@ public class PersistentExternalBus extends PersistentQueueBase implements Extern
         return result;
     }
 
+    private final UUID getAccountIdFromRecordId(final Long recordId, final InternalCallContext context) {
+        try {
+            final Account account = accountApi.getAccountByRecordId(recordId, context);
+            return account.getId();
+        } catch (final AccountApiException e) {
+            log.warn("Failed to retrieve acount from recordId {}", recordId);
+            return null;
+        }
+    }
+
     private List<ExtBusEventEntry> getNextBusEvent(final InternalCallContext context) {
         final Date now = clock.getUTCNow().toDate();
         final Date nextAvailable = clock.getUTCNow().plus(DELTA_IN_PROCESSING_TIME_MS).toDate();
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
index 9ddb2b8..762a184 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/NotificationJson.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/NotificationJson.java
@@ -44,8 +44,7 @@ public class NotificationJson {
 
 
     public NotificationJson(final ExtBusEvent event) {
-        // API_FIX accountId NULL
-        this(event.getEventType().toString(), null /* event.getAccountId().toString() */, event.getObjectType().toString(), event.getObjectId().toString());
+        this(event.getEventType().toString(), event.getAccountId().toString(), event.getObjectType().toString(), event.getObjectId().toString());
     }
 
 
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 9062bf8..9e3fb83 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/KillbillClient.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/KillbillClient.java
@@ -163,6 +163,18 @@ public abstract class KillbillClient extends ServerTestSuiteWithEmbeddedDB {
     // ACCOUNT UTILITIES
     //
 
+    protected AccountJson getAccountById(final String id) throws Exception {
+        final String uri = JaxrsResource.ACCOUNTS_PATH + "/" + id;
+        final Response response = doGet(uri, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
+
+        final String baseJson = response.getResponseBody();
+        final AccountJson objFromJson = mapper.readValue(baseJson, AccountJson.class);
+        Assert.assertNotNull(objFromJson);
+
+        return objFromJson;
+    }
+
     protected AccountJson getAccountByExternalKey(final String externalKey) throws Exception {
         final Response response = getAccountByExternalKeyNoValidation(externalKey);
         Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestPushNotification.java b/server/src/test/java/com/ning/billing/jaxrs/TestPushNotification.java
index b6b6704..94946af 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestPushNotification.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestPushNotification.java
@@ -48,22 +48,20 @@ public class TestPushNotification extends TestJaxrsBase {
     private final static String CALLBACK_ENDPPOINT = "/callmeback";
 
     private volatile boolean callbackCompleted;
+    private volatile boolean callbackCompletedWithError;
 
 
     @BeforeMethod(groups = "slow")
     public void startServer() throws Exception {
         callbackServer = new CallbackServer(this, SERVER_PORT, CALLBACK_ENDPPOINT);
         callbackCompleted = false;
+        callbackCompletedWithError = false;
         callbackServer.startServer();
     }
 
     @AfterMethod(groups = "slow")
     public void stopServer() throws Exception {
-        final boolean success = waitForCallbacksToComplete();
-       callbackServer.stopServer();
-       if (!success) {
-           Assert.fail("Fail to see push notification callbacks after 5 sec");
-       }
+        callbackServer.stopServer();
     }
 
     private boolean waitForCallbacksToComplete() throws InterruptedException {
@@ -79,16 +77,35 @@ public class TestPushNotification extends TestJaxrsBase {
         return (remainingMs > 0);
     }
 
+    public void retrieveAccountWithAsserts(final String accountId) {
+        try {
+            // Just check we can retrieve the account with the id from the callback
+            /* final AccountJson account = */ getAccountById(accountId);
+        } catch(final Exception e) {
+            Assert.fail(e.getMessage());
+        }
+    }
+
     @Test(groups = "slow")
-    public void testTenant() throws Exception {
+    public void testPushNotification() throws Exception {
         // Register tenant for callback
         registerCallbackNotificationForTenant("http://127.0.0.1:" + SERVER_PORT + CALLBACK_ENDPPOINT);
         // Create account to trigger a push notification
         createAccount();
+
+        final boolean success = waitForCallbacksToComplete();
+        if (!success) {
+            Assert.fail("Fail to see push notification callbacks after 5 sec");
+        }
+
+        if (callbackCompletedWithError) {
+            Assert.fail("Assertion during callback failed...");
+        }
     }
 
-    public void setCompleted() {
+    public void setCompleted(final boolean withError) {
         callbackCompleted = true;
+        callbackCompletedWithError = withError;
     }
 
     public static class CallbackServer {
@@ -128,10 +145,13 @@ public class TestPushNotification extends TestJaxrsBase {
         private final TestPushNotification test;
         private final ObjectMapper objectMapper = new ObjectMapper();
 
+        private boolean withError;
+
         public CallmebackServlet(final TestPushNotification test, final int expectedNbCalls) {
             this.expectedNbCalls = expectedNbCalls;
             this.test = test;
             this.receivedCalls = new AtomicInteger(0);
+            this.withError = false;
         }
 
         @Override
@@ -140,26 +160,33 @@ public class TestPushNotification extends TestJaxrsBase {
 
             final String body = CharStreams.toString( new InputStreamReader(request.getInputStream(), "UTF-8" ));
 
+            response.setContentType("application/json");
+            response.setStatus(HttpServletResponse.SC_OK);
+
             log.info("Got body {}", body);
 
-            final NotificationJson notification =  objectMapper.readValue(body, NotificationJson.class);
-            Assert.assertEquals(notification.getEventType(), "ACCOUNT_CREATION");
-            Assert.assertEquals(notification.getObjectType(), "ACCOUNT");
-            // Not checking the ID returned we we should
+            try {
+                final NotificationJson notification =  objectMapper.readValue(body, NotificationJson.class);
+                Assert.assertEquals(notification.getEventType(), "ACCOUNT_CREATION");
+                Assert.assertEquals(notification.getObjectType(), "ACCOUNT");
+                Assert.assertNotNull(notification.getObjectId());
+                Assert.assertNotNull(notification.getAccountId());
+                Assert.assertEquals(notification.getObjectId(), notification.getAccountId());
+
+                test.retrieveAccountWithAsserts(notification.getObjectId());
+            } catch (final AssertionError e) {
+                withError = true;
+            }
 
             log.info("CallmebackServlet received {} calls , current = {}", current, body);
-
-            response.setContentType("application/json");
-            response.setStatus(HttpServletResponse.SC_OK);
-
-            stopServerWhenComplete(current);
+            stopServerWhenComplete(current, withError);
         }
 
 
-        private void stopServerWhenComplete(final int current) {
+        private void stopServerWhenComplete(final int current, final boolean withError) {
             if (current == expectedNbCalls) {
                 log.info("Excellent, we are done!");
-                test.setCompleted();
+                test.setCompleted(withError);
             }
         }
     }
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 6f59f17..16620e3 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
@@ -122,4 +122,9 @@ public class DefaultTenantDao implements TenantDao {
     public void deleteTenantKey(final String key, final InternalCallContext context) {
         tenantKVSqlDao.deleteTenantKey(key, context.getTenantRecordId());
     }
+
+    @Override
+    public Tenant getByRecordId(final Long recordId, final InternalTenantContext context) {
+        throw new UnsupportedOperationException();
+    }
 }
diff --git a/util/src/main/java/com/ning/billing/util/entity/dao/EntityDao.java b/util/src/main/java/com/ning/billing/util/entity/dao/EntityDao.java
index 3d4ab80..370f0e9 100644
--- a/util/src/main/java/com/ning/billing/util/entity/dao/EntityDao.java
+++ b/util/src/main/java/com/ning/billing/util/entity/dao/EntityDao.java
@@ -30,6 +30,8 @@ public interface EntityDao<T extends Entity> {
 
     public Long getRecordId(UUID id, InternalTenantContext context);
 
+    public T getByRecordId(Long recordId, InternalTenantContext context);
+
     public T getById(UUID id, InternalTenantContext context);
 
     public List<T> get(InternalTenantContext context);
diff --git a/util/src/main/java/com/ning/billing/util/svcapi/account/AccountInternalApi.java b/util/src/main/java/com/ning/billing/util/svcapi/account/AccountInternalApi.java
index 006278f..60b36b1 100644
--- a/util/src/main/java/com/ning/billing/util/svcapi/account/AccountInternalApi.java
+++ b/util/src/main/java/com/ning/billing/util/svcapi/account/AccountInternalApi.java
@@ -31,6 +31,8 @@ public interface AccountInternalApi {
 
     public Account getAccountById(UUID accountId, InternalTenantContext context) throws AccountApiException;
 
+    public Account getAccountByRecordId(Long recordId, InternalTenantContext context) throws AccountApiException;
+
     public void updateAccount(String key, AccountData accountData, InternalCallContext context) throws AccountApiException;
 
     public List<AccountEmail> getEmails(UUID accountId, InternalTenantContext context);