killbill-aplcache
Changes
.circleci/config.yml 2(+1 -1)
account/pom.xml 2(+1 -1)
api/pom.xml 2(+1 -1)
api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseWithAddOnsSpecifier.java 8(+4 -4)
beatrix/pom.xml 2(+1 -1)
beatrix/src/test/java/org/killbill/billing/beatrix/integration/BeatrixIntegrationModule.java 4(+4 -0)
catalog/pom.xml 2(+1 -1)
currency/pom.xml 2(+1 -1)
entitlement/pom.xml 2(+1 -1)
entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementApi.java 421(+242 -179)
entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementSpecifier.java 12(+12 -0)
entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementApiBase.java 3(+1 -2)
entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementInternalApi.java 3(+1 -2)
entitlement/src/main/java/org/killbill/billing/entitlement/dao/DefaultBlockingStateDao.java 47(+30 -17)
entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/DefaultEventsStream.java 7(+7 -0)
entitlement/src/main/java/org/killbill/billing/entitlement/logging/EntitlementLoggingHelper.java 51(+23 -28)
entitlement/src/main/resources/org/killbill/billing/entitlement/dao/BlockingStateSqlDao.sql.stg 59(+42 -17)
entitlement/src/test/java/org/killbill/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java 12(+12 -0)
invoice/pom.xml 2(+1 -1)
invoice/src/main/java/org/killbill/billing/invoice/generator/FixedAndRecurringInvoiceItemGenerator.java 6(+3 -3)
invoice/src/main/java/org/killbill/billing/invoice/usage/details/UsageConsumableInArrearTierUnitAggregate.java 3(+2 -1)
invoice/src/main/java/org/killbill/billing/invoice/usage/details/UsageInArrearTierUnitDetail.java 2(+2 -0)
invoice/src/test/java/org/killbill/billing/invoice/glue/TestInvoiceModuleWithEmbeddedDb.java 2(+2 -0)
jaxrs/pom.xml 2(+1 -1)
junction/pom.xml 2(+1 -1)
junction/src/test/java/org/killbill/billing/junction/glue/TestJunctionModuleWithEmbeddedDB.java 2(+2 -0)
junction/src/test/java/org/killbill/billing/junction/JunctionTestSuiteWithEmbeddedDB.java 12(+12 -0)
NEWS 8(+8 -0)
overdue/pom.xml 2(+1 -1)
overdue/src/test/java/org/killbill/billing/overdue/calculator/TestBillingStateCalculator.java 3(+2 -1)
payment/pom.xml 2(+1 -1)
payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentTransactionTask.java 6(+3 -3)
payment/src/main/java/org/killbill/billing/payment/core/sm/control/CompletionControlOperation.java 5(+5 -0)
payment/src/main/java/org/killbill/billing/payment/core/sm/control/DefaultControlCompleted.java 5(+5 -0)
payment/src/main/java/org/killbill/billing/payment/core/sm/payments/PaymentOperation.java 23(+2 -21)
payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java 12(+6 -6)
payment/src/main/java/org/killbill/billing/payment/provider/ExternalPaymentProviderPlugin.java 12(+6 -6)
payment/src/test/java/org/killbill/billing/payment/glue/TestPaymentModuleWithEmbeddedDB.java 2(+2 -0)
payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentProviderPlugin.java 29(+29 -0)
pom.xml 4(+2 -2)
profiles/killbill/pom.xml 2(+1 -1)
profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillbillServerModule.java 5(+3 -2)
profiles/killbill/src/test/java/org/killbill/billing/jaxrs/resources/TestDBRouterResource.java 88(+88 -0)
profiles/killbill/src/test/java/org/killbill/billing/server/security/TestKillbillJdbcTenantRealm.java 2(+1 -1)
profiles/killpay/pom.xml 2(+1 -1)
profiles/killpay/src/main/java/org/killbill/billing/server/modules/KillpayServerModule.java 5(+3 -2)
profiles/pom.xml 2(+1 -1)
subscription/pom.xml 2(+1 -1)
subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java 51(+26 -25)
subscription/src/main/java/org/killbill/billing/subscription/api/transfer/DefaultSubscriptionBaseTransferApi.java 20(+10 -10)
subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBaseApiService.java 21(+7 -14)
subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java 17(+8 -9)
subscription/src/test/java/org/killbill/billing/subscription/api/transfer/TestDefaultSubscriptionTransferApi.java 2(+1 -1)
subscription/src/test/java/org/killbill/billing/subscription/api/transfer/TestTransfer.java 6(+3 -3)
subscription/src/test/java/org/killbill/billing/subscription/api/user/TestSubscriptionHelper.java 30(+8 -22)
subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiAddOn.java 14(+7 -7)
subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiCancel.java 27(+13 -14)
subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiChangePlan.java 16(+8 -8)
subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiCreate.java 25(+12 -13)
subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiError.java 1(+0 -1)
subscription/src/test/java/org/killbill/billing/subscription/engine/dao/TestSubscriptionDao.java 83(+47 -36)
subscription/src/test/java/org/killbill/billing/subscription/glue/TestDefaultSubscriptionModuleWithEmbeddedDB.java 7(+3 -4)
subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteNoDB.java 15(+13 -2)
subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteWithEmbeddedDB.java 14(+12 -2)
tenant/pom.xml 2(+1 -1)
usage/pom.xml 2(+1 -1)
util/pom.xml 2(+1 -1)
util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoTransactionalJdbiWrapper.java 44(+9 -35)
Details
.circleci/config.yml 2(+1 -1)
diff --git a/.circleci/config.yml b/.circleci/config.yml
index b759366..b81ff31 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -17,7 +17,7 @@ jobs:
name: Setup dependencies
command: |
if [ "${CIRCLE_BRANCH}" != "master" ]; then
- for i in killbill-oss-parent killbill-api killbill-plugin-api killbill-commons killbill-platform; do
+ for i in killbill-oss-parent killbill-api killbill-plugin-api killbill-commons killbill-platform killbill-client-java; do
if [ -n "$(git ls-remote --heads https://github.com/killbill/$i.git ${CIRCLE_BRANCH})" ]; then
echo "*** Setting up $i"
mkdir -p /home/killbill/$i
account/pom.xml 2(+1 -1)
diff --git a/account/pom.xml b/account/pom.xml
index 417f676..03f02dd 100644
--- a/account/pom.xml
+++ b/account/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.19.11-SNAPSHOT</version>
+ <version>0.19.13-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-account</artifactId>
diff --git a/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountUserApi.java b/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountUserApi.java
index 798d720..46a3ea0 100644
--- a/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountUserApi.java
+++ b/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountUserApi.java
@@ -36,6 +36,8 @@ import org.killbill.billing.account.dao.AccountEmailModelDao;
import org.killbill.billing.account.dao.AccountModelDao;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.util.api.AuditLevel;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
import org.killbill.billing.util.cache.CacheControllerDispatcher;
import org.killbill.billing.util.callcontext.CallContext;
import org.killbill.billing.util.callcontext.InternalCallContextFactory;
@@ -232,4 +234,9 @@ public class DefaultAccountUserApi extends DefaultAccountApiBase implements Acco
}
}));
}
+
+ @Override
+ public List<AuditLogWithHistory> getAuditLogsWithHistoryForId(final UUID accountId, final AuditLevel auditLevel, final TenantContext tenantContext) throws AccountApiException {
+ return accountDao.getAuditLogsWithHistoryForId(accountId, auditLevel, internalCallContextFactory.createInternalTenantContext(accountId, tenantContext));
+ }
}
diff --git a/account/src/main/java/org/killbill/billing/account/dao/AccountDao.java b/account/src/main/java/org/killbill/billing/account/dao/AccountDao.java
index 1446204..1e0e94c 100644
--- a/account/src/main/java/org/killbill/billing/account/dao/AccountDao.java
+++ b/account/src/main/java/org/killbill/billing/account/dao/AccountDao.java
@@ -23,6 +23,8 @@ import org.killbill.billing.account.api.Account;
import org.killbill.billing.account.api.AccountApiException;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.util.api.AuditLevel;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
import org.killbill.billing.util.entity.Pagination;
import org.killbill.billing.util.entity.dao.EntityDao;
@@ -54,4 +56,6 @@ public interface AccountDao extends EntityDao<AccountModelDao, Account, AccountA
Integer getAccountBCD(UUID accountId, InternalTenantContext context);
List<AccountModelDao> getAccountsByParentId(UUID parentAccountId, InternalTenantContext context);
+
+ List<AuditLogWithHistory> getAuditLogsWithHistoryForId(UUID accountId, AuditLevel auditLevel, InternalTenantContext context) throws AccountApiException;
}
diff --git a/account/src/main/java/org/killbill/billing/account/dao/DefaultAccountDao.java b/account/src/main/java/org/killbill/billing/account/dao/DefaultAccountDao.java
index a2a5ea3..dddab19 100644
--- a/account/src/main/java/org/killbill/billing/account/dao/DefaultAccountDao.java
+++ b/account/src/main/java/org/killbill/billing/account/dao/DefaultAccountDao.java
@@ -38,12 +38,16 @@ import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.entity.EntityPersistenceException;
import org.killbill.billing.events.AccountChangeInternalEvent;
import org.killbill.billing.events.AccountCreationInternalEvent;
+import org.killbill.billing.util.api.AuditLevel;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
import org.killbill.billing.util.audit.ChangeType;
+import org.killbill.billing.util.audit.dao.AuditDao;
import org.killbill.billing.util.cache.Cachable.CacheType;
import org.killbill.billing.util.cache.CacheController;
import org.killbill.billing.util.cache.CacheControllerDispatcher;
import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.dao.NonEntityDao;
+import org.killbill.billing.util.dao.TableName;
import org.killbill.billing.util.entity.DefaultPagination;
import org.killbill.billing.util.entity.Pagination;
import org.killbill.billing.util.entity.dao.DefaultPaginationSqlDaoHelper.Ordering;
@@ -72,15 +76,17 @@ public class DefaultAccountDao extends EntityDaoBase<AccountModelDao, Account, A
private final PersistentBus eventBus;
private final InternalCallContextFactory internalCallContextFactory;
private final Clock clock;
+ private final AuditDao auditDao;
@Inject
public DefaultAccountDao(final IDBI dbi, @Named(MAIN_RO_IDBI_NAMED) final IDBI roDbi, final PersistentBus eventBus, final Clock clock, final CacheControllerDispatcher cacheControllerDispatcher,
- final InternalCallContextFactory internalCallContextFactory, final NonEntityDao nonEntityDao) {
+ final InternalCallContextFactory internalCallContextFactory, final NonEntityDao nonEntityDao, final AuditDao auditDao) {
super(new EntitySqlDaoTransactionalJdbiWrapper(dbi, roDbi, clock, cacheControllerDispatcher, nonEntityDao, internalCallContextFactory), AccountSqlDao.class);
this.accountImmutableCacheController = cacheControllerDispatcher.getCacheController(CacheType.ACCOUNT_IMMUTABLE);
this.eventBus = eventBus;
this.internalCallContextFactory = internalCallContextFactory;
this.clock = clock;
+ this.auditDao = auditDao;
}
@Override
@@ -211,7 +217,7 @@ public class DefaultAccountDao extends EntityDaoBase<AccountModelDao, Account, A
context.getAccountRecordId(),
context.getTenantRecordId(),
context.getUserToken(),
- clock.getUTCNow());
+ context.getCreatedDate());
try {
eventBus.postFromTransaction(changeEvent, entitySqlDaoWrapperFactory.getHandle().getConnection());
} catch (final EventBusException e) {
@@ -248,7 +254,7 @@ public class DefaultAccountDao extends EntityDaoBase<AccountModelDao, Account, A
context.getAccountRecordId(),
context.getTenantRecordId(),
context.getUserToken(),
- clock.getUTCNow());
+ context.getCreatedDate());
try {
eventBus.postFromTransaction(changeEvent, entitySqlDaoWrapperFactory.getHandle().getConnection());
@@ -317,4 +323,15 @@ public class DefaultAccountDao extends EntityDaoBase<AccountModelDao, Account, A
}
});
}
+
+ @Override
+ public List<AuditLogWithHistory> getAuditLogsWithHistoryForId(final UUID accountId, final AuditLevel auditLevel, final InternalTenantContext context) throws AccountApiException {
+ return transactionalSqlDao.execute(true, AccountApiException.class, new EntitySqlDaoTransactionWrapper<List<AuditLogWithHistory>>() {
+ @Override
+ public List<AuditLogWithHistory> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) {
+ final AccountSqlDao transactional = entitySqlDaoWrapperFactory.become(AccountSqlDao.class);
+ return auditDao.getAuditLogsWithHistoryForId(transactional, TableName.ACCOUNT, accountId, auditLevel, context);
+ }
+ });
+ }
}
diff --git a/account/src/main/java/org/killbill/billing/account/glue/DefaultAccountModule.java b/account/src/main/java/org/killbill/billing/account/glue/DefaultAccountModule.java
index c5931f6..aaf2372 100644
--- a/account/src/main/java/org/killbill/billing/account/glue/DefaultAccountModule.java
+++ b/account/src/main/java/org/killbill/billing/account/glue/DefaultAccountModule.java
@@ -30,6 +30,8 @@ import org.killbill.billing.account.dao.AccountDao;
import org.killbill.billing.account.dao.DefaultAccountDao;
import org.killbill.billing.glue.AccountModule;
import org.killbill.billing.platform.api.KillbillConfigSource;
+import org.killbill.billing.util.audit.dao.AuditDao;
+import org.killbill.billing.util.audit.dao.DefaultAuditDao;
import org.killbill.billing.util.glue.KillBillModule;
public class DefaultAccountModule extends KillBillModule implements AccountModule {
diff --git a/account/src/test/java/org/killbill/billing/account/AccountTestSuiteNoDB.java b/account/src/test/java/org/killbill/billing/account/AccountTestSuiteNoDB.java
index 447b18d..d892e83 100644
--- a/account/src/test/java/org/killbill/billing/account/AccountTestSuiteNoDB.java
+++ b/account/src/test/java/org/killbill/billing/account/AccountTestSuiteNoDB.java
@@ -18,17 +18,11 @@
package org.killbill.billing.account;
-import org.killbill.billing.account.api.ImmutableAccountInternalApi;
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.BeforeMethod;
-
import org.killbill.billing.GuicyKillbillTestSuiteNoDB;
import org.killbill.billing.account.api.AccountUserApi;
+import org.killbill.billing.account.api.ImmutableAccountInternalApi;
import org.killbill.billing.account.dao.AccountDao;
import org.killbill.billing.account.glue.TestAccountModuleNoDB;
-import org.killbill.bus.api.PersistentBus;
-import org.killbill.clock.Clock;
import org.killbill.billing.util.audit.dao.AuditDao;
import org.killbill.billing.util.cache.CacheControllerDispatcher;
import org.killbill.billing.util.customfield.dao.CustomFieldDao;
@@ -36,6 +30,11 @@ import org.killbill.billing.util.dao.NonEntityDao;
import org.killbill.billing.util.tag.api.user.TagEventBuilder;
import org.killbill.billing.util.tag.dao.TagDao;
import org.killbill.billing.util.tag.dao.TagDefinitionDao;
+import org.killbill.bus.api.PersistentBus;
+import org.killbill.clock.Clock;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
import com.google.inject.Guice;
import com.google.inject.Inject;
@@ -70,12 +69,20 @@ public abstract class AccountTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
@BeforeClass(groups = "fast")
protected void beforeClass() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
final Injector injector = Guice.createInjector(new TestAccountModuleNoDB(configSource));
injector.injectMembers(this);
}
@BeforeMethod(groups = "fast")
public void beforeMethod() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
bus.start();
}
diff --git a/account/src/test/java/org/killbill/billing/account/AccountTestSuiteWithEmbeddedDB.java b/account/src/test/java/org/killbill/billing/account/AccountTestSuiteWithEmbeddedDB.java
index 1e146a5..2f20eda 100644
--- a/account/src/test/java/org/killbill/billing/account/AccountTestSuiteWithEmbeddedDB.java
+++ b/account/src/test/java/org/killbill/billing/account/AccountTestSuiteWithEmbeddedDB.java
@@ -69,12 +69,20 @@ public abstract class AccountTestSuiteWithEmbeddedDB extends GuicyKillbillTestSu
@BeforeClass(groups = "slow")
protected void beforeClass() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
final Injector injector = Guice.createInjector(new TestAccountModuleWithEmbeddedDB(configSource));
injector.injectMembers(this);
}
@BeforeMethod(groups = "slow")
public void beforeMethod() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
super.beforeMethod();
controllerDispatcher.clearAll();
bus.start();
diff --git a/account/src/test/java/org/killbill/billing/account/dao/MockAccountDao.java b/account/src/test/java/org/killbill/billing/account/dao/MockAccountDao.java
index 9d097c4..077ca8d 100644
--- a/account/src/test/java/org/killbill/billing/account/dao/MockAccountDao.java
+++ b/account/src/test/java/org/killbill/billing/account/dao/MockAccountDao.java
@@ -37,6 +37,8 @@ import org.killbill.billing.account.api.user.DefaultAccountCreationEvent.Default
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.events.AccountChangeInternalEvent;
+import org.killbill.billing.util.api.AuditLevel;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.entity.DefaultPagination;
import org.killbill.billing.util.entity.Pagination;
@@ -186,4 +188,9 @@ public class MockAccountDao extends MockEntityDaoBase<AccountModelDao, Account,
}
}));
}
+
+ @Override
+ public List<AuditLogWithHistory> getAuditLogsWithHistoryForId(final UUID accountId, final AuditLevel auditLevel, final InternalTenantContext context) throws AccountApiException {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/account/src/test/java/org/killbill/billing/account/dao/TestAccountDao.java b/account/src/test/java/org/killbill/billing/account/dao/TestAccountDao.java
index 3db923f..8a97b2a 100644
--- a/account/src/test/java/org/killbill/billing/account/dao/TestAccountDao.java
+++ b/account/src/test/java/org/killbill/billing/account/dao/TestAccountDao.java
@@ -26,7 +26,6 @@ import org.joda.time.DateTimeZone;
import org.killbill.billing.ErrorCode;
import org.killbill.billing.ObjectType;
import org.killbill.billing.account.AccountTestSuiteWithEmbeddedDB;
-import org.killbill.billing.account.api.Account;
import org.killbill.billing.account.api.AccountApiException;
import org.killbill.billing.account.api.AccountData;
import org.killbill.billing.account.api.AccountEmail;
@@ -40,10 +39,10 @@ import org.killbill.billing.util.api.CustomFieldApiException;
import org.killbill.billing.util.api.TagApiException;
import org.killbill.billing.util.api.TagDefinitionApiException;
import org.killbill.billing.util.audit.AuditLog;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
import org.killbill.billing.util.audit.ChangeType;
import org.killbill.billing.util.audit.DefaultAccountAuditLogs;
import org.killbill.billing.util.customfield.dao.CustomFieldModelDao;
-import org.killbill.billing.util.dao.EntityHistoryModelDao;
import org.killbill.billing.util.dao.TableName;
import org.killbill.billing.util.entity.Pagination;
import org.killbill.billing.util.tag.DescriptiveTag;
@@ -214,17 +213,18 @@ public class TestAccountDao extends AccountTestSuiteWithEmbeddedDB {
final AccountModelDao account = createTestAccount();
accountDao.create(account, internalCallContext);
final AccountModelDao createdAccount = accountDao.getAccountByKey(account.getExternalKey(), internalCallContext);
-
- final List<EntityHistoryModelDao<AccountModelDao, Account>> history1 = getAccountHistory(createdAccount.getRecordId());
- Assert.assertEquals(history1.size(), 1);
- Assert.assertEquals(history1.get(0).getChangeType(), ChangeType.INSERT);
- Assert.assertEquals(history1.get(0).getEntity().getAccountRecordId(), createdAccount.getRecordId());
- Assert.assertEquals(history1.get(0).getEntity().getTenantRecordId(), createdAccount.getTenantRecordId());
- Assert.assertEquals(history1.get(0).getEntity().getExternalKey(), createdAccount.getExternalKey());
- Assert.assertEquals(history1.get(0).getEntity().getMigrated(), createdAccount.getMigrated());
- Assert.assertEquals(history1.get(0).getEntity().getIsNotifiedForInvoices(), createdAccount.getIsNotifiedForInvoices());
- Assert.assertEquals(history1.get(0).getEntity().getTimeZone(), createdAccount.getTimeZone());
- Assert.assertEquals(history1.get(0).getEntity().getLocale(), createdAccount.getLocale());
+ List<AuditLogWithHistory> auditLogsWithHistory = accountDao.getAuditLogsWithHistoryForId(account.getId(), AuditLevel.FULL, internalCallContext);
+ Assert.assertEquals(auditLogsWithHistory.size(), 1);
+
+ final AccountModelDao history1 = (AccountModelDao) auditLogsWithHistory.get(0).getEntity();
+ Assert.assertEquals(auditLogsWithHistory.get(0).getChangeType(), ChangeType.INSERT);
+ Assert.assertEquals(history1.getAccountRecordId(), createdAccount.getRecordId());
+ Assert.assertEquals(history1.getTenantRecordId(), createdAccount.getTenantRecordId());
+ Assert.assertEquals(history1.getExternalKey(), createdAccount.getExternalKey());
+ Assert.assertEquals(history1.getMigrated(), createdAccount.getMigrated());
+ Assert.assertEquals(history1.getIsNotifiedForInvoices(), createdAccount.getIsNotifiedForInvoices());
+ Assert.assertEquals(history1.getTimeZone(), createdAccount.getTimeZone());
+ Assert.assertEquals(history1.getLocale(), createdAccount.getLocale());
final AccountData accountData = new MockAccountBuilder(new DefaultAccount(account)).migrated(false)
.isNotifiedForInvoices(false)
@@ -236,18 +236,19 @@ public class TestAccountDao extends AccountTestSuiteWithEmbeddedDB {
final AccountModelDao retrievedAccount = accountDao.getAccountByKey(account.getExternalKey(), internalCallContext);
checkAccountsEqual(retrievedAccount, updatedAccount);
-
- final List<EntityHistoryModelDao<AccountModelDao, Account>> history2 = getAccountHistory(createdAccount.getRecordId());
- Assert.assertEquals(history2.size(), 2);
- Assert.assertEquals(history2.get(0).getChangeType(), ChangeType.INSERT);
- Assert.assertEquals(history2.get(1).getChangeType(), ChangeType.UPDATE);
- Assert.assertEquals(history2.get(1).getEntity().getAccountRecordId(), retrievedAccount.getRecordId());
- Assert.assertEquals(history2.get(1).getEntity().getTenantRecordId(), retrievedAccount.getTenantRecordId());
- Assert.assertEquals(history2.get(1).getEntity().getExternalKey(), retrievedAccount.getExternalKey());
- Assert.assertEquals(history2.get(1).getEntity().getMigrated(), retrievedAccount.getMigrated());
- Assert.assertEquals(history2.get(1).getEntity().getIsNotifiedForInvoices(), retrievedAccount.getIsNotifiedForInvoices());
- Assert.assertEquals(history2.get(1).getEntity().getTimeZone(), retrievedAccount.getTimeZone());
- Assert.assertEquals(history2.get(1).getEntity().getLocale(), retrievedAccount.getLocale());
+ auditLogsWithHistory = accountDao.getAuditLogsWithHistoryForId(retrievedAccount.getId(), AuditLevel.FULL, internalCallContext);
+ Assert.assertEquals(auditLogsWithHistory.size(), 2);
+
+ final AccountModelDao history2 = (AccountModelDao) auditLogsWithHistory.get(1).getEntity();
+ Assert.assertEquals(auditLogsWithHistory.get(0).getChangeType(), ChangeType.INSERT);
+ Assert.assertEquals(auditLogsWithHistory.get(1).getChangeType(), ChangeType.UPDATE);
+ Assert.assertEquals(history2.getAccountRecordId(), retrievedAccount.getRecordId());
+ Assert.assertEquals(history2.getTenantRecordId(), retrievedAccount.getTenantRecordId());
+ Assert.assertEquals(history2.getExternalKey(), retrievedAccount.getExternalKey());
+ Assert.assertEquals(history2.getMigrated(), retrievedAccount.getMigrated());
+ Assert.assertEquals(history2.getIsNotifiedForInvoices(), retrievedAccount.getIsNotifiedForInvoices());
+ Assert.assertEquals(history2.getTimeZone(), retrievedAccount.getTimeZone());
+ Assert.assertEquals(history2.getLocale(), retrievedAccount.getLocale());
final AccountData accountData2 = new MockAccountBuilder(new DefaultAccount(updatedAccount)).isNotifiedForInvoices(true)
.locale("en_US")
@@ -257,19 +258,20 @@ public class TestAccountDao extends AccountTestSuiteWithEmbeddedDB {
final AccountModelDao retrievedAccount2 = accountDao.getAccountByKey(account.getExternalKey(), internalCallContext);
checkAccountsEqual(retrievedAccount2, updatedAccount2);
-
- final List<EntityHistoryModelDao<AccountModelDao, Account>> history3 = getAccountHistory(createdAccount.getRecordId());
- Assert.assertEquals(history3.size(), 3);
- Assert.assertEquals(history3.get(0).getChangeType(), ChangeType.INSERT);
- Assert.assertEquals(history3.get(1).getChangeType(), ChangeType.UPDATE);
- Assert.assertEquals(history3.get(2).getChangeType(), ChangeType.UPDATE);
- Assert.assertEquals(history3.get(2).getEntity().getAccountRecordId(), retrievedAccount2.getRecordId());
- Assert.assertEquals(history3.get(2).getEntity().getTenantRecordId(), retrievedAccount2.getTenantRecordId());
- Assert.assertEquals(history3.get(2).getEntity().getExternalKey(), retrievedAccount2.getExternalKey());
- Assert.assertEquals(history3.get(2).getEntity().getMigrated(), retrievedAccount2.getMigrated());
- Assert.assertEquals(history3.get(2).getEntity().getIsNotifiedForInvoices(), retrievedAccount2.getIsNotifiedForInvoices());
- Assert.assertEquals(history3.get(2).getEntity().getTimeZone(), retrievedAccount2.getTimeZone());
- Assert.assertEquals(history3.get(2).getEntity().getLocale(), retrievedAccount2.getLocale());
+ auditLogsWithHistory = accountDao.getAuditLogsWithHistoryForId(retrievedAccount2.getId(), AuditLevel.FULL, internalCallContext);
+ Assert.assertEquals(auditLogsWithHistory.size(), 3);
+
+ final AccountModelDao history3 = (AccountModelDao) auditLogsWithHistory.get(2).getEntity();
+ Assert.assertEquals(auditLogsWithHistory.get(0).getChangeType(), ChangeType.INSERT);
+ Assert.assertEquals(auditLogsWithHistory.get(1).getChangeType(), ChangeType.UPDATE);
+ Assert.assertEquals(auditLogsWithHistory.get(2).getChangeType(), ChangeType.UPDATE);
+ Assert.assertEquals(history3.getAccountRecordId(), retrievedAccount2.getRecordId());
+ Assert.assertEquals(history3.getTenantRecordId(), retrievedAccount2.getTenantRecordId());
+ Assert.assertEquals(history3.getExternalKey(), retrievedAccount2.getExternalKey());
+ Assert.assertEquals(history3.getMigrated(), retrievedAccount2.getMigrated());
+ Assert.assertEquals(history3.getIsNotifiedForInvoices(), retrievedAccount2.getIsNotifiedForInvoices());
+ Assert.assertEquals(history3.getTimeZone(), retrievedAccount2.getTimeZone());
+ Assert.assertEquals(history3.getLocale(), retrievedAccount2.getLocale());
}
@Test(groups = "slow", description = "Test Account DAO: payment method update")
@@ -399,10 +401,4 @@ public class TestAccountDao extends AccountTestSuiteWithEmbeddedDB {
Assert.assertEquals(auditLogsForAccountEmail2.size(), 1);
Assert.assertEquals(auditLogsForAccountEmail2.get(0).getChangeType(), ChangeType.INSERT);
}
-
- private List<EntityHistoryModelDao<AccountModelDao, Account>> getAccountHistory(final Long accountRecordId) {
- // See https://github.com/killbill/killbill/issues/335
- final AccountSqlDao accountSqlDao = dbi.onDemand(AccountSqlDao.class);
- return accountSqlDao.getHistoryForTargetRecordId(accountRecordId, internalCallContext);
- }
}
api/pom.xml 2(+1 -1)
diff --git a/api/pom.xml b/api/pom.xml
index cd2c6d5..c25649f 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.19.11-SNAPSHOT</version>
+ <version>0.19.13-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-internal-api</artifactId>
diff --git a/api/src/main/java/org/killbill/billing/entitlement/EventsStream.java b/api/src/main/java/org/killbill/billing/entitlement/EventsStream.java
index 20027f2..a62fc85 100644
--- a/api/src/main/java/org/killbill/billing/entitlement/EventsStream.java
+++ b/api/src/main/java/org/killbill/billing/entitlement/EventsStream.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2017 Groupon, Inc
- * Copyright 2014-2017 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
*
* The Billing Project 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
@@ -64,6 +64,8 @@ public interface EventsStream {
boolean isBlockChange(final DateTime effectiveDate);
+ boolean isBlockEntitlement(final DateTime effectiveDate);
+
int getDefaultBillCycleDayLocal();
Collection<BlockingState> getPendingEntitlementCancellationEvents();
diff --git a/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseInternalApi.java b/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseInternalApi.java
index ac1df1a..0728a12 100644
--- a/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseInternalApi.java
+++ b/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseInternalApi.java
@@ -65,6 +65,8 @@ public interface SubscriptionBaseInternalApi {
public Iterable<UUID> getNonAOSubscriptionIdsForKey(String bundleKey, InternalTenantContext context);
+ public SubscriptionBaseBundle getActiveBundleForKey(String bundleKey, Catalog catalog, InternalTenantContext context);
+
public List<SubscriptionBase> getSubscriptionsForBundle(UUID bundleId, DryRunArguments dryRunArguments, InternalTenantContext context)
throws SubscriptionBaseApiException;
diff --git a/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseWithAddOnsSpecifier.java b/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseWithAddOnsSpecifier.java
index e6a7099..4b95243 100644
--- a/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseWithAddOnsSpecifier.java
+++ b/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseWithAddOnsSpecifier.java
@@ -19,7 +19,7 @@ package org.killbill.billing.subscription.api;
import java.util.UUID;
-import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
import org.killbill.billing.entitlement.api.EntitlementSpecifier;
public class SubscriptionBaseWithAddOnsSpecifier {
@@ -27,13 +27,13 @@ public class SubscriptionBaseWithAddOnsSpecifier {
private final UUID bundleId;
private final String bundleExternalKey;
private final Iterable<EntitlementSpecifier> entitlementSpecifiers;
- private final DateTime billingEffectiveDate;
+ private final LocalDate billingEffectiveDate;
private final boolean isMigrated;
public SubscriptionBaseWithAddOnsSpecifier(final UUID bundleId,
final String bundleExternalKey,
final Iterable<EntitlementSpecifier> entitlementSpecifiers,
- final DateTime billingEffectiveDate,
+ final LocalDate billingEffectiveDate,
final boolean isMigrated) {
this.bundleId = bundleId;
this.bundleExternalKey = bundleExternalKey;
@@ -54,7 +54,7 @@ public class SubscriptionBaseWithAddOnsSpecifier {
return entitlementSpecifiers;
}
- public DateTime getBillingEffectiveDate() {
+ public LocalDate getBillingEffectiveDate() {
return billingEffectiveDate;
}
beatrix/pom.xml 2(+1 -1)
diff --git a/beatrix/pom.xml b/beatrix/pom.xml
index 61adc18..e6c9447 100644
--- a/beatrix/pom.xml
+++ b/beatrix/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.19.11-SNAPSHOT</version>
+ <version>0.19.13-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-beatrix</artifactId>
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/BeatrixTestSuiteNoDB.java b/beatrix/src/test/java/org/killbill/billing/beatrix/BeatrixTestSuiteNoDB.java
index 0b30880..9e1af49 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/BeatrixTestSuiteNoDB.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/BeatrixTestSuiteNoDB.java
@@ -27,6 +27,10 @@ public abstract class BeatrixTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
@BeforeClass(groups = "fast")
protected void beforeClass() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
final Injector injector = Guice.createInjector(new BeatrixIntegrationModuleNoDB(configSource));
injector.injectMembers(this);
}
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/BeatrixIntegrationModule.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/BeatrixIntegrationModule.java
index 0fae546..6bdf4e5 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/BeatrixIntegrationModule.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/BeatrixIntegrationModule.java
@@ -24,6 +24,7 @@ import org.killbill.billing.GuicyKillbillTestWithEmbeddedDBModule;
import org.killbill.billing.account.glue.DefaultAccountModule;
import org.killbill.billing.api.TestApiListener;
import org.killbill.billing.beatrix.glue.BeatrixModule;
+import org.killbill.billing.beatrix.integration.db.TestDBRouterAPI;
import org.killbill.billing.beatrix.integration.overdue.IntegrationTestOverdueModule;
import org.killbill.billing.beatrix.util.AccountChecker;
import org.killbill.billing.beatrix.util.AuditChecker;
@@ -57,6 +58,7 @@ import org.killbill.billing.util.glue.ExportModule;
import org.killbill.billing.util.glue.GlobalLockerModule;
import org.killbill.billing.util.glue.KillBillModule;
import org.killbill.billing.util.glue.KillBillShiroModule;
+import org.killbill.billing.util.glue.KillbillApiAopModule;
import org.killbill.billing.util.glue.NodesModule;
import org.killbill.billing.util.glue.NonEntityDaoModule;
import org.killbill.billing.util.glue.RecordIdModule;
@@ -111,6 +113,7 @@ public class BeatrixIntegrationModule extends KillBillModule {
install(new BroadcastModule(configSource));
install(new KillBillShiroModuleOnlyIniRealm(configSource));
install(new BeatrixModule(configSource));
+ install(new KillbillApiAopModule());
bind(AccountChecker.class).asEagerSingleton();
bind(SubscriptionChecker.class).asEagerSingleton();
@@ -119,6 +122,7 @@ public class BeatrixIntegrationModule extends KillBillModule {
bind(RefundChecker.class).asEagerSingleton();
bind(AuditChecker.class).asEagerSingleton();
bind(TestApiListener.class).asEagerSingleton();
+ bind(TestDBRouterAPI.class).asEagerSingleton();
}
private final class DefaultInvoiceModuleWithSwitchRepairLogic extends DefaultInvoiceModule {
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/db/TestDBRouter.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/db/TestDBRouter.java
new file mode 100644
index 0000000..a8d4727
--- /dev/null
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/db/TestDBRouter.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.beatrix.integration.db;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.inject.Inject;
+
+import org.joda.time.DateTime;
+import org.killbill.billing.account.api.Account;
+import org.killbill.billing.api.TestApiListener.NextEvent;
+import org.killbill.billing.beatrix.integration.TestIntegrationBase;
+import org.killbill.billing.callcontext.DefaultTenantContext;
+import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.ProductCategory;
+import org.killbill.billing.entitlement.api.DefaultEntitlement;
+import org.killbill.billing.notification.plugin.api.ExtBusEvent;
+import org.killbill.billing.osgi.api.ROTenantContext;
+import org.killbill.billing.util.callcontext.TenantContext;
+import org.killbill.bus.api.PersistentBus.EventBusException;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.eventbus.Subscribe;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.awaitility.Awaitility.await;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+
+public class TestDBRouter extends TestIntegrationBase {
+
+ @Inject
+ private TestDBRouterAPI testDBRouterAPI;
+
+ private PublicListener publicListener;
+ private AtomicInteger externalBusCount;
+
+ @Override
+ @BeforeMethod(groups = "slow")
+ public void beforeMethod() throws Exception {
+ super.beforeMethod();
+
+ this.externalBusCount = new AtomicInteger(0);
+ testDBRouterAPI.reset();
+ }
+
+ @Override
+ protected void registerHandlers() throws EventBusException {
+ super.registerHandlers();
+
+ publicListener = new PublicListener();
+ externalBus.register(publicListener);
+ }
+
+ @AfterMethod(groups = "slow")
+ public void afterMethod() throws Exception {
+ externalBus.unregister(publicListener);
+ super.afterMethod();
+ }
+
+ @Test(groups = "slow")
+ public void testWithBusEvents() throws Exception {
+ final DateTime initialDate = new DateTime(2012, 2, 1, 0, 3, 42, 0, testTimeZone);
+ clock.setTime(initialDate);
+
+ final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(2));
+ assertNotNull(account);
+
+ final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+ assertNotNull(bpEntitlement);
+
+ await().atMost(10, SECONDS)
+ .until(new Callable<Boolean>() {
+ @Override
+ public Boolean call() throws Exception {
+ // Expecting ACCOUNT_CREATE, ACCOUNT_CHANGE, SUBSCRIPTION_CREATION (2), ENTITLEMENT_CREATE INVOICE_CREATION
+ return externalBusCount.get() == 6;
+ }
+ });
+ }
+
+ private void assertNbCalls(final int expectedNbRWCalls, final int expectedNbROCalls) {
+ assertEquals(testDBRouterAPI.getNbRWCalls(), expectedNbRWCalls);
+ assertEquals(testDBRouterAPI.getNbRoCalls(), expectedNbROCalls);
+ }
+
+ public class PublicListener {
+
+ @Subscribe
+ public void handleExternalEvents(final ExtBusEvent event) {
+ testDBRouterAPI.reset();
+
+ final TenantContext tenantContext = new DefaultTenantContext(callContext.getAccountId(), callContext.getTenantId());
+ // Only RO tenant will trigger use of RO DBI (initiated by plugins)
+ final ROTenantContext roTenantContext = new ROTenantContext(tenantContext);
+
+ // RO calls goes to RW DB by default
+ testDBRouterAPI.doROCall(tenantContext);
+ assertNbCalls(1, 0);
+
+ testDBRouterAPI.doROCall(callContext);
+ assertNbCalls(2, 0);
+
+ // Even if the thread is dirty (previous RW calls), the plugin asked for RO DBI
+ testDBRouterAPI.doROCall(roTenantContext);
+ assertNbCalls(2, 1);
+
+ // Make sure subsequent calls go back to the RW DB
+ testDBRouterAPI.doROCall(tenantContext);
+ assertNbCalls(3, 1);
+
+ testDBRouterAPI.doRWCall(callContext);
+ assertNbCalls(4, 1);
+
+ testDBRouterAPI.doROCall(roTenantContext);
+ assertNbCalls(4, 2);
+
+ testDBRouterAPI.doROCall(callContext);
+ assertNbCalls(5, 2);
+
+ testDBRouterAPI.doROCall(tenantContext);
+ assertNbCalls(6, 2);
+
+ testDBRouterAPI.doChainedROCall(tenantContext);
+ assertNbCalls(7, 2);
+
+ testDBRouterAPI.doChainedRWCall(callContext);
+ assertNbCalls(8, 2);
+
+ // Increment only if there are no errors
+ externalBusCount.incrementAndGet();
+ }
+ }
+}
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/db/TestDBRouterAPI.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/db/TestDBRouterAPI.java
new file mode 100644
index 0000000..73eb7c1
--- /dev/null
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/db/TestDBRouterAPI.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.beatrix.integration.db;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.inject.Inject;
+
+import org.killbill.billing.KillbillApi;
+import org.killbill.billing.util.callcontext.CallContext;
+import org.killbill.billing.util.callcontext.TenantContext;
+import org.killbill.billing.util.entity.dao.DBRouterUntyped;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.skife.jdbi.v2.Handle;
+import org.skife.jdbi.v2.IDBI;
+
+public class TestDBRouterAPI implements KillbillApi {
+
+ private final AtomicInteger rwCalls = new AtomicInteger(0);
+ private final AtomicInteger roCalls = new AtomicInteger(0);
+
+ private final DBRouterUntyped dbRouter;
+
+ @Inject
+ public TestDBRouterAPI() {
+ final IDBI dbi = Mockito.mock(IDBI.class);
+ Mockito.when(dbi.open()).thenAnswer(new Answer<Handle>() {
+ @Override
+ public Handle answer(final InvocationOnMock invocation) {
+ rwCalls.incrementAndGet();
+ return null;
+ }
+ });
+ final IDBI roDbi = Mockito.mock(IDBI.class);
+ Mockito.when(roDbi.open()).thenAnswer(new Answer<Handle>() {
+ @Override
+ public Handle answer(final InvocationOnMock invocation) {
+ roCalls.incrementAndGet();
+ return null;
+ }
+ });
+
+ this.dbRouter = new DBRouterUntyped(dbi, roDbi);
+ }
+
+ public void reset() {
+ rwCalls.set(0);
+ roCalls.set(0);
+ }
+
+ public void doRWCall(final CallContext callContext) {
+ dbRouter.getHandle(false);
+ }
+
+ public void doROCall(final TenantContext tenantContext) {
+ dbRouter.getHandle(true);
+ }
+
+ // Nesting dolls
+ public void doChainedROCall(final TenantContext tenantContext) {
+ doROCall(tenantContext);
+ }
+
+ // Nesting dolls
+ public void doChainedRWCall(final CallContext callContext) {
+ doRWCall(callContext);
+ }
+
+ public int getNbRWCalls() {
+ return rwCalls.get();
+ }
+
+ public int getNbRoCalls() {
+ return roCalls.get();
+ }
+}
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
index 33fe5ab..504dafe 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
@@ -120,6 +120,7 @@ import org.killbill.billing.util.dao.NonEntityDao;
import org.killbill.billing.util.nodes.KillbillNodesApi;
import org.killbill.billing.util.tag.ControlTagType;
import org.killbill.bus.api.PersistentBus;
+import org.killbill.bus.api.PersistentBus.EventBusException;
import org.skife.config.ConfigurationObjectFactory;
import org.skife.config.TimeSpan;
import org.slf4j.Logger;
@@ -306,6 +307,10 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB implemen
@BeforeClass(groups = "slow")
public void beforeClass() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
final InvoiceConfig defaultInvoiceConfig = new ConfigurationObjectFactory(skifeConfigSource).build(InvoiceConfig.class);
invoiceConfig = new ConfigurableInvoiceConfig(defaultInvoiceConfig);
// The default value is 50, i.e. wait 50 x 100ms = 5s to get the lock. This isn't always enough and can lead to random tests failures
@@ -335,12 +340,16 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB implemen
// Start services
lifecycle.fireStartupSequencePriorEventRegistration();
- busService.getBus().register(busHandler);
+ registerHandlers();
lifecycle.fireStartupSequencePostEventRegistration();
paymentPlugin.clear();
}
+ protected void registerHandlers() throws EventBusException {
+ busService.getBus().register(busHandler);
+ }
+
@AfterMethod(groups = "slow")
public void afterMethod() throws Exception {
lifecycle.fireShutdownSequencePriorEventUnRegistration();
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithFakeKPMPlugin.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithFakeKPMPlugin.java
index 60e3176..f3892bf 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithFakeKPMPlugin.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithFakeKPMPlugin.java
@@ -312,12 +312,20 @@ public class TestWithFakeKPMPlugin extends TestIntegrationBase {
@BeforeClass(groups = "slow")
public void beforeClass() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
final Injector g = Guice.createInjector(Stage.PRODUCTION, Modules.override(new BeatrixIntegrationModule(configSource)).with(new OverrideModuleForOSGI()));
g.injectMembers(this);
}
@BeforeMethod(groups = "slow")
public void beforeMethod() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
log.debug("RESET TEST FRAMEWORK");
cleanupAllTables();
catalog/pom.xml 2(+1 -1)
diff --git a/catalog/pom.xml b/catalog/pom.xml
index abb51d5..2a45bf3 100644
--- a/catalog/pom.xml
+++ b/catalog/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.19.11-SNAPSHOT</version>
+ <version>0.19.13-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-catalog</artifactId>
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/api/user/DefaultCatalogUserApi.java b/catalog/src/main/java/org/killbill/billing/catalog/api/user/DefaultCatalogUserApi.java
index 0373e89..2fee429 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/api/user/DefaultCatalogUserApi.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/api/user/DefaultCatalogUserApi.java
@@ -33,7 +33,6 @@ import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.catalog.CatalogUpdater;
import org.killbill.billing.catalog.StandaloneCatalog;
import org.killbill.billing.catalog.VersionedCatalog;
-import org.killbill.billing.catalog.api.BillingMode;
import org.killbill.billing.catalog.api.Catalog;
import org.killbill.billing.catalog.api.CatalogApiException;
import org.killbill.billing.catalog.api.CatalogService;
@@ -45,7 +44,6 @@ import org.killbill.billing.catalog.caching.CatalogCache;
import org.killbill.billing.tenant.api.TenantApiException;
import org.killbill.billing.tenant.api.TenantKV.TenantKey;
import org.killbill.billing.tenant.api.TenantUserApi;
-import org.killbill.billing.util.cache.Cachable.CacheType;
import org.killbill.billing.util.callcontext.CallContext;
import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.callcontext.TenantContext;
@@ -175,7 +173,7 @@ public class DefaultCatalogUserApi implements CatalogUserApi {
final StandaloneCatalog currentCatalog = getCurrentStandaloneCatalogForTenant(internalTenantContext);
final CatalogUpdater catalogUpdater = (currentCatalog != null) ?
new CatalogUpdater(currentCatalog) :
- new CatalogUpdater(getSafeFirstCatalogEffectiveDate(effectiveDate), null);
+ new CatalogUpdater(getSafeFirstCatalogEffectiveDate(effectiveDate, callContext), null);
catalogCache.clearCatalog(internalTenantContext);
tenantApi.updateTenantKeyValue(TenantKey.CATALOG.toString(), catalogUpdater.getCatalogXML(), callContext);
@@ -192,7 +190,7 @@ public class DefaultCatalogUserApi implements CatalogUserApi {
final StandaloneCatalog currentCatalog = getCurrentStandaloneCatalogForTenant(internalTenantContext);
final CatalogUpdater catalogUpdater = (currentCatalog != null) ?
new CatalogUpdater(currentCatalog) :
- new CatalogUpdater(getSafeFirstCatalogEffectiveDate(effectiveDate), descriptor.getCurrency());
+ new CatalogUpdater(getSafeFirstCatalogEffectiveDate(effectiveDate, callContext), descriptor.getCurrency());
catalogUpdater.addSimplePlanDescriptor(descriptor);
catalogCache.clearCatalog(internalTenantContext);
@@ -209,15 +207,15 @@ public class DefaultCatalogUserApi implements CatalogUserApi {
try {
tenantApi.deleteTenantKey(TenantKey.CATALOG.toString(), callContext);
catalogCache.clearCatalog(internalTenantContext);
- createDefaultEmptyCatalog(clock.getUTCNow(), callContext);
+ createDefaultEmptyCatalog(callContext.getCreatedDate(), callContext);
} catch (final TenantApiException e) {
throw new CatalogApiException(e);
}
}
- private DateTime getSafeFirstCatalogEffectiveDate(@Nullable final DateTime input) {
+ private DateTime getSafeFirstCatalogEffectiveDate(@Nullable final DateTime input, final CallContext callContext) {
// The effectiveDate for the initial version does not matter too much
// Because of #760, we want to make that client passing a approximate date (e.g today.toDateTimeAtStartOfDay()) will find the version
- final DateTime catalogEffectiveDate = clock.getUTCNow().minusDays(1);
+ final DateTime catalogEffectiveDate = callContext.getCreatedDate().minusDays(1);
return (input == null || input.isAfter(catalogEffectiveDate)) ? catalogEffectiveDate : input;
}
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/CatalogTestSuiteNoDB.java b/catalog/src/test/java/org/killbill/billing/catalog/CatalogTestSuiteNoDB.java
index 3109de4..1301e8e 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/CatalogTestSuiteNoDB.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/CatalogTestSuiteNoDB.java
@@ -52,6 +52,10 @@ public abstract class CatalogTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
@BeforeClass(groups = "fast")
protected void beforeClass() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
final Injector injector = Guice.createInjector(new TestCatalogModuleNoDB(configSource));
injector.injectMembers(this);
}
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/CatalogTestSuiteWithEmbeddedDB.java b/catalog/src/test/java/org/killbill/billing/catalog/CatalogTestSuiteWithEmbeddedDB.java
index a556b15..2767ce8 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/CatalogTestSuiteWithEmbeddedDB.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/CatalogTestSuiteWithEmbeddedDB.java
@@ -47,6 +47,10 @@ public class CatalogTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuiteWithEm
@BeforeClass(groups = "slow")
protected void beforeClass() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
final Injector injector = Guice.createInjector(new TestCatalogModuleWithEmbeddedDB(configSource));
injector.injectMembers(this);
}
currency/pom.xml 2(+1 -1)
diff --git a/currency/pom.xml b/currency/pom.xml
index 62792ff..29f302d 100644
--- a/currency/pom.xml
+++ b/currency/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.19.11-SNAPSHOT</version>
+ <version>0.19.13-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-currency</artifactId>
entitlement/pom.xml 2(+1 -1)
diff --git a/entitlement/pom.xml b/entitlement/pom.xml
index ee7964e..82352f1 100644
--- a/entitlement/pom.xml
+++ b/entitlement/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.19.11-SNAPSHOT</version>
+ <version>0.19.13-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-entitlement</artifactId>
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlement.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlement.java
index 79f82d7..c8a4bbd 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlement.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlement.java
@@ -347,8 +347,7 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(getAccountId(), callContext);
- final DateTime now = clock.getUTCNow();
- final DateTime billingEffectiveCancelDate = dateHelper.fromLocalDateAndReferenceTimeWithMinimum(billingEffectiveDate, getEventsStream().getSubscriptionBase().getStartDate(), now, contextWithValidAccountRecordId);
+ final DateTime billingEffectiveCancelDate = dateHelper.fromLocalDateAndReferenceTimeWithMinimum(billingEffectiveDate, getEventsStream().getSubscriptionBase().getStartDate(), updatedPluginContext.getCreatedDate(), contextWithValidAccountRecordId);
try {
if (overrideBillingEffectiveDate) {
getSubscriptionBase().cancelWithDate(billingEffectiveCancelDate, callContext);
@@ -359,7 +358,7 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
throw new EntitlementApiException(e);
}
- final DateTime entitlementEffectiveCancelDate = dateHelper.fromLocalDateAndReferenceTimeWithMinimum(entitlementEffectiveDate, getEventsStream().getEntitlementEffectiveStartDateTime(), now, contextWithValidAccountRecordId);
+ final DateTime entitlementEffectiveCancelDate = dateHelper.fromLocalDateAndReferenceTimeWithMinimum(entitlementEffectiveDate, getEventsStream().getEntitlementEffectiveStartDateTime(), updatedPluginContext.getCreatedDate(), contextWithValidAccountRecordId);
final BlockingState newBlockingState = new DefaultBlockingState(getId(), BlockingStateType.SUBSCRIPTION, DefaultEntitlementApi.ENT_STATE_CANCELLED, EntitlementService.ENTITLEMENT_SERVICE_NAME, true, true, false, entitlementEffectiveCancelDate);
final Collection<NotificationEvent> notificationEvents = new ArrayList<NotificationEvent>();
final Collection<BlockingState> addOnsBlockingStates = computeAddOnBlockingStates(entitlementEffectiveCancelDate, notificationEvents, callContext, contextWithValidAccountRecordId);
@@ -503,8 +502,7 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
throw new EntitlementApiException(e);
}
- final DateTime now = clock.getUTCNow();
- final DateTime effectiveCancelDate = dateHelper.fromLocalDateAndReferenceTimeWithMinimum(entitlementEffectiveDate, getEventsStream().getEntitlementEffectiveStartDateTime(), now, contextWithValidAccountRecordId);
+ final DateTime effectiveCancelDate = dateHelper.fromLocalDateAndReferenceTimeWithMinimum(entitlementEffectiveDate, getEventsStream().getEntitlementEffectiveStartDateTime(), updatedPluginContext.getCreatedDate(), contextWithValidAccountRecordId);
final BlockingState newBlockingState = new DefaultBlockingState(getId(), BlockingStateType.SUBSCRIPTION, DefaultEntitlementApi.ENT_STATE_CANCELLED, EntitlementService.ENTITLEMENT_SERVICE_NAME, true, true, false, effectiveCancelDate);
final Collection<NotificationEvent> notificationEvents = new ArrayList<NotificationEvent>();
final Collection<BlockingState> addOnsBlockingStates = computeAddOnBlockingStates(effectiveCancelDate, notificationEvents, callContext, contextWithValidAccountRecordId);
@@ -521,19 +519,19 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
return pluginExecution.executeWithPlugin(cancelEntitlementWithPlugin, pluginContext);
}
- private LocalDate getLocalDateFromEntitlementPolicy(final EntitlementActionPolicy entitlementPolicy, final TenantContext tenantContext) {
- final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(getAccountId(), tenantContext);
+ private LocalDate getLocalDateFromEntitlementPolicy(final EntitlementActionPolicy entitlementPolicy, final CallContext callContext) {
+ final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(getAccountId(), callContext);
final LocalDate cancellationDate;
switch (entitlementPolicy) {
case IMMEDIATE:
- cancellationDate = internalTenantContext.toLocalDate(clock.getUTCNow());
+ cancellationDate = internalTenantContext.toLocalDate(callContext.getCreatedDate());
break;
case END_OF_TERM:
if (getSubscriptionBase().getChargedThroughDate() != null) {
cancellationDate = internalTenantContext.toLocalDate(getSubscriptionBase().getChargedThroughDate());
} else {
- cancellationDate = internalTenantContext.toLocalDate(clock.getUTCNow());
+ cancellationDate = internalTenantContext.toLocalDate(callContext.getCreatedDate());
}
break;
default:
@@ -696,9 +694,7 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
final InternalCallContext context = internalCallContextFactory.createInternalCallContext(getAccountId(), callContext);
-
- final DateTime now = clock.getUTCNow();
- final DateTime effectiveChangeDate = effectiveDate != null ? dateHelper.fromLocalDateAndReferenceTime(effectiveDate, now, context) : null;
+ final DateTime effectiveChangeDate = effectiveDate != null ? dateHelper.fromLocalDateAndReferenceTime(effectiveDate, context.getCreatedDate(), context) : null;
final DateTime resultingEffectiveDate;
try {
@@ -837,8 +833,7 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
// (e.g. base plan cancellation): future entitlement cancellations for add-ons on disk always reflect
// an explicit cancellation. This trick lets us determine what to do when un-cancelling.
// This mirror the behavior in subscription base (see DefaultSubscriptionBaseApiService).
- final DateTime now = clock.getUTCNow();
- if (effectiveDate.compareTo(now) > 0) {
+ if (effectiveDate.compareTo(internalCallContext.getCreatedDate()) > 0) {
// Note that usually we record the notification from the DAO. We cannot do it here because not all calls
// go through the DAO (e.g. change)
final boolean isBaseEntitlementCancelled = eventsStream.isEntitlementCancelled();
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementApi.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementApi.java
index a5e8793..595060f 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementApi.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementApi.java
@@ -38,6 +38,9 @@ import org.killbill.billing.account.api.AccountInternalApi;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.catalog.api.BillingActionPolicy;
+import org.killbill.billing.catalog.api.Catalog;
+import org.killbill.billing.catalog.api.CatalogApiException;
+import org.killbill.billing.catalog.api.CatalogInternalApi;
import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
import org.killbill.billing.catalog.api.ProductCategory;
@@ -72,12 +75,12 @@ import org.killbill.clock.Clock;
import org.killbill.notificationq.api.NotificationQueueService;
import com.google.common.base.Function;
+import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
-import static org.killbill.billing.entitlement.logging.EntitlementLoggingHelper.logCreateEntitlement;
import static org.killbill.billing.entitlement.logging.EntitlementLoggingHelper.logCreateEntitlementsWithAOs;
import static org.killbill.billing.entitlement.logging.EntitlementLoggingHelper.logPauseResumeEntitlement;
import static org.killbill.billing.entitlement.logging.EntitlementLoggingHelper.logTransferEntitlement;
@@ -102,6 +105,7 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements
private final NotificationQueueService notificationQueueService;
private final EntitlementPluginExecution pluginExecution;
private final SecurityApi securityApi;
+ private final CatalogInternalApi catalogInternalApi;
@Inject
public DefaultEntitlementApi(final PersistentBus eventBus, final InternalCallContextFactory internalCallContextFactory,
@@ -110,6 +114,7 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements
final BlockingChecker checker, final NotificationQueueService notificationQueueService,
final EventsStreamBuilder eventsStreamBuilder, final EntitlementUtils entitlementUtils,
final EntitlementPluginExecution pluginExecution,
+ final CatalogInternalApi catalogInternalApi,
final SecurityApi securityApi) {
super(eventBus, null, pluginExecution, internalCallContextFactory, subscriptionInternalApi, accountApi, blockingStateDao, clock, checker, notificationQueueService, eventsStreamBuilder, entitlementUtils, securityApi);
this.internalCallContextFactory = internalCallContextFactory;
@@ -123,6 +128,7 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements
this.entitlementUtils = entitlementUtils;
this.pluginExecution = pluginExecution;
this.securityApi = securityApi;
+ this.catalogInternalApi = catalogInternalApi;
this.dateHelper = new EntitlementDateHelper();
}
@@ -137,7 +143,8 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements
entitlementEffectiveDate,
billingEffectiveDate,
isMigrated);
- final List<UUID> createdEntitlements = createBaseEntitlementsWithAddOns(accountId,
+ final List<UUID> createdEntitlements = createBaseEntitlementsWithAddOns(OperationType.CREATE_SUBSCRIPTION,
+ accountId,
ImmutableList.<BaseEntitlementWithAddOnsSpecifier>of(baseEntitlementWithAddOnsSpecifier),
renameCancelledBundleIfExist,
properties,
@@ -145,188 +152,41 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements
return createdEntitlements.get(0);
}
- private BaseEntitlementWithAddOnsSpecifier getFirstBaseEntitlementWithAddOnsSpecifier(final Iterable<BaseEntitlementWithAddOnsSpecifier> baseEntitlementWithAddOnsSpecifiers) throws SubscriptionBaseApiException {
- if (baseEntitlementWithAddOnsSpecifiers == null) {
- throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_INVALID_ENTITLEMENT_SPECIFIER);
- }
-
- final Iterator<BaseEntitlementWithAddOnsSpecifier> iterator = baseEntitlementWithAddOnsSpecifiers.iterator();
- if (!iterator.hasNext()) {
- throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_INVALID_ENTITLEMENT_SPECIFIER);
- }
-
- return iterator.next();
- }
-
@Override
public List<UUID> createBaseEntitlementsWithAddOns(final UUID accountId, final Iterable<BaseEntitlementWithAddOnsSpecifier> originalBaseEntitlementWithAddOnsSpecifiers, final boolean renameCancelledBundleIfExist, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
- logCreateEntitlementsWithAOs(log, originalBaseEntitlementWithAddOnsSpecifiers);
-
- final EntitlementContext pluginContext = new DefaultEntitlementContext(OperationType.CREATE_SHOPPING_CART_SUBSCRIPTIONS,
- accountId,
- null,
- originalBaseEntitlementWithAddOnsSpecifiers,
- null,
- properties,
- callContext);
-
- final WithEntitlementPlugin<List<UUID>> createBaseEntitlementsWithAddOns = new WithEntitlementPlugin<List<UUID>>() {
- @Override
- public List<UUID> doCall(final EntitlementApi entitlementApi, final EntitlementContext updatedPluginContext) throws EntitlementApiException {
- final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(accountId, callContext);
- final DateTime now = clock.getUTCNow();
-
- final Iterable<BaseEntitlementWithAddOnsSpecifier> baseEntitlementWithAddOnsSpecifiersAfterPlugins = updatedPluginContext.getBaseEntitlementWithAddOnsSpecifiers();
- final Collection<SubscriptionBaseWithAddOnsSpecifier> subscriptionBaseWithAddOnsSpecifiers = new LinkedList<SubscriptionBaseWithAddOnsSpecifier>();
- DateTime upTo = null;
- for (final BaseEntitlementWithAddOnsSpecifier baseEntitlementWithAddOnsSpecifier : baseEntitlementWithAddOnsSpecifiersAfterPlugins) {
- // Entitlement
- final DateTime entitlementRequestedDate = dateHelper.fromLocalDateAndReferenceTime(baseEntitlementWithAddOnsSpecifier.getEntitlementEffectiveDate(),
- now,
- contextWithValidAccountRecordId);
- upTo = upTo == null || upTo.compareTo(entitlementRequestedDate) < 0 ? entitlementRequestedDate : upTo;
-
- final DateTime billingEffectiveDateTime = (baseEntitlementWithAddOnsSpecifier.getBillingEffectiveDate() != null) ?
- contextWithValidAccountRecordId.toUTCDateTime(baseEntitlementWithAddOnsSpecifier.getBillingEffectiveDate()) : null;
- final SubscriptionBaseWithAddOnsSpecifier subscriptionBaseWithAddOnsSpecifier = new SubscriptionBaseWithAddOnsSpecifier(baseEntitlementWithAddOnsSpecifier.getBundleId(),
- baseEntitlementWithAddOnsSpecifier.getExternalKey(),
- baseEntitlementWithAddOnsSpecifier.getEntitlementSpecifier(),
- billingEffectiveDateTime,
- baseEntitlementWithAddOnsSpecifier.isMigrated());
- subscriptionBaseWithAddOnsSpecifiers.add(subscriptionBaseWithAddOnsSpecifier);
- }
-
- try {
- // Verify if operation is allowed by looking for is_block_change on Account
- // Note that to fully check for block_change we should also look for BlockingState at the BUNDLE/SUBSCRIPTION level in case some of the input contain a BP that already exists.
- checkForAccountBlockingChange(accountId, upTo, contextWithValidAccountRecordId);
-
- final List<SubscriptionBaseWithAddOns> subscriptionsWithAddOns = subscriptionBaseInternalApi.createBaseSubscriptionsWithAddOns(subscriptionBaseWithAddOnsSpecifiers,
- renameCancelledBundleIfExist,
- contextWithValidAccountRecordId);
- final List<UUID> createdSubscriptionIds = new LinkedList<UUID>();
- final Map<BlockingState, UUID> blockingStateMap = new HashMap<BlockingState, UUID>();
- int i = 0;
- for (final Iterator<BaseEntitlementWithAddOnsSpecifier> it = baseEntitlementWithAddOnsSpecifiersAfterPlugins.iterator(); it.hasNext(); i++) {
- final BaseEntitlementWithAddOnsSpecifier baseEntitlementWithAddOnsSpecifier = it.next();
- for (final SubscriptionBase subscriptionBase : subscriptionsWithAddOns.get(i).getSubscriptionBaseList()) {
- final BlockingState blockingState = new DefaultBlockingState(subscriptionBase.getId(),
- BlockingStateType.SUBSCRIPTION,
- DefaultEntitlementApi.ENT_STATE_START,
- EntitlementService.ENTITLEMENT_SERVICE_NAME,
- false,
- false,
- false,
- dateHelper.fromLocalDateAndReferenceTime(baseEntitlementWithAddOnsSpecifier.getEntitlementEffectiveDate(), now, contextWithValidAccountRecordId));
- blockingStateMap.put(blockingState, subscriptionsWithAddOns.get(i).getBundle().getId());
-
- createdSubscriptionIds.add(subscriptionBase.getId());
- }
- }
- entitlementUtils.setBlockingStateAndPostBlockingTransitionEvent(blockingStateMap, contextWithValidAccountRecordId);
- return createdSubscriptionIds;
- } catch (final SubscriptionBaseApiException e) {
- throw new EntitlementApiException(e);
- }
- }
- };
- return pluginExecution.executeWithPlugin(createBaseEntitlementsWithAddOns, pluginContext);
+ return createBaseEntitlementsWithAddOns(OperationType.CREATE_SHOPPING_CART_SUBSCRIPTIONS,
+ accountId,
+ originalBaseEntitlementWithAddOnsSpecifiers,
+ renameCancelledBundleIfExist,
+ properties,
+ callContext);
}
@Override
public UUID addEntitlement(final UUID bundleId, final PlanPhaseSpecifier planPhaseSpecifier, final List<PlanPhasePriceOverride> overrides, @Nullable final LocalDate entitlementEffectiveDate, @Nullable final LocalDate billingEffectiveDate,
- final boolean isMigrated, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
- logCreateEntitlement(log, bundleId, planPhaseSpecifier, overrides, entitlementEffectiveDate, billingEffectiveDate);
-
+ final boolean isMigrated, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
final EntitlementSpecifier entitlementSpecifier = new DefaultEntitlementSpecifier(planPhaseSpecifier, overrides);
- final List<EntitlementSpecifier> entitlementSpecifierList = new ArrayList<EntitlementSpecifier>();
- entitlementSpecifierList.add(entitlementSpecifier);
- final BaseEntitlementWithAddOnsSpecifier baseEntitlementWithAddOnsSpecifier = new DefaultBaseEntitlementWithAddOnsSpecifier(
- bundleId,
- null,
- entitlementSpecifierList,
- entitlementEffectiveDate,
- billingEffectiveDate,
- isMigrated);
- final List<BaseEntitlementWithAddOnsSpecifier> baseEntitlementWithAddOnsSpecifierList = new ArrayList<BaseEntitlementWithAddOnsSpecifier>();
- baseEntitlementWithAddOnsSpecifierList.add(baseEntitlementWithAddOnsSpecifier);
-
- final EntitlementContext pluginContext = new DefaultEntitlementContext(OperationType.CREATE_SUBSCRIPTION,
- null,
- null,
- baseEntitlementWithAddOnsSpecifierList,
- null,
- properties,
- callContext);
-
- final WithEntitlementPlugin<UUID> addEntitlementWithPlugin = new WithEntitlementPlugin<UUID>() {
- @Override
- public UUID doCall(final EntitlementApi entitlementApi, final EntitlementContext updatedPluginContext) throws EntitlementApiException {
- final InternalCallContext context = internalCallContextFactory.createInternalCallContext(bundleId, ObjectType.BUNDLE, callContext);
- final DateTime now = clock.getUTCNow();
-
- final List<SubscriptionBase> subscriptionsByBundle;
- try {
- subscriptionsByBundle = subscriptionBaseInternalApi.getSubscriptionsForBundle(bundleId, null, context);
-
- if (subscriptionsByBundle == null || subscriptionsByBundle.isEmpty()) {
- throw new EntitlementApiException(ErrorCode.SUB_NO_ACTIVE_SUBSCRIPTIONS, bundleId);
- }
- } catch (final SubscriptionBaseApiException e) {
- throw new EntitlementApiException(e);
- }
- final boolean isStandalone = ProductCategory.STANDALONE.equals(subscriptionsByBundle.get(0).getCategory());
-
- final EventsStream eventsStreamForBaseSubscription = isStandalone ? null : eventsStreamBuilder.buildForBaseSubscription(bundleId, callContext);
-
- final DateTime entitlementRequestedDateRaw = dateHelper.fromLocalDateAndReferenceTime(baseEntitlementWithAddOnsSpecifier.getEntitlementEffectiveDate(), now, context);
- DateTime entitlementRequestedDate = entitlementRequestedDateRaw;
- if (!isStandalone) {
- final DateTime baseEntitlementStartDate = eventsStreamForBaseSubscription.getEntitlementEffectiveStartDateTime();
- entitlementRequestedDate = entitlementRequestedDateRaw.isBefore(baseEntitlementStartDate) ? baseEntitlementStartDate : entitlementRequestedDateRaw;
- preCheckAddEntitlement(bundleId, entitlementRequestedDate, baseEntitlementWithAddOnsSpecifier, eventsStreamForBaseSubscription, callContext);
- }
-
- try {
- final BaseEntitlementWithAddOnsSpecifier baseEntitlementWithAddOnsSpecifierAfterPlugins = getFirstBaseEntitlementWithAddOnsSpecifier(updatedPluginContext.getBaseEntitlementWithAddOnsSpecifiers());
- final DateTime billingEffectiveDateTime = (baseEntitlementWithAddOnsSpecifierAfterPlugins.getBillingEffectiveDate() != null) ?
- context.toUTCDateTime(baseEntitlementWithAddOnsSpecifierAfterPlugins.getBillingEffectiveDate()) : null;
- final SubscriptionBaseWithAddOnsSpecifier subscriptionBaseWithAddOnsSpecifier = new SubscriptionBaseWithAddOnsSpecifier(baseEntitlementWithAddOnsSpecifierAfterPlugins.getBundleId(),
- baseEntitlementWithAddOnsSpecifierAfterPlugins.getExternalKey(),
- baseEntitlementWithAddOnsSpecifierAfterPlugins.getEntitlementSpecifier(),
- billingEffectiveDateTime,
- baseEntitlementWithAddOnsSpecifierAfterPlugins.isMigrated());
-
- final List<SubscriptionBaseWithAddOns> subscriptionsWithAddOns = subscriptionBaseInternalApi.createBaseSubscriptionsWithAddOns(ImmutableList.<SubscriptionBaseWithAddOnsSpecifier>of(subscriptionBaseWithAddOnsSpecifier),
- false,
- context);
- final SubscriptionBaseWithAddOns subscriptionBaseWithAddOns = subscriptionsWithAddOns.get(0);
- final UUID subscriptionId = subscriptionBaseWithAddOns.getSubscriptionBaseList().get(0).getId();
-
- final BlockingState newBlockingState = new DefaultBlockingState(subscriptionId, BlockingStateType.SUBSCRIPTION, DefaultEntitlementApi.ENT_STATE_START, EntitlementService.ENTITLEMENT_SERVICE_NAME, false, false, false, entitlementRequestedDate);
- entitlementUtils.setBlockingStatesAndPostBlockingTransitionEvent(ImmutableList.<BlockingState>of(newBlockingState), subscriptionBaseWithAddOns.getBundle().getId(), context);
-
- return subscriptionId;
- } catch (final SubscriptionBaseApiException e) {
- throw new EntitlementApiException(e);
- }
- }
- };
- return pluginExecution.executeWithPlugin(addEntitlementWithPlugin, pluginContext);
- }
-
- private void preCheckAddEntitlement(final UUID bundleId, final DateTime entitlementRequestedDate, final BaseEntitlementWithAddOnsSpecifier baseEntitlementWithAddOnsSpecifier, final EventsStream eventsStreamForBaseSubscription, final CallContext callContext) throws EntitlementApiException {
- if (eventsStreamForBaseSubscription.isEntitlementCancelled() ||
- (eventsStreamForBaseSubscription.isEntitlementPending() &&
- (baseEntitlementWithAddOnsSpecifier.getEntitlementEffectiveDate() == null ||
- baseEntitlementWithAddOnsSpecifier.getEntitlementEffectiveDate().compareTo(eventsStreamForBaseSubscription.getEntitlementEffectiveStartDate()) < 0))) {
- throw new EntitlementApiException(ErrorCode.SUB_GET_NO_SUCH_BASE_SUBSCRIPTION, bundleId);
+ final BaseEntitlementWithAddOnsSpecifier baseEntitlementWithAddOnsSpecifier = new DefaultBaseEntitlementWithAddOnsSpecifier(bundleId,
+ null,
+ ImmutableList.<EntitlementSpecifier>of(entitlementSpecifier),
+ entitlementEffectiveDate,
+ billingEffectiveDate,
+ isMigrated);
+ final InternalCallContext context = internalCallContextFactory.createInternalCallContext(bundleId, ObjectType.BUNDLE, callContext);
+ final UUID accountId;
+ try {
+ accountId = subscriptionBaseInternalApi.getAccountIdFromBundleId(bundleId, context);
+ } catch (final SubscriptionBaseApiException e) {
+ throw new EntitlementApiException(e);
}
- // Check the base entitlement state is not blocked
- if (eventsStreamForBaseSubscription.isBlockChange(entitlementRequestedDate)) {
- throw new EntitlementApiException(new BlockingApiException(ErrorCode.BLOCK_BLOCKED_ACTION, BlockingChecker.ACTION_CHANGE, BlockingChecker.TYPE_SUBSCRIPTION, eventsStreamForBaseSubscription.getEntitlementId().toString()));
- }
+ final List<UUID> createdEntitlements = createBaseEntitlementsWithAddOns(OperationType.CREATE_SUBSCRIPTION,
+ accountId,
+ ImmutableList.<BaseEntitlementWithAddOnsSpecifier>of(baseEntitlementWithAddOnsSpecifier),
+ false,
+ properties,
+ callContext);
+ return createdEntitlements.get(0);
}
@Override
@@ -475,8 +335,7 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements
final BaseEntitlementWithAddOnsSpecifier baseEntitlementWithAddOnsSpecifier = getFirstBaseEntitlementWithAddOnsSpecifier(updatedPluginContext.getBaseEntitlementWithAddOnsSpecifiers());
- final DateTime now = clock.getUTCNow();
- final DateTime requestedDate = dateHelper.fromLocalDateAndReferenceTime(baseEntitlementWithAddOnsSpecifier.getBillingEffectiveDate(), now, contextWithSourceAccountRecordId);
+ final DateTime requestedDate = dateHelper.fromLocalDateAndReferenceTime(baseEntitlementWithAddOnsSpecifier.getBillingEffectiveDate(), updatedPluginContext.getCreatedDate(), contextWithSourceAccountRecordId);
final SubscriptionBaseBundle newBundle = subscriptionBaseTransferApi.transferBundle(sourceAccountId, destAccountId, externalKey, requestedDate, true, cancelImm, context);
@@ -494,7 +353,7 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements
final InternalCallContext contextWithDestAccountRecordId = internalCallContextFactory.createInternalCallContext(destAccountId, context);
blockingStates.clear();
- final DateTime entitlementRequestedDate = dateHelper.fromLocalDateAndReferenceTime(baseEntitlementWithAddOnsSpecifier.getEntitlementEffectiveDate(), now, contextWithDestAccountRecordId);
+ final DateTime entitlementRequestedDate = dateHelper.fromLocalDateAndReferenceTime(baseEntitlementWithAddOnsSpecifier.getEntitlementEffectiveDate(), updatedPluginContext.getCreatedDate(), contextWithDestAccountRecordId);
for (final SubscriptionBase subscriptionBase : subscriptionBaseInternalApi.getSubscriptionsForBundle(newBundle.getId(), null, contextWithDestAccountRecordId)) {
final BlockingState newBlockingState = new DefaultBlockingState(subscriptionBase.getId(), BlockingStateType.SUBSCRIPTION, DefaultEntitlementApi.ENT_STATE_START, EntitlementService.ENTITLEMENT_SERVICE_NAME, false, false, false, entitlementRequestedDate);
blockingStates.put(newBlockingState, subscriptionBase.getBundleId());
@@ -513,8 +372,185 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements
return pluginExecution.executeWithPlugin(transferWithPlugin, pluginContext);
}
- private void checkForAccountBlockingChange(final UUID accountId, @Nullable final DateTime upTo, final InternalCallContext context) throws EntitlementApiException {
+ private List<UUID> createBaseEntitlementsWithAddOns(final OperationType operationType,
+ final UUID accountId,
+ final Iterable<BaseEntitlementWithAddOnsSpecifier> originalBaseEntitlementWithAddOnsSpecifiers,
+ final boolean renameCancelledBundleIfExist,
+ final Iterable<PluginProperty> properties,
+ final CallContext callContext) throws EntitlementApiException {
+ logCreateEntitlementsWithAOs(log, originalBaseEntitlementWithAddOnsSpecifiers);
+
+ final EntitlementContext pluginContext = new DefaultEntitlementContext(operationType,
+ accountId,
+ null,
+ originalBaseEntitlementWithAddOnsSpecifiers,
+ null,
+ properties,
+ callContext);
+
+ final WithEntitlementPlugin<List<UUID>> createBaseEntitlementsWithAddOns = new WithEntitlementPlugin<List<UUID>>() {
+ @Override
+ public List<UUID> doCall(final EntitlementApi entitlementApi, final EntitlementContext updatedPluginContext) throws EntitlementApiException {
+ final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(accountId, callContext);
+
+ final Catalog catalog;
+ try {
+ catalog = catalogInternalApi.getFullCatalog(true, true, contextWithValidAccountRecordId);
+ } catch (final CatalogApiException e) {
+ throw new EntitlementApiException(e);
+ }
+
+ final Map<UUID, Optional<EventsStream>> eventsStreamForBaseSubscriptionPerBundle = new HashMap<UUID, Optional<EventsStream>>();
+ final Map<String, Optional<UUID>> bundleKeyToIdMapping = new HashMap<String, Optional<UUID>>();
+ final Iterable<BaseEntitlementWithAddOnsSpecifier> baseEntitlementWithAddOnsSpecifiersAfterPlugins = updatedPluginContext.getBaseEntitlementWithAddOnsSpecifiers();
+ final Collection<SubscriptionBaseWithAddOnsSpecifier> subscriptionBaseWithAddOnsSpecifiers = new LinkedList<SubscriptionBaseWithAddOnsSpecifier>();
+ DateTime upTo = null;
+ for (final BaseEntitlementWithAddOnsSpecifier baseEntitlementWithAddOnsSpecifier : baseEntitlementWithAddOnsSpecifiersAfterPlugins) {
+ // Entitlement
+ final DateTime entitlementRequestedDate = dateHelper.fromLocalDateAndReferenceTime(baseEntitlementWithAddOnsSpecifier.getEntitlementEffectiveDate(),
+ updatedPluginContext.getCreatedDate(),
+ contextWithValidAccountRecordId);
+ upTo = upTo == null || upTo.compareTo(entitlementRequestedDate) < 0 ? entitlementRequestedDate : upTo;
+
+ final UUID bundleId = populateCaches(baseEntitlementWithAddOnsSpecifier,
+ eventsStreamForBaseSubscriptionPerBundle,
+ bundleKeyToIdMapping,
+ catalog,
+ callContext,
+ contextWithValidAccountRecordId);
+ if (bundleId != null) {
+ final Optional<EventsStream> eventsStreamForBaseSubscription = eventsStreamForBaseSubscriptionPerBundle.get(bundleId);
+ if (eventsStreamForBaseSubscription.isPresent()) {
+ // Verify if the operation is valid for that bundle
+ preCheckAddEntitlement(bundleId, entitlementRequestedDate, baseEntitlementWithAddOnsSpecifier, eventsStreamForBaseSubscription.get());
+ }
+ }
+
+ final SubscriptionBaseWithAddOnsSpecifier subscriptionBaseWithAddOnsSpecifier = new SubscriptionBaseWithAddOnsSpecifier(baseEntitlementWithAddOnsSpecifier.getBundleId(),
+ baseEntitlementWithAddOnsSpecifier.getExternalKey(),
+ baseEntitlementWithAddOnsSpecifier.getEntitlementSpecifier(),
+ baseEntitlementWithAddOnsSpecifier.getBillingEffectiveDate(),
+ baseEntitlementWithAddOnsSpecifier.isMigrated());
+ subscriptionBaseWithAddOnsSpecifiers.add(subscriptionBaseWithAddOnsSpecifier);
+ }
+
+ // Verify if operation is allowed by looking for is_block_change on Account
+ // Note that to fully check for block_change we should also look for BlockingState at the BUNDLE/SUBSCRIPTION level in case some of the input contain a BP that already exists.
+ checkForAccountBlockingChange(accountId, upTo, contextWithValidAccountRecordId);
+
+ final List<SubscriptionBaseWithAddOns> subscriptionsWithAddOns;
+ try {
+ subscriptionsWithAddOns = subscriptionBaseInternalApi.createBaseSubscriptionsWithAddOns(subscriptionBaseWithAddOnsSpecifiers,
+ renameCancelledBundleIfExist,
+ contextWithValidAccountRecordId);
+ } catch (final SubscriptionBaseApiException e) {
+ throw new EntitlementApiException(e);
+ }
+
+ return createEntitlementEvents(baseEntitlementWithAddOnsSpecifiersAfterPlugins, subscriptionsWithAddOns, updatedPluginContext, contextWithValidAccountRecordId);
+ }
+ };
+ return pluginExecution.executeWithPlugin(createBaseEntitlementsWithAddOns, pluginContext);
+ }
+ private BaseEntitlementWithAddOnsSpecifier getFirstBaseEntitlementWithAddOnsSpecifier(final Iterable<BaseEntitlementWithAddOnsSpecifier> baseEntitlementWithAddOnsSpecifiers) throws SubscriptionBaseApiException {
+ if (baseEntitlementWithAddOnsSpecifiers == null) {
+ throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_INVALID_ENTITLEMENT_SPECIFIER);
+ }
+
+ final Iterator<BaseEntitlementWithAddOnsSpecifier> iterator = baseEntitlementWithAddOnsSpecifiers.iterator();
+ if (!iterator.hasNext()) {
+ throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_INVALID_ENTITLEMENT_SPECIFIER);
+ }
+
+ return iterator.next();
+ }
+
+ private UUID populateCaches(final BaseEntitlementWithAddOnsSpecifier baseEntitlementWithAddOnsSpecifier,
+ final Map<UUID, Optional<EventsStream>> eventsStreamForBaseSubscriptionPerBundle,
+ final Map<String, Optional<UUID>> bundleKeyToIdMapping,
+ final Catalog catalog,
+ final TenantContext callContext,
+ final InternalCallContext contextWithValidAccountRecordId) throws EntitlementApiException {
+ // In the addEntitlement codepath, bundleId is always set. But, technically, an existing bundle could be specified by externalKey in
+ // the createBaseEntitlementsWithAddOns codepath. In that case, we should also check if that bundle is blocked.
+ UUID bundleId = baseEntitlementWithAddOnsSpecifier.getBundleId();
+ if (bundleId == null && baseEntitlementWithAddOnsSpecifier.getExternalKey() != null) {
+ populateBundleKeyToIdMappingCache(baseEntitlementWithAddOnsSpecifier, bundleKeyToIdMapping, catalog, contextWithValidAccountRecordId);
+
+ final Optional<UUID> bundleIdForKey = bundleKeyToIdMapping.get(baseEntitlementWithAddOnsSpecifier.getExternalKey());
+ if (bundleIdForKey.isPresent()) {
+ bundleId = bundleIdForKey.get();
+ }
+ }
+
+ if (bundleId == null) {
+ return null;
+ }
+
+ populateEventsStreamForBaseSubscriptionPerBundleCache(bundleId, eventsStreamForBaseSubscriptionPerBundle, callContext, contextWithValidAccountRecordId);
+
+ return bundleId;
+ }
+
+ private void populateBundleKeyToIdMappingCache(final BaseEntitlementWithAddOnsSpecifier baseEntitlementWithAddOnsSpecifier, final Map<String, Optional<UUID>> bundleKeyToIdMapping, final Catalog catalog, final InternalCallContext contextWithValidAccountRecordId) throws EntitlementApiException {
+ if (bundleKeyToIdMapping.get(baseEntitlementWithAddOnsSpecifier.getExternalKey()) == null) {
+ final SubscriptionBaseBundle bundle = subscriptionBaseInternalApi.getActiveBundleForKey(baseEntitlementWithAddOnsSpecifier.getExternalKey(), catalog, contextWithValidAccountRecordId);
+ if (bundle != null) {
+ bundleKeyToIdMapping.put(baseEntitlementWithAddOnsSpecifier.getExternalKey(), Optional.<UUID>of(bundle.getId()));
+ } else {
+ bundleKeyToIdMapping.put(baseEntitlementWithAddOnsSpecifier.getExternalKey(), Optional.<UUID>absent());
+ }
+ }
+ }
+
+ private void populateEventsStreamForBaseSubscriptionPerBundleCache(final UUID bundleId, final Map<UUID, Optional<EventsStream>> eventsStreamForBaseSubscriptionPerBundle, final TenantContext callContext, final InternalCallContext contextWithValidAccountRecordId) throws EntitlementApiException {
+ if (eventsStreamForBaseSubscriptionPerBundle.get(bundleId) == null) {
+ final List<SubscriptionBase> subscriptionsByBundle;
+ try {
+ subscriptionsByBundle = subscriptionBaseInternalApi.getSubscriptionsForBundle(bundleId, null, contextWithValidAccountRecordId);
+
+ if (subscriptionsByBundle == null || subscriptionsByBundle.isEmpty()) {
+ throw new EntitlementApiException(ErrorCode.SUB_NO_ACTIVE_SUBSCRIPTIONS, bundleId);
+ }
+ } catch (final SubscriptionBaseApiException e) {
+ throw new EntitlementApiException(e);
+ }
+
+ final boolean isStandalone = Iterables.any(subscriptionsByBundle,
+ new Predicate<SubscriptionBase>() {
+ @Override
+ public boolean apply(final SubscriptionBase input) {
+ return ProductCategory.STANDALONE.equals(input.getCategory());
+ }
+ });
+
+ if (!isStandalone) {
+ final EventsStream eventsStreamForBaseSubscription = eventsStreamBuilder.buildForBaseSubscription(bundleId, callContext);
+ eventsStreamForBaseSubscriptionPerBundle.put(bundleId, Optional.<EventsStream>of(eventsStreamForBaseSubscription));
+ } else {
+ eventsStreamForBaseSubscriptionPerBundle.put(bundleId, Optional.<EventsStream>absent());
+ }
+ }
+ }
+
+ private void preCheckAddEntitlement(final UUID bundleId, final DateTime entitlementRequestedDate, final BaseEntitlementWithAddOnsSpecifier baseEntitlementWithAddOnsSpecifier, final EventsStream eventsStreamForBaseSubscription) throws EntitlementApiException {
+ if (eventsStreamForBaseSubscription.isEntitlementCancelled() ||
+ (eventsStreamForBaseSubscription.isEntitlementPending() &&
+ (baseEntitlementWithAddOnsSpecifier.getEntitlementEffectiveDate() == null ||
+ baseEntitlementWithAddOnsSpecifier.getEntitlementEffectiveDate().compareTo(eventsStreamForBaseSubscription.getEntitlementEffectiveStartDate()) < 0))) {
+ throw new EntitlementApiException(ErrorCode.SUB_GET_NO_SUCH_BASE_SUBSCRIPTION, bundleId);
+ }
+
+ // Check the base entitlement state is not blocked
+ if (eventsStreamForBaseSubscription.isBlockChange(entitlementRequestedDate)) {
+ throw new EntitlementApiException(new BlockingApiException(ErrorCode.BLOCK_BLOCKED_ACTION, BlockingChecker.ACTION_CHANGE, BlockingChecker.TYPE_SUBSCRIPTION, eventsStreamForBaseSubscription.getEntitlementId().toString()));
+ } else if (eventsStreamForBaseSubscription.isBlockEntitlement(entitlementRequestedDate)) {
+ throw new EntitlementApiException(new BlockingApiException(ErrorCode.BLOCK_BLOCKED_ACTION, BlockingChecker.ACTION_ENTITLEMENT, BlockingChecker.TYPE_SUBSCRIPTION, eventsStreamForBaseSubscription.getEntitlementId().toString()));
+ }
+ }
+
+ private void checkForAccountBlockingChange(final UUID accountId, @Nullable final DateTime upTo, final InternalCallContext context) throws EntitlementApiException {
try {
final BlockingAggregator blockingAggregator = checker.getBlockedStatus(accountId, BlockingStateType.ACCOUNT, upTo, context);
if (blockingAggregator.isBlockChange()) {
@@ -524,4 +560,31 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements
throw new EntitlementApiException(e);
}
}
+
+ private List<UUID> createEntitlementEvents(final Iterable<BaseEntitlementWithAddOnsSpecifier> baseEntitlementWithAddOnsSpecifiersAfterPlugins,
+ final List<SubscriptionBaseWithAddOns> subscriptionsWithAddOns,
+ final CallContext updatedPluginContext,
+ final InternalCallContext contextWithValidAccountRecordId) throws EntitlementApiException {
+ final List<UUID> createdSubscriptionIds = new LinkedList<UUID>();
+ final Map<BlockingState, UUID> blockingStateMap = new HashMap<BlockingState, UUID>();
+ int i = 0;
+ for (final Iterator<BaseEntitlementWithAddOnsSpecifier> it = baseEntitlementWithAddOnsSpecifiersAfterPlugins.iterator(); it.hasNext(); i++) {
+ final BaseEntitlementWithAddOnsSpecifier baseEntitlementWithAddOnsSpecifier = it.next();
+ for (final SubscriptionBase subscriptionBase : subscriptionsWithAddOns.get(i).getSubscriptionBaseList()) {
+ final BlockingState blockingState = new DefaultBlockingState(subscriptionBase.getId(),
+ BlockingStateType.SUBSCRIPTION,
+ DefaultEntitlementApi.ENT_STATE_START,
+ EntitlementService.ENTITLEMENT_SERVICE_NAME,
+ false,
+ false,
+ false,
+ dateHelper.fromLocalDateAndReferenceTime(baseEntitlementWithAddOnsSpecifier.getEntitlementEffectiveDate(), updatedPluginContext.getCreatedDate(), contextWithValidAccountRecordId));
+ blockingStateMap.put(blockingState, subscriptionsWithAddOns.get(i).getBundle().getId());
+
+ createdSubscriptionIds.add(subscriptionBase.getId());
+ }
+ }
+ entitlementUtils.setBlockingStateAndPostBlockingTransitionEvent(blockingStateMap, contextWithValidAccountRecordId);
+ return createdSubscriptionIds;
+ }
}
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementSpecifier.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementSpecifier.java
index dbf7876..0fc92b0 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementSpecifier.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementSpecifier.java
@@ -42,4 +42,16 @@ public class DefaultEntitlementSpecifier implements EntitlementSpecifier {
return overrides;
}
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("DefaultEntitlementSpecifier{");
+ sb.append("planName=").append(planPhaseSpecifier.getPlanName());
+ sb.append(", productName=").append(planPhaseSpecifier.getProductName());
+ sb.append(", billingPeriod=").append(planPhaseSpecifier.getBillingPeriod());
+ sb.append(", phaseType=").append(planPhaseSpecifier.getPhaseType());
+ sb.append(", priceListName=").append(planPhaseSpecifier.getPriceListName());
+ sb.append(", overrides=").append(overrides);
+ sb.append('}');
+ return sb.toString();
+ }
}
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionApi.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionApi.java
index a9691ee..2178510 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionApi.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionApi.java
@@ -287,7 +287,7 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
}
- final LocalDate effectiveDate = internalCallContext.toLocalDate(clock.getUTCNow());
+ final LocalDate effectiveDate = internalCallContext.toLocalDate(callContext.getCreatedDate());
final BaseEntitlementWithAddOnsSpecifier baseEntitlementWithAddOnsSpecifier = new DefaultBaseEntitlementWithAddOnsSpecifier(
bundleId,
newExternalKey,
@@ -375,7 +375,7 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
throw new EntitlementApiException(e);
}
- final DateTime effectiveDate = inputEffectiveDate == null ? clock.getUTCNow() : internalCallContextWithValidAccountId.toUTCDateTime(inputEffectiveDate);
+ final DateTime effectiveDate = inputEffectiveDate == null ? callContext.getCreatedDate() : internalCallContextWithValidAccountId.toUTCDateTime(inputEffectiveDate);
final DefaultBlockingState blockingState = new DefaultBlockingState(inputBlockingState, effectiveDate);
final BaseEntitlementWithAddOnsSpecifier baseEntitlementWithAddOnsSpecifier = new DefaultBaseEntitlementWithAddOnsSpecifier(
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementApiBase.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementApiBase.java
index 169c38a..e73bdad 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementApiBase.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementApiBase.java
@@ -209,8 +209,7 @@ public class DefaultEntitlementApiBase {
private UUID blockUnblockBundle(final UUID bundleId, final String stateName, final String serviceName, @Nullable final LocalDate localEffectiveDate, boolean blockBilling, boolean blockEntitlement, boolean blockChange, @Nullable final SubscriptionBase inputBaseSubscription, final InternalCallContext internalCallContext)
throws EntitlementApiException {
- final DateTime now = clock.getUTCNow();
- final DateTime effectiveDate = dateHelper.fromLocalDateAndReferenceTime(localEffectiveDate, now, internalCallContext);
+ final DateTime effectiveDate = dateHelper.fromLocalDateAndReferenceTime(localEffectiveDate, internalCallContext.getCreatedDate(), internalCallContext);
final BlockingState state = new DefaultBlockingState(bundleId, BlockingStateType.SUBSCRIPTION_BUNDLE, stateName, serviceName, blockChange, blockEntitlement, blockBilling, effectiveDate);
entitlementUtils.setBlockingStatesAndPostBlockingTransitionEvent(ImmutableList.<BlockingState>of(state), bundleId, internalCallContext);
return state.getId();
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementInternalApi.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementInternalApi.java
index 7e91ad9..0b69332 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementInternalApi.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementInternalApi.java
@@ -234,8 +234,7 @@ public class DefaultEntitlementInternalApi extends DefaultEntitlementApiBase imp
@Override
public Entitlement doCall(final EntitlementApi entitlementApi, final EntitlementContext updatedPluginContext) throws EntitlementApiException {
- final DateTime now = clock.getUTCNow();
- DateTime effectiveDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getBaseEntitlementWithAddOnsSpecifiers().iterator().next().getEntitlementEffectiveDate(), now, internalCallContext);
+ DateTime effectiveDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getBaseEntitlementWithAddOnsSpecifiers().iterator().next().getEntitlementEffectiveDate(), updatedPluginContext.getCreatedDate(), internalCallContext);
//
// If the entitlementDate provided is ahead we default to the effective subscriptionBase cancellationDate to avoid weird timing issues.
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/dao/BlockingStateSqlDao.java b/entitlement/src/main/java/org/killbill/billing/entitlement/dao/BlockingStateSqlDao.java
index 344a645..abc01c9 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/dao/BlockingStateSqlDao.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/dao/BlockingStateSqlDao.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
*
* The Billing Project 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
@@ -25,12 +25,13 @@ import java.util.UUID;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.entitlement.api.BlockingState;
+import org.killbill.billing.entitlement.api.BlockingStateType;
import org.killbill.billing.util.audit.ChangeType;
import org.killbill.billing.util.entity.dao.Audited;
import org.killbill.billing.util.entity.dao.EntitySqlDao;
+import org.killbill.commons.jdbi.binder.SmartBindBean;
import org.killbill.commons.jdbi.template.KillBillSqlDaoStringTemplate;
import org.skife.jdbi.v2.sqlobject.Bind;
-import org.killbill.commons.jdbi.binder.SmartBindBean;
import org.skife.jdbi.v2.sqlobject.SqlQuery;
import org.skife.jdbi.v2.sqlobject.SqlUpdate;
@@ -45,10 +46,15 @@ public interface BlockingStateSqlDao extends EntitySqlDao<BlockingStateModelDao,
@SqlQuery
public abstract List<BlockingStateModelDao> getBlockingState(@Bind("blockableId") UUID blockableId,
+ @Bind("type") BlockingStateType blockingStateType,
@Bind("effectiveDate") Date effectiveDate,
@SmartBindBean final InternalTenantContext context);
@SqlQuery
+ public abstract List<BlockingStateModelDao> getBlockingAllUpToForAccount(@Bind("effectiveDate") Date effectiveDate,
+ @SmartBindBean final InternalTenantContext context);
+
+ @SqlQuery
public abstract List<BlockingStateModelDao> getBlockingHistoryForService(@Bind("blockableId") UUID blockableId,
@Bind("service") String serviceName,
@SmartBindBean final InternalTenantContext context);
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/dao/DefaultBlockingStateDao.java b/entitlement/src/main/java/org/killbill/billing/entitlement/dao/DefaultBlockingStateDao.java
index 7bda51f..0e7aada 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/dao/DefaultBlockingStateDao.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/dao/DefaultBlockingStateDao.java
@@ -157,9 +157,20 @@ public class DefaultBlockingStateDao extends EntityDaoBase<BlockingStateModelDao
private List<BlockingState> getBlockingState(final BlockingStateSqlDao sqlDao, final UUID blockableId, final BlockingStateType blockingStateType, final DateTime upToDate, final InternalTenantContext context) {
final Date upTo = upToDate.toDate();
- final List<BlockingStateModelDao> models = sqlDao.getBlockingState(blockableId, upTo, context);
- final Collection<BlockingStateModelDao> modelsFiltered = filterBlockingStates(models, blockingStateType);
- return new ArrayList<BlockingState>(Collections2.transform(modelsFiltered,
+ final List<BlockingStateModelDao> models = sqlDao.getBlockingState(blockableId, blockingStateType, upTo, context);
+ return new ArrayList<BlockingState>(Collections2.transform(models,
+ new Function<BlockingStateModelDao, BlockingState>() {
+ @Override
+ public BlockingState apply(@Nullable final BlockingStateModelDao src) {
+ return BlockingStateModelDao.toBlockingState(src);
+ }
+ }));
+ }
+
+ private List<BlockingState> getBlockingAllUpToForAccountRecordId(final BlockingStateSqlDao sqlDao, final DateTime upToDate, final InternalTenantContext context) {
+ final Date upTo = upToDate.toDate();
+ final List<BlockingStateModelDao> models = sqlDao.getBlockingAllUpToForAccount(upTo, context);
+ return new ArrayList<BlockingState>(Collections2.transform(models,
new Function<BlockingStateModelDao, BlockingState>() {
@Override
public BlockingState apply(@Nullable final BlockingStateModelDao src) {
@@ -265,13 +276,15 @@ public class DefaultBlockingStateDao extends EntityDaoBase<BlockingStateModelDao
final List<BlockingState> subscriptionBlockingStates;
if (type == BlockingStateType.SUBSCRIPTION) {
final UUID accountId = nonEntityDao.retrieveIdFromObjectInTransaction(context.getAccountRecordId(), ObjectType.ACCOUNT, objectIdCacheController, handle);
- accountBlockingStates = getBlockingState(sqlDao, accountId, BlockingStateType.ACCOUNT, upToDate, context);
- bundleBlockingStates = getBlockingState(sqlDao, bundleId, BlockingStateType.SUBSCRIPTION_BUNDLE, upToDate, context);
- subscriptionBlockingStates = getBlockingState(sqlDao, blockableId, BlockingStateType.SUBSCRIPTION, upToDate, context);
+ final List<BlockingState> allBlockingStatesForAccount = getBlockingAllUpToForAccountRecordId(sqlDao, upToDate, context);
+ accountBlockingStates = filterBlockingStates(allBlockingStatesForAccount, accountId, BlockingStateType.ACCOUNT);
+ bundleBlockingStates = filterBlockingStates(allBlockingStatesForAccount, bundleId, BlockingStateType.SUBSCRIPTION_BUNDLE);
+ subscriptionBlockingStates = filterBlockingStates(allBlockingStatesForAccount, blockableId, BlockingStateType.SUBSCRIPTION);
} else if (type == BlockingStateType.SUBSCRIPTION_BUNDLE) {
final UUID accountId = nonEntityDao.retrieveIdFromObjectInTransaction(context.getAccountRecordId(), ObjectType.ACCOUNT, objectIdCacheController, handle);
- accountBlockingStates = getBlockingState(sqlDao, accountId, BlockingStateType.ACCOUNT, upToDate, context);
- bundleBlockingStates = getBlockingState(sqlDao, blockableId, BlockingStateType.SUBSCRIPTION_BUNDLE, upToDate, context);
+ final List<BlockingState> allBlockingStatesForAccount = getBlockingAllUpToForAccountRecordId(sqlDao, upToDate, context);
+ accountBlockingStates = filterBlockingStates(allBlockingStatesForAccount, accountId, BlockingStateType.ACCOUNT);
+ bundleBlockingStates = filterBlockingStates(allBlockingStatesForAccount, blockableId, BlockingStateType.SUBSCRIPTION_BUNDLE);
subscriptionBlockingStates = ImmutableList.<BlockingState>of();
} else { // BlockingStateType.ACCOUNT {
accountBlockingStates = getBlockingState(sqlDao, blockableId, BlockingStateType.ACCOUNT, upToDate, context);
@@ -298,7 +311,7 @@ public class DefaultBlockingStateDao extends EntityDaoBase<BlockingStateModelDao
final boolean isTransitionToBlockedEntitlement = !previousState.isBlockEntitlement() && currentState.isBlockEntitlement();
final boolean isTransitionToUnblockedEntitlement = previousState.isBlockEntitlement() && !currentState.isBlockEntitlement();
- if (effectiveDate.compareTo(clock.getUTCNow()) > 0) {
+ if (effectiveDate.compareTo(context.getCreatedDate()) > 0) {
// Add notification entry to send the bus event at the effective date
final NotificationEvent notificationEvent = new BlockingTransitionNotificationKey(blockingStateId,
blockableId,
@@ -373,13 +386,13 @@ public class DefaultBlockingStateDao extends EntityDaoBase<BlockingStateModelDao
});
}
- private Collection<BlockingStateModelDao> filterBlockingStates(final Collection<BlockingStateModelDao> models, final BlockingStateType blockingStateType) {
- return Collections2.<BlockingStateModelDao>filter(models,
- new Predicate<BlockingStateModelDao>() {
- @Override
- public boolean apply(final BlockingStateModelDao input) {
- return input.getType().equals(blockingStateType);
- }
- });
+ private List<BlockingState> filterBlockingStates(final Collection<BlockingState> models, final UUID objectId, final BlockingStateType blockingStateType) {
+ return ImmutableList.<BlockingState>copyOf(Collections2.<BlockingState>filter(models,
+ new Predicate<BlockingState>() {
+ @Override
+ public boolean apply(final BlockingState input) {
+ return input.getBlockedId().equals(objectId) && input.getType().equals(blockingStateType);
+ }
+ }));
}
}
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/DefaultEventsStream.java b/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/DefaultEventsStream.java
index 5ff2339..9c5a89b 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/DefaultEventsStream.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/DefaultEventsStream.java
@@ -226,6 +226,13 @@ public class DefaultEventsStream implements EventsStream {
}
@Override
+ public boolean isBlockEntitlement(final DateTime effectiveDate) {
+ Preconditions.checkState(effectiveDate != null);
+ final BlockingAggregator aggregator = getBlockingAggregator(effectiveDate);
+ return aggregator.isBlockEntitlement();
+ }
+
+ @Override
public int getDefaultBillCycleDayLocal() {
return defaultBillCycleDayLocal;
}
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/logging/EntitlementLoggingHelper.java b/entitlement/src/main/java/org/killbill/billing/entitlement/logging/EntitlementLoggingHelper.java
index a0b158c..3039f4c 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/logging/EntitlementLoggingHelper.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/logging/EntitlementLoggingHelper.java
@@ -74,8 +74,9 @@ public abstract class EntitlementLoggingHelper {
final StringBuilder logLine = new StringBuilder("Create Entitlements with AddOns: ");
if (baseEntitlementSpecifiersWithAddOns != null && baseEntitlementSpecifiersWithAddOns.iterator().hasNext()) {
- for (BaseEntitlementWithAddOnsSpecifier cur : baseEntitlementSpecifiersWithAddOns) {
- logCreateEntitlementWithAOs(log, cur.getExternalKey(),
+ for (final BaseEntitlementWithAddOnsSpecifier cur : baseEntitlementSpecifiersWithAddOns) {
+ logCreateEntitlementWithAOs(logLine,
+ cur.getExternalKey(),
cur.getEntitlementSpecifier(),
cur.getEntitlementEffectiveDate(),
cur.getBillingEffectiveDate());
@@ -85,33 +86,27 @@ public abstract class EntitlementLoggingHelper {
}
}
- public static void logCreateEntitlementWithAOs(final Logger log,
- final String externalKey,
- final Iterable<EntitlementSpecifier> entitlementSpecifiers,
- final LocalDate entitlementDate,
- final LocalDate billingDate) {
- if (log.isInfoEnabled()) {
- final StringBuilder logLine = new StringBuilder("Create Entitlements: ");
-
- if (externalKey != null) {
- logLine.append("key='")
- .append(externalKey)
- .append("'");
- }
- if (entitlementDate != null) {
- logLine.append(", entDate='")
- .append(entitlementDate)
- .append("'");
- }
- if (billingDate != null) {
- logLine.append(", billDate='")
- .append(billingDate)
- .append("'");
- }
- logEntitlementSpecifier(logLine, entitlementSpecifiers);
- log.info(logLine.toString());
+ private static void logCreateEntitlementWithAOs(final StringBuilder logLine,
+ final String externalKey,
+ final Iterable<EntitlementSpecifier> entitlementSpecifiers,
+ final LocalDate entitlementDate,
+ final LocalDate billingDate) {
+ if (externalKey != null) {
+ logLine.append("key='")
+ .append(externalKey)
+ .append("'");
}
-
+ if (entitlementDate != null) {
+ logLine.append(", entDate='")
+ .append(entitlementDate)
+ .append("'");
+ }
+ if (billingDate != null) {
+ logLine.append(", billDate='")
+ .append(billingDate)
+ .append("'");
+ }
+ logEntitlementSpecifier(logLine, entitlementSpecifiers);
}
public static void logPauseResumeEntitlement(final Logger log,
diff --git a/entitlement/src/main/resources/org/killbill/billing/entitlement/dao/BlockingStateSqlDao.sql.stg b/entitlement/src/main/resources/org/killbill/billing/entitlement/dao/BlockingStateSqlDao.sql.stg
index 9d78819..1e6d9dd 100644
--- a/entitlement/src/main/resources/org/killbill/billing/entitlement/dao/BlockingStateSqlDao.sql.stg
+++ b/entitlement/src/main/resources/org/killbill/billing/entitlement/dao/BlockingStateSqlDao.sql.stg
@@ -59,23 +59,48 @@ limit 1
>>
getBlockingState() ::= <<
- select
- <allTableFields("t.")>
- from
- <tableName()> t
- join (
- select max(record_id) record_id
- , service
- from blocking_states
- where blockable_id = :blockableId
- and effective_date \<= :effectiveDate
- and is_active
- <AND_CHECK_TENANT("")>
- group by service
- ) tmp
- on t.record_id = tmp.record_id
- <defaultOrderBy("t.")>
- ;
+select
+<allTableFields("t.")>
+from
+<tableName()> t
+join (
+ select max(record_id) record_id
+ , service
+ from blocking_states
+ where blockable_id = :blockableId
+ and type = :type
+ and effective_date \<= :effectiveDate
+ <andCheckSoftDeletionWithComma("")>
+ and <accountRecordIdField("")> = :accountRecordId
+ <AND_CHECK_TENANT("")>
+ group by service
+) tmp
+on t.record_id = tmp.record_id
+where t.type = :type
+and <accountRecordIdField("t.")> = :accountRecordId
+<defaultOrderBy("t.")>
+;
+ >>
+
+getBlockingAllUpToForAccount() ::= <<
+select
+<allTableFields("t.")>
+from
+<tableName()> t
+join (
+ select max(record_id) record_id
+ , service
+ from blocking_states
+ where effective_date \<= :effectiveDate
+ <andCheckSoftDeletionWithComma("")>
+ and <accountRecordIdField("")> = :accountRecordId
+ <AND_CHECK_TENANT("")>
+ group by service
+) tmp
+on t.record_id = tmp.record_id
+where <accountRecordIdField("t.")> = :accountRecordId
+<defaultOrderBy("t.")>
+;
>>
getBlockingHistoryForService() ::= <<
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/EntitlementTestSuiteNoDB.java b/entitlement/src/test/java/org/killbill/billing/entitlement/EntitlementTestSuiteNoDB.java
index df95234..cd116fc 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/EntitlementTestSuiteNoDB.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/EntitlementTestSuiteNoDB.java
@@ -69,17 +69,29 @@ public abstract class EntitlementTestSuiteNoDB extends GuicyKillbillTestSuiteNoD
@BeforeClass(groups = "fast")
protected void beforeClass() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
final Injector injector = Guice.createInjector(new TestEntitlementModuleNoDB(configSource));
injector.injectMembers(this);
}
@BeforeMethod(groups = "fast")
public void beforeMethod() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
bus.start();
}
@AfterMethod(groups = "fast")
public void afterMethod() {
+ if (hasFailed()) {
+ return;
+ }
+
bus.stop();
}
}
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java b/entitlement/src/test/java/org/killbill/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java
index 27c7b67..b8f8c94 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java
@@ -130,12 +130,20 @@ public class EntitlementTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuiteWi
@BeforeClass(groups = "slow")
protected void beforeClass() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
final Injector injector = Guice.createInjector(Stage.PRODUCTION, new TestEntitlementModuleWithEmbeddedDB(configSource));
injector.injectMembers(this);
}
@BeforeMethod(groups = "slow")
public void beforeMethod() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
super.beforeMethod();
startTestFamework(testListener, clock, busService, subscriptionBaseService, entitlementService);
this.catalog = initCatalog(catalogService);
@@ -171,6 +179,10 @@ public class EntitlementTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuiteWi
@AfterMethod(groups = "slow")
public void afterMethod() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
securityApi.logout();
stopTestFramework(testListener, busService, subscriptionBaseService, entitlementService);
invoice/pom.xml 2(+1 -1)
diff --git a/invoice/pom.xml b/invoice/pom.xml
index d86d45c..111e16c 100644
--- a/invoice/pom.xml
+++ b/invoice/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.19.11-SNAPSHOT</version>
+ <version>0.19.13-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-invoice</artifactId>
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
index 1283805..e8acdde 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
@@ -1142,11 +1142,10 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
private void notifyOfParentInvoiceCreation(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory,
final InvoiceModelDao parentInvoice,
final InternalCallContext context) {
- final DateTime now = clock.getUTCNow();
final LocalTime localTime = LocalTime.parse(invoiceConfig.getParentAutoCommitUtcTime(context));
- DateTime targetFutureNotificationDate = now.withTime(localTime);
- while (targetFutureNotificationDate.compareTo(now) < 0) {
+ DateTime targetFutureNotificationDate = context.getCreatedDate().withTime(localTime);
+ while (targetFutureNotificationDate.compareTo(context.getCreatedDate()) < 0) {
targetFutureNotificationDate = targetFutureNotificationDate.plusDays(1);
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/generator/FixedAndRecurringInvoiceItemGenerator.java b/invoice/src/main/java/org/killbill/billing/invoice/generator/FixedAndRecurringInvoiceItemGenerator.java
index c73adba..0fd86a0 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/generator/FixedAndRecurringInvoiceItemGenerator.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/generator/FixedAndRecurringInvoiceItemGenerator.java
@@ -412,7 +412,7 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator
}
@VisibleForTesting
- void safetyBounds(final Iterable<InvoiceItem> resultingItems, final Multimap<UUID, LocalDate> createdItemsPerDayPerSubscription, final InternalTenantContext internalCallContext) throws InvoiceApiException {
+ void safetyBounds(final Iterable<InvoiceItem> resultingItems, final Multimap<UUID, LocalDate> createdItemsPerDayPerSubscription, final InternalCallContext internalCallContext) throws InvoiceApiException {
// Trigger an exception if we detect the creation of similar items for a given subscription
// See https://github.com/killbill/killbill/issues/664
if (config.isSanitySafetyBoundEnabled(internalCallContext)) {
@@ -472,13 +472,13 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator
}
}
- private LocalDate trackInvoiceItemCreatedDay(final InvoiceItem invoiceItem, final Multimap<UUID, LocalDate> createdItemsPerDayPerSubscription, final InternalTenantContext internalCallContext) {
+ private LocalDate trackInvoiceItemCreatedDay(final InvoiceItem invoiceItem, final Multimap<UUID, LocalDate> createdItemsPerDayPerSubscription, final InternalCallContext internalCallContext) {
final UUID subscriptionId = invoiceItem.getSubscriptionId();
if (subscriptionId == null) {
return null;
}
- final LocalDate createdDay = internalCallContext.toLocalDate(MoreObjects.firstNonNull(invoiceItem.getCreatedDate(), clock.getUTCNow()));
+ final LocalDate createdDay = internalCallContext.toLocalDate(MoreObjects.firstNonNull(invoiceItem.getCreatedDate(), internalCallContext.getCreatedDate()));
createdItemsPerDayPerSubscription.put(subscriptionId, createdDay);
return createdDay;
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/usage/details/UsageConsumableInArrearTierUnitAggregate.java b/invoice/src/main/java/org/killbill/billing/invoice/usage/details/UsageConsumableInArrearTierUnitAggregate.java
index 0a423d8..57ab28e 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/usage/details/UsageConsumableInArrearTierUnitAggregate.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/usage/details/UsageConsumableInArrearTierUnitAggregate.java
@@ -19,11 +19,12 @@ package org.killbill.billing.invoice.usage.details;
import java.math.BigDecimal;
-import org.killbill.billing.invoice.usage.details.UsageInArrearTierUnitDetail;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
+@JsonIgnoreProperties(ignoreUnknown = true)
public class UsageConsumableInArrearTierUnitAggregate extends UsageInArrearTierUnitDetail {
private final int tierBlockSize;
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/usage/details/UsageInArrearTierUnitDetail.java b/invoice/src/main/java/org/killbill/billing/invoice/usage/details/UsageInArrearTierUnitDetail.java
index 59aebc7..53d1918 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/usage/details/UsageInArrearTierUnitDetail.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/usage/details/UsageInArrearTierUnitDetail.java
@@ -20,8 +20,10 @@ package org.killbill.billing.invoice.usage.details;
import java.math.BigDecimal;
import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
+@JsonIgnoreProperties(ignoreUnknown = true)
public class UsageInArrearTierUnitDetail {
protected final int tier;
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/glue/TestInvoiceModuleWithEmbeddedDb.java b/invoice/src/test/java/org/killbill/billing/invoice/glue/TestInvoiceModuleWithEmbeddedDb.java
index 626a257..c64ea1f 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/glue/TestInvoiceModuleWithEmbeddedDb.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/glue/TestInvoiceModuleWithEmbeddedDb.java
@@ -24,6 +24,7 @@ import org.killbill.billing.currency.api.CurrencyConversionApi;
import org.killbill.billing.invoice.InvoiceListener;
import org.killbill.billing.invoice.TestInvoiceNotificationQListener;
import org.killbill.billing.platform.api.KillbillConfigSource;
+import org.killbill.billing.util.glue.AuditModule;
import org.killbill.billing.util.glue.NonEntityDaoModule;
import org.killbill.billing.util.glue.TagStoreModule;
import org.mockito.Mockito;
@@ -47,6 +48,7 @@ public class TestInvoiceModuleWithEmbeddedDb extends TestInvoiceModule {
install(new GuicyKillbillTestWithEmbeddedDBModule(configSource));
install(new NonEntityDaoModule(configSource));
install(new TagStoreModule(configSource));
+ install(new AuditModule(configSource));
bind(CurrencyConversionApi.class).toInstance(Mockito.mock(CurrencyConversionApi.class));
}
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/InvoiceTestSuiteNoDB.java b/invoice/src/test/java/org/killbill/billing/invoice/InvoiceTestSuiteNoDB.java
index dff38c1..e097fac 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/InvoiceTestSuiteNoDB.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/InvoiceTestSuiteNoDB.java
@@ -112,17 +112,29 @@ public abstract class InvoiceTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
@BeforeClass(groups = "fast")
protected void beforeClass() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
final Injector injector = Guice.createInjector(new TestInvoiceModuleNoDB(configSource));
injector.injectMembers(this);
}
@BeforeMethod(groups = "fast")
public void beforeMethod() {
+ if (hasFailed()) {
+ return;
+ }
+
bus.start();
}
@AfterMethod(groups = "fast")
public void afterMethod() {
+ if (hasFailed()) {
+ return;
+ }
+
bus.stop();
}
}
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/InvoiceTestSuiteWithEmbeddedDB.java b/invoice/src/test/java/org/killbill/billing/invoice/InvoiceTestSuiteWithEmbeddedDB.java
index f5eceaa..523e480 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/InvoiceTestSuiteWithEmbeddedDB.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/InvoiceTestSuiteWithEmbeddedDB.java
@@ -92,8 +92,6 @@ public abstract class InvoiceTestSuiteWithEmbeddedDB extends GuicyKillbillTestSu
@Inject
protected GlobalLocker locker;
@Inject
- protected ClockMock clock;
- @Inject
protected InternalCallContextFactory internalCallContextFactory;
@Inject
protected InvoiceInternalApi invoiceInternalApi;
@@ -119,6 +117,10 @@ public abstract class InvoiceTestSuiteWithEmbeddedDB extends GuicyKillbillTestSu
@BeforeClass(groups = "slow")
protected void beforeClass() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
final Injector injector = Guice.createInjector(new TestInvoiceModuleWithEmbeddedDb(configSource));
injector.injectMembers(this);
}
@@ -126,6 +128,10 @@ public abstract class InvoiceTestSuiteWithEmbeddedDB extends GuicyKillbillTestSu
@Override
@BeforeMethod(groups = "slow")
public void beforeMethod() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
super.beforeMethod();
controllerDispatcher.clearAll();
bus.start();
@@ -144,6 +150,10 @@ public abstract class InvoiceTestSuiteWithEmbeddedDB extends GuicyKillbillTestSu
@AfterMethod(groups = "slow")
public void afterMethod() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
bus.stop();
stopInvoiceService(invoiceService);
}
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/proRations/InvoiceTestUtils.java b/invoice/src/test/java/org/killbill/billing/invoice/proRations/InvoiceTestUtils.java
index 42fccdb..92bce8a 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/proRations/InvoiceTestUtils.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/proRations/InvoiceTestUtils.java
@@ -23,6 +23,8 @@ import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
import org.killbill.billing.account.api.Account;
import org.killbill.billing.account.api.AccountApiException;
import org.killbill.billing.callcontext.InternalCallContext;
@@ -88,8 +90,9 @@ public class InvoiceTestUtils {
Mockito.when(invoice.getId()).thenReturn(invoiceId);
Mockito.when(invoice.getAccountId()).thenReturn(accountId);
- Mockito.when(invoice.getInvoiceDate()).thenReturn(clock.getUTCToday());
- Mockito.when(invoice.getTargetDate()).thenReturn(clock.getUTCToday());
+ final LocalDate today = clock.getUTCToday();
+ Mockito.when(invoice.getInvoiceDate()).thenReturn(today);
+ Mockito.when(invoice.getTargetDate()).thenReturn(today);
Mockito.when(invoice.getCurrency()).thenReturn(currency);
Mockito.when(invoice.isMigrationInvoice()).thenReturn(false);
Mockito.when(invoice.getStatus()).thenReturn(InvoiceStatus.COMMITTED);
@@ -127,7 +130,8 @@ public class InvoiceTestUtils {
Mockito.when(payment.getInvoiceId()).thenReturn(invoiceId);
Mockito.when(payment.getPaymentId()).thenReturn(UUID.randomUUID());
Mockito.when(payment.getPaymentCookieId()).thenReturn(UUID.randomUUID().toString());
- Mockito.when(payment.getPaymentDate()).thenReturn(clock.getUTCNow());
+ final DateTime utcNow = clock.getUTCNow();
+ Mockito.when(payment.getPaymentDate()).thenReturn(utcNow);
Mockito.when(payment.getAmount()).thenReturn(amount);
Mockito.when(payment.getCurrency()).thenReturn(currency);
Mockito.when(payment.getProcessedCurrency()).thenReturn(currency);
jaxrs/pom.xml 2(+1 -1)
diff --git a/jaxrs/pom.xml b/jaxrs/pom.xml
index 335c64a..cbde768 100644
--- a/jaxrs/pom.xml
+++ b/jaxrs/pom.xml
@@ -21,7 +21,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.19.11-SNAPSHOT</version>
+ <version>0.19.13-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-jaxrs</artifactId>
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/AuditLogJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/AuditLogJson.java
index b79e883..3dde19b 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/AuditLogJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/AuditLogJson.java
@@ -22,6 +22,8 @@ import org.joda.time.DateTime;
import org.killbill.billing.ObjectType;
import org.killbill.billing.util.audit.AuditLog;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
+import org.killbill.billing.util.entity.Entity;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
@@ -42,6 +44,8 @@ public class AuditLogJson {
private final ObjectType objectType;
private final UUID objectId;
+ private final Entity history;
+
@JsonCreator
public AuditLogJson(@JsonProperty("changeType") final String changeType,
@JsonProperty("changeDate") final DateTime changeDate,
@@ -50,7 +54,8 @@ public class AuditLogJson {
@JsonProperty("changedBy") final String changedBy,
@JsonProperty("reasonCode") final String reasonCode,
@JsonProperty("comments") final String comments,
- @JsonProperty("userToken") final String userToken) {
+ @JsonProperty("userToken") final String userToken,
+ @JsonProperty("history") final Entity history) {
this.changeType = changeType;
this.changeDate = changeDate;
this.changedBy = changedBy;
@@ -59,11 +64,17 @@ public class AuditLogJson {
this.userToken = userToken;
this.objectType = objectType;
this.objectId = objectId;
+ this.history = history;
+ }
+
+ public AuditLogJson(final AuditLogWithHistory auditLogWithHistory) {
+ this(auditLogWithHistory.getChangeType().toString(), auditLogWithHistory.getCreatedDate(), auditLogWithHistory.getAuditedObjectType(), auditLogWithHistory.getAuditedEntityId(), auditLogWithHistory.getUserName(), auditLogWithHistory.getReasonCode(),
+ auditLogWithHistory.getComment(), auditLogWithHistory.getUserToken(), auditLogWithHistory.getEntity());
}
public AuditLogJson(final AuditLog auditLog) {
this(auditLog.getChangeType().toString(), auditLog.getCreatedDate(), auditLog.getAuditedObjectType(), auditLog.getAuditedEntityId(), auditLog.getUserName(), auditLog.getReasonCode(),
- auditLog.getComment(), auditLog.getUserToken());
+ auditLog.getComment(), auditLog.getUserToken(), null);
}
public String getChangeType() {
@@ -98,6 +109,10 @@ public class AuditLogJson {
return objectId;
}
+ public Entity getHistory() {
+ return history;
+ }
+
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/BulkSubscriptionsBundleJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/BulkSubscriptionsBundleJson.java
index 4066056..faaf0fb 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/BulkSubscriptionsBundleJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/BulkSubscriptionsBundleJson.java
@@ -32,8 +32,7 @@ public class BulkSubscriptionsBundleJson {
private final List<SubscriptionJson> baseEntitlementAndAddOns;
@JsonCreator
- public BulkSubscriptionsBundleJson(
- @JsonProperty("baseEntitlementAndAddOns") @Nullable final List<SubscriptionJson> baseEntitlementAndAddOns) {
+ public BulkSubscriptionsBundleJson(@JsonProperty("baseEntitlementAndAddOns") @Nullable final List<SubscriptionJson> baseEntitlementAndAddOns) {
this.baseEntitlementAndAddOns = baseEntitlementAndAddOns;
}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
index 7e877c5..edac6ce 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
@@ -121,6 +121,7 @@ import org.killbill.billing.util.api.TagDefinitionApiException;
import org.killbill.billing.util.api.TagUserApi;
import org.killbill.billing.util.audit.AccountAuditLogs;
import org.killbill.billing.util.audit.AuditLog;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
import org.killbill.billing.util.callcontext.CallContext;
import org.killbill.billing.util.callcontext.TenantContext;
import org.killbill.billing.util.config.definition.JaxrsConfig;
@@ -1511,10 +1512,22 @@ public class AccountResource extends JaxRsResourceBase {
@javax.ws.rs.core.Context final HttpServletRequest request) throws AccountApiException {
final TenantContext tenantContext = context.createTenantContextWithAccountId(accountId, request);
final AccountAuditLogs accountAuditLogs = auditUserApi.getAccountAuditLogs(accountId, AuditLevel.FULL, tenantContext);
-
return Response.status(Status.OK).entity(getAuditLogs(accountAuditLogs)).build();
}
+ @TimedResource
+ @GET
+ @Path("/{accountId:" + UUID_PATTERN + "}/" + AUDIT_LOG_WITH_HISTORY)
+ @Produces(APPLICATION_JSON)
+ @ApiOperation(value = "Retrieve account audit logs with history by account id", response = AuditLogJson.class, responseContainer = "List")
+ @ApiResponses(value = {@ApiResponse(code = 404, message = "Account not found")})
+ public Response getAccountAuditLogsWithHistory(@PathParam("accountId") final UUID accountId,
+ @javax.ws.rs.core.Context final HttpServletRequest request) throws AccountApiException {
+ final TenantContext tenantContext = context.createTenantContextWithAccountId(accountId, request);
+ final List<AuditLogWithHistory> auditLogWithHistory = accountUserApi.getAuditLogsWithHistoryForId(accountId, AuditLevel.FULL, tenantContext);
+ return Response.status(Status.OK).entity(getAuditLogsWithHistory(auditLogWithHistory)).build();
+ }
+
private List<AuditLogJson> getAuditLogs(AccountAuditLogs accountAuditLogs) {
if (accountAuditLogs.getAuditLogs() == null) {
return null;
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CreditResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CreditResource.java
index 0d4b453..c4f3a9c 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CreditResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CreditResource.java
@@ -118,10 +118,10 @@ public class CreditResource extends JaxRsResourceBase {
verifyNonNullOrEmpty(json.getAccountId(), "CreditJson accountId needs to be set",
json.getCreditAmount(), "CreditJson creditAmount needs to be set");
- final CallContext callContext = context.createCallContextNoAccountId(createdBy, reason, comment, request);
+ final CallContext callContext = context.createCallContextWithAccountId(json.getAccountId(), createdBy, reason, comment, request);
final Account account = accountUserApi.getAccountById(json.getAccountId(), callContext);
- final LocalDate effectiveDate = new LocalDate(clock.getUTCNow(), account.getTimeZone());
+ final LocalDate effectiveDate = new LocalDate(callContext.getCreatedDate(), account.getTimeZone());
final InvoiceItem credit;
if (json.getInvoiceId() != null) {
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
index 44766ad..344a119 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
@@ -293,5 +293,5 @@ public interface JaxrsResource {
String QUERY_INCLUDED_DELETED = "includedDeleted";
String AUDIT_LOG = "auditLogs";
-
+ String AUDIT_LOG_WITH_HISTORY = "auditLogsWithHistory";
}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxRsResourceBase.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxRsResourceBase.java
index e966a1a..87b2c37 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxRsResourceBase.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxRsResourceBase.java
@@ -60,6 +60,7 @@ import org.killbill.billing.entitlement.api.SubscriptionApi;
import org.killbill.billing.entitlement.api.SubscriptionApiException;
import org.killbill.billing.invoice.api.InvoicePayment;
import org.killbill.billing.invoice.api.InvoicePaymentType;
+import org.killbill.billing.jaxrs.json.AuditLogJson;
import org.killbill.billing.jaxrs.json.BillingExceptionJson;
import org.killbill.billing.jaxrs.json.BillingExceptionJson.StackTraceElementJson;
import org.killbill.billing.jaxrs.json.BlockingStateJson;
@@ -88,6 +89,7 @@ import org.killbill.billing.util.api.TagDefinitionApiException;
import org.killbill.billing.util.api.TagUserApi;
import org.killbill.billing.util.audit.AccountAuditLogsForObjectType;
import org.killbill.billing.util.audit.AuditLog;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
import org.killbill.billing.util.callcontext.CallContext;
import org.killbill.billing.util.callcontext.TenantContext;
import org.killbill.billing.util.customfield.CustomField;
@@ -713,4 +715,13 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
// If nothing is found, return the latest transaction of given type
return Iterables.getFirst(matchingTransactions, null);
}
+
+ protected List<AuditLogJson> getAuditLogsWithHistory(List<AuditLogWithHistory> auditLogWithHistory) {
+ return ImmutableList.<AuditLogJson>copyOf(Collections2.transform(auditLogWithHistory, new Function<AuditLogWithHistory, AuditLogJson>() {
+ @Override
+ public AuditLogJson apply(@Nullable final AuditLogWithHistory input) {
+ return new AuditLogJson(input);
+ }
+ }));
+ }
}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java
index ad5d2db..005d33d 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java
@@ -56,7 +56,6 @@ import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.catalog.api.PhaseType;
import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
-import org.killbill.billing.catalog.api.ProductCategory;
import org.killbill.billing.entitlement.api.BaseEntitlementWithAddOnsSpecifier;
import org.killbill.billing.entitlement.api.BlockingStateType;
import org.killbill.billing.entitlement.api.Entitlement;
@@ -105,8 +104,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Preconditions;
-import com.google.common.base.Predicate;
-import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.inject.Inject;
@@ -169,10 +166,11 @@ public class SubscriptionResource extends JaxRsResourceBase {
@POST
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
+
@ApiOperation(value = "Create an subscription", response = SubscriptionJson.class)
@ApiResponses(value = {@ApiResponse(code = 201, message = "Subscription created successfully")})
public Response createSubscription(final SubscriptionJson subscription,
- @ApiParam(hidden = true) @QueryParam(QUERY_REQUESTED_DT) final String requestedDate, /* This is deprecated, only used for backward compatibility */
+ @QueryParam(QUERY_REQUESTED_DT) final String requestedDate, /* This is deprecated, only used for backward compatibility */
@QueryParam(QUERY_ENTITLEMENT_REQUESTED_DT) final String entitlementDate,
@QueryParam(QUERY_BILLING_REQUESTED_DT) final String billingDate,
@QueryParam(QUERY_BUNDLES_RENAME_KEY_IF_EXIST_UNUSED) @DefaultValue("true") final Boolean renameKeyIfExistsAndUnused,
@@ -186,74 +184,8 @@ public class SubscriptionResource extends JaxRsResourceBase {
@HeaderParam(HDR_COMMENT) final String comment,
@javax.ws.rs.core.Context final HttpServletRequest request,
@javax.ws.rs.core.Context final UriInfo uriInfo) throws EntitlementApiException, AccountApiException, SubscriptionApiException {
- verifyNonNullOrEmpty(subscription, "SubscriptionJson body should be specified");
- if (subscription.getPlanName() == null) {
- verifyNonNullOrEmpty(subscription.getProductName(), "SubscriptionJson productName needs to be set",
- subscription.getProductCategory(), "SubscriptionJson productCategory needs to be set",
- subscription.getBillingPeriod(), "SubscriptionJson billingPeriod needs to be set",
- subscription.getPriceList(), "SubscriptionJson priceList needs to be set");
- }
-
- logDeprecationParameterWarningIfNeeded(QUERY_REQUESTED_DT, QUERY_ENTITLEMENT_REQUESTED_DT, QUERY_BILLING_REQUESTED_DT);
-
- // For ADD_ON we need to provide externalKey or the bundleId
- if (ProductCategory.ADD_ON.equals(subscription.getProductCategory())) {
- Preconditions.checkArgument(subscription.getExternalKey() != null || subscription.getBundleId() != null, "SubscriptionJson bundleId or externalKey should be specified for ADD_ON");
- }
-
- final boolean isReusingExistingBundle = ProductCategory.ADD_ON == subscription.getProductCategory() ||
- (ProductCategory.STANDALONE == subscription.getProductCategory() && subscription.getBundleId() != null);
-
- final Iterable<PluginProperty> pluginProperties = extractPluginProperties(pluginPropertiesString);
- final CallContext callContext = context.createCallContextNoAccountId(createdBy, reason, comment, request);
-
- final EntitlementCallCompletionCallback<UUID> callback = new EntitlementCallCompletionCallback<UUID>() {
- @Override
- public UUID doOperation(final CallContext ctx) throws InterruptedException, TimeoutException, EntitlementApiException, SubscriptionApiException, AccountApiException {
-
- final Account account = getAccountFromSubscriptionJson(subscription, callContext);
- final PhaseType phaseType = subscription.getPhaseType();
- final PlanPhaseSpecifier spec = subscription.getPlanName() != null ?
- new PlanPhaseSpecifier(subscription.getPlanName(), phaseType) :
- new PlanPhaseSpecifier(subscription.getProductName(),
- subscription.getBillingPeriod(), subscription.getPriceList(), phaseType);
-
- final LocalDate resolvedEntitlementDate = requestedDate != null ? toLocalDate(requestedDate) : toLocalDate(entitlementDate);
- final LocalDate resolvedBillingDate = requestedDate != null ? toLocalDate(requestedDate) : toLocalDate(billingDate);
- final List<PlanPhasePriceOverride> overrides = PhasePriceOverrideJson.toPlanPhasePriceOverrides(subscription.getPriceOverrides(), spec, account.getCurrency());
- final UUID result = isReusingExistingBundle
- ? entitlementApi.addEntitlement(getBundleIdForAddOnCreation(subscription), spec, overrides, resolvedEntitlementDate, resolvedBillingDate, isMigrated, pluginProperties, callContext)
- : entitlementApi.createBaseEntitlement(account.getId(), spec, subscription.getExternalKey(), overrides, resolvedEntitlementDate, resolvedBillingDate, isMigrated, renameKeyIfExistsAndUnused, pluginProperties, callContext);
- if (newBCD != null) {
- final Entitlement createdEntitlement = entitlementApi.getEntitlementForId(result, callContext);
- createdEntitlement.updateBCD(newBCD, null, callContext);
- }
- return result;
- }
-
- private UUID getBundleIdForAddOnCreation(final SubscriptionJson entitlement) throws SubscriptionApiException {
-
- if (entitlement.getBundleId() != null) {
- return entitlement.getBundleId();
- }
- // If user only specified the externalKey we need to fech the bundle (expensive operation) to extract the bundleId
- final SubscriptionBundle bundle = subscriptionApi.getActiveSubscriptionBundleForExternalKey(entitlement.getExternalKey(), callContext);
- return bundle.getId();
- }
-
- @Override
- public boolean isImmOperation() {
- return true;
- }
-
- @Override
- public Response doResponseOk(final UUID createdEntitlementId) {
- return uriBuilder.buildResponse(uriInfo, SubscriptionResource.class, "getSubscription", createdEntitlementId, request);
- }
- };
-
- final EntitlementCallCompletion<UUID> callCompletionCreation = new EntitlementCallCompletion<UUID>();
- return callCompletionCreation.withSynchronization(callback, timeoutSec, callCompletion, callContext);
+ final List<BulkSubscriptionsBundleJson> entitlementsWithAddOns = ImmutableList.of(new BulkSubscriptionsBundleJson(ImmutableList.<SubscriptionJson>of(subscription)));
+ return createSubscriptionsWithAddOnsInternal(entitlementsWithAddOns, requestedDate, entitlementDate, billingDate, isMigrated, renameKeyIfExistsAndUnused, callCompletion, timeoutSec, pluginPropertiesString, createdBy, reason, comment, request, uriInfo, ObjectType.SUBSCRIPTION);
}
@TimedResource
@@ -320,61 +252,58 @@ public class SubscriptionResource extends JaxRsResourceBase {
final HttpServletRequest request,
final UriInfo uriInfo,
final ObjectType responseObject) throws EntitlementApiException, AccountApiException, SubscriptionApiException {
-
- Preconditions.checkArgument(Iterables.size(entitlementsWithAddOns) > 0, "Subscription bulk list mustn't be null or empty.");
+ Preconditions.checkArgument(Iterables.size(entitlementsWithAddOns) > 0, "No subscription specified to create");
logDeprecationParameterWarningIfNeeded(QUERY_REQUESTED_DT, QUERY_ENTITLEMENT_REQUESTED_DT, QUERY_BILLING_REQUESTED_DT);
final Iterable<PluginProperty> pluginProperties = extractPluginProperties(pluginPropertiesString);
final CallContext callContext = context.createCallContextNoAccountId(createdBy, reason, comment, request);
+ Preconditions.checkArgument(Iterables.size(entitlementsWithAddOns.get(0).getBaseEntitlementAndAddOns()) > 0, "SubscriptionJson body should be specified");
final Account account = accountUserApi.getAccountById(entitlementsWithAddOns.get(0).getBaseEntitlementAndAddOns().get(0).getAccountId(), callContext);
final Collection<BaseEntitlementWithAddOnsSpecifier> baseEntitlementWithAddOnsSpecifierList = new ArrayList<BaseEntitlementWithAddOnsSpecifier>();
- for (final BulkSubscriptionsBundleJson bulkBaseEntitlementWithAddOns : entitlementsWithAddOns) {
- final Iterable<SubscriptionJson> baseEntitlements = Iterables.filter(
- bulkBaseEntitlementWithAddOns.getBaseEntitlementAndAddOns(), new Predicate<SubscriptionJson>() {
- @Override
- public boolean apply(final SubscriptionJson subscription) {
- return ProductCategory.BASE.equals(subscription.getProductCategory());
- }
- });
-
- final List<EntitlementSpecifier> entitlementSpecifierList;
- final String bundleExternalKey;
- if (baseEntitlements.iterator().hasNext()) {
- Preconditions.checkArgument(Iterables.size(baseEntitlements) == 1, "Only one BASE product is allowed per bundle.");
-
- final SubscriptionJson baseEntitlement = baseEntitlements.iterator().next();
- final Iterable<SubscriptionJson> addonEntitlements = Iterables.filter(bulkBaseEntitlementWithAddOns.getBaseEntitlementAndAddOns(),
- new Predicate<SubscriptionJson>() {
- @Override
- public boolean apply(final SubscriptionJson subscription) {
- return ProductCategory.ADD_ON.equals(subscription.getProductCategory());
- }
- });
-
- entitlementSpecifierList = buildEntitlementSpecifierList(baseEntitlement, addonEntitlements, account.getCurrency());
- bundleExternalKey = baseEntitlement.getExternalKey();
- } else {
- final Collection<SubscriptionJson> standaloneEntitlements = Collections2.filter(bulkBaseEntitlementWithAddOns.getBaseEntitlementAndAddOns(),
- new Predicate<SubscriptionJson>() {
- @Override
- public boolean apply(final SubscriptionJson subscription) {
- return ProductCategory.STANDALONE.equals(subscription.getProductCategory());
- }
- });
- entitlementSpecifierList = buildEntitlementSpecifierList(standaloneEntitlements, account.getCurrency());
- bundleExternalKey = standaloneEntitlements.isEmpty() ? null : standaloneEntitlements.iterator().next().getExternalKey();
+
+ for (final BulkSubscriptionsBundleJson subscriptionsBundleJson : entitlementsWithAddOns) {
+ UUID bundleId = null;
+ String bundleExternalKey = null;
+ final Collection<EntitlementSpecifier> entitlementSpecifierList = new ArrayList<EntitlementSpecifier>();
+ for (final SubscriptionJson entitlement : subscriptionsBundleJson.getBaseEntitlementAndAddOns()) {
+ // verifications
+ verifyNonNullOrEmpty(entitlement, "SubscriptionJson body should be specified for each element");
+ if (entitlement.getPlanName() == null) {
+ verifyNonNullOrEmpty(entitlement.getProductName(), "SubscriptionJson productName needs to be set when no planName is specified",
+ entitlement.getProductCategory(), "SubscriptionJson productCategory needs to be set when no planName is specified",
+ entitlement.getBillingPeriod(), "SubscriptionJson billingPeriod needs to be set when no planName is specified",
+ entitlement.getPriceList(), "SubscriptionJson priceList needs to be set when no planName is specified");
+ } else {
+ Preconditions.checkArgument(entitlement.getProductName() == null, "SubscriptionJson productName should not be set when planName is specified");
+ Preconditions.checkArgument(entitlement.getProductCategory() == null, "SubscriptionJson productCategory should not be set when planName is specified");
+ Preconditions.checkArgument(entitlement.getBillingPeriod() == null, "SubscriptionJson billingPeriod should not be set when planName is specified");
+ Preconditions.checkArgument(entitlement.getPriceList() == null, "SubscriptionJson priceList should not be set when planName is specified");
+ }
+ Preconditions.checkArgument(account.getId().equals(entitlement.getAccountId()), "SubscriptionJson accountId should be the same for each element");
+ // If set on one element, it should be set on all elements
+ Preconditions.checkArgument(bundleId == null || bundleId.equals(entitlement.getBundleId()), "SubscriptionJson bundleId should be the same for each element");
+ if (bundleId == null) {
+ bundleId = entitlement.getBundleId();
+ }
+ // Can be set on a single element (e.g. BASE + ADD_ON for a new bundle)
+ Preconditions.checkArgument(bundleExternalKey == null || entitlement.getExternalKey() == null || bundleExternalKey.equals(entitlement.getExternalKey()), "SubscriptionJson externalKey should be the same for each element");
+ if (bundleExternalKey == null) {
+ bundleExternalKey = entitlement.getExternalKey();
+ }
+ // create the entitlementSpecifier
+ buildEntitlementSpecifier(entitlement, account.getCurrency(), entitlementSpecifierList);
}
- // create the baseEntitlementSpecifierWithAddOns
final LocalDate resolvedEntitlementDate = requestedDate != null ? toLocalDate(requestedDate) : toLocalDate(entitlementDate);
final LocalDate resolvedBillingDate = requestedDate != null ? toLocalDate(requestedDate) : toLocalDate(billingDate);
final BaseEntitlementWithAddOnsSpecifier baseEntitlementSpecifierWithAddOns = buildBaseEntitlementWithAddOnsSpecifier(entitlementSpecifierList,
resolvedEntitlementDate,
resolvedBillingDate,
+ bundleId,
bundleExternalKey,
isMigrated);
baseEntitlementWithAddOnsSpecifierList.add(baseEntitlementSpecifierWithAddOns);
@@ -382,7 +311,7 @@ public class SubscriptionResource extends JaxRsResourceBase {
final EntitlementCallCompletionCallback<List<UUID>> callback = new EntitlementCallCompletionCallback<List<UUID>>() {
@Override
- public List<UUID> doOperation(final CallContext ctx) throws InterruptedException, TimeoutException, EntitlementApiException, SubscriptionApiException, AccountApiException {
+ public List<UUID> doOperation(final CallContext ctx) throws EntitlementApiException {
return entitlementApi.createBaseEntitlementsWithAddOns(account.getId(), baseEntitlementWithAddOnsSpecifierList, renameKeyIfExistsAndUnused, pluginProperties, callContext);
}
@@ -393,6 +322,10 @@ public class SubscriptionResource extends JaxRsResourceBase {
@Override
public Response doResponseOk(final List<UUID> entitlementIds) {
+ if (responseObject == ObjectType.SUBSCRIPTION) {
+ return uriBuilder.buildResponse(uriInfo, SubscriptionResource.class, "getEntitlement", Iterables.getFirst(entitlementIds, null), request);
+ }
+
final Collection<String> bundleIds = new LinkedHashSet<String>();
try {
for (final Entitlement entitlement : entitlementApi.getAllEntitlementsForAccountId(account.getId(), callContext)) {
@@ -417,50 +350,6 @@ public class SubscriptionResource extends JaxRsResourceBase {
return callCompletionCreation.withSynchronization(callback, timeoutSec, callCompletion, callContext);
}
- private List<EntitlementSpecifier> buildEntitlementSpecifierList(final SubscriptionJson baseEntitlement,
- final Iterable<SubscriptionJson> addonEntitlements,
- final Currency currency) {
- final List<EntitlementSpecifier> entitlementSpecifierList = new ArrayList<EntitlementSpecifier>();
-
- //
- // BASE or STANDALONE is fully specified, we can add it
- //
- buildEntitlementSpecifier(baseEntitlement, currency, entitlementSpecifierList);
-
- for (final SubscriptionJson entitlement : addonEntitlements) {
- // verifications
- verifyNonNullOrEmpty(entitlement, "SubscriptionJson body should be specified for each element");
- if (entitlement.getPlanName() == null) {
- verifyNonNullOrEmpty(entitlement.getProductName(), "SubscriptionJson productName needs to be set for each element",
- entitlement.getProductCategory(), "SubscriptionJson productCategory needs to be set for each element",
- entitlement.getBillingPeriod(), "SubscriptionJson billingPeriod needs to be set for each element",
- entitlement.getPriceList(), "SubscriptionJson priceList needs to be set for each element");
- }
- // create the entitlementSpecifier
- buildEntitlementSpecifier(entitlement, currency, entitlementSpecifierList);
- }
- return entitlementSpecifierList;
- }
-
- private List<EntitlementSpecifier> buildEntitlementSpecifierList(final Iterable<SubscriptionJson> standaloneEntitlements,
- final Currency currency) {
- final List<EntitlementSpecifier> entitlementSpecifierList = new ArrayList<EntitlementSpecifier>();
-
- for (final SubscriptionJson standaloneEntitlement : standaloneEntitlements) {
- // verifications
- verifyNonNullOrEmpty(standaloneEntitlement, "SubscriptionJson body should be specified for each element");
- if (standaloneEntitlement.getPlanName() == null) {
- verifyNonNullOrEmpty(standaloneEntitlement.getProductName(), "SubscriptionJson productName needs to be set for each element",
- standaloneEntitlement.getProductCategory(), "SubscriptionJson productCategory needs to be set for each element",
- standaloneEntitlement.getBillingPeriod(), "SubscriptionJson billingPeriod needs to be set for each element",
- standaloneEntitlement.getPriceList(), "SubscriptionJson priceList needs to be set for each element");
- }
- // create the entitlementSpecifier
- buildEntitlementSpecifier(standaloneEntitlement, currency, entitlementSpecifierList);
- }
- return entitlementSpecifierList;
- }
-
private void buildEntitlementSpecifier(final SubscriptionJson subscriptionJson,
final Currency currency,
final Collection<EntitlementSpecifier> entitlementSpecifierList) {
@@ -477,7 +366,7 @@ public class SubscriptionResource extends JaxRsResourceBase {
new PlanPhaseSpecifier(subscriptionJson.getProductName(),
subscriptionJson.getBillingPeriod(),
subscriptionJson.getPriceList(),
- null);
+ subscriptionJson.getPhaseType());
final List<PlanPhasePriceOverride> overrides = PhasePriceOverrideJson.toPlanPhasePriceOverrides(subscriptionJson.getPriceOverrides(),
planPhaseSpecifier,
currency);
@@ -499,12 +388,13 @@ public class SubscriptionResource extends JaxRsResourceBase {
private BaseEntitlementWithAddOnsSpecifier buildBaseEntitlementWithAddOnsSpecifier(final Iterable<EntitlementSpecifier> entitlementSpecifierList,
final LocalDate resolvedEntitlementDate,
final LocalDate resolvedBillingDate,
+ @Nullable final UUID bundleId,
@Nullable final String bundleExternalKey,
final Boolean isMigrated) {
return new BaseEntitlementWithAddOnsSpecifier() {
@Override
public UUID getBundleId() {
- return null;
+ return bundleId;
}
@Override
@@ -623,8 +513,8 @@ public class SubscriptionResource extends JaxRsResourceBase {
private boolean isImmediateOp = true;
@Override
- public Response doOperation(final CallContext ctx) throws EntitlementApiException, InterruptedException,
- TimeoutException, AccountApiException {
+ public Response doOperation(final CallContext ctx) throws EntitlementApiException,
+ AccountApiException {
final Entitlement current = entitlementApi.getEntitlementForId(subscriptionId, callContext);
final LocalDate inputLocalDate = toLocalDate(requestedDate);
@@ -721,8 +611,8 @@ public class SubscriptionResource extends JaxRsResourceBase {
@Override
public Response doOperation(final CallContext ctx)
- throws EntitlementApiException, InterruptedException,
- TimeoutException, AccountApiException, SubscriptionApiException {
+ throws EntitlementApiException,
+ SubscriptionApiException {
final Entitlement current = entitlementApi.getEntitlementForId(subscriptionId, ctx);
final LocalDate inputLocalDate = toLocalDate(requestedDate);
final Entitlement newEntitlement;
@@ -738,7 +628,7 @@ public class SubscriptionResource extends JaxRsResourceBase {
final Subscription subscription = subscriptionApi.getSubscriptionForEntitlementId(newEntitlement.getId(), ctx);
- final LocalDate nowInAccountTimeZone = new LocalDate(clock.getUTCNow(), subscription.getBillingEndDate().getChronology().getZone());
+ final LocalDate nowInAccountTimeZone = new LocalDate(callContext.getCreatedDate(), subscription.getBillingEndDate().getChronology().getZone());
isImmediateOp = subscription.getBillingEndDate() != null &&
!subscription.getBillingEndDate().isAfter(nowInAccountTimeZone);
return Response.status(Status.NO_CONTENT).build();
@@ -775,7 +665,7 @@ public class SubscriptionResource extends JaxRsResourceBase {
@HeaderParam(HDR_REASON) final String reason,
@HeaderParam(HDR_COMMENT) final String comment,
@javax.ws.rs.core.Context final UriInfo uriInfo,
- @javax.ws.rs.core.Context final HttpServletRequest request) throws SubscriptionApiException, EntitlementApiException, AccountApiException {
+ @javax.ws.rs.core.Context final HttpServletRequest request) throws EntitlementApiException, AccountApiException {
verifyNonNullOrEmpty(json, "SubscriptionJson body should be specified");
verifyNonNullOrEmpty(json.getBillCycleDayLocal(), "SubscriptionJson new BCD should be specified");
@@ -786,7 +676,7 @@ public class SubscriptionResource extends JaxRsResourceBase {
final Entitlement entitlement = entitlementApi.getEntitlementForId(subscriptionId, callContext);
if (effectiveFromDateStr != null) {
final Account account = accountUserApi.getAccountById(entitlement.getAccountId(), callContext);
- final LocalDate accountToday = new LocalDate(clock.getUTCNow(), account.getTimeZone());
+ final LocalDate accountToday = new LocalDate(callContext.getCreatedDate(), account.getTimeZone());
int comp = effectiveFromDate.compareTo(accountToday);
switch (comp) {
case -1:
@@ -872,11 +762,11 @@ public class SubscriptionResource extends JaxRsResourceBase {
private interface EntitlementCallCompletionCallback<T> {
- public T doOperation(final CallContext ctx) throws EntitlementApiException, InterruptedException, TimeoutException, AccountApiException, SubscriptionApiException;
+ T doOperation(final CallContext ctx) throws EntitlementApiException, InterruptedException, TimeoutException, AccountApiException, SubscriptionApiException;
- public boolean isImmOperation();
+ boolean isImmOperation();
- public Response doResponseOk(final T operationResponse) throws SubscriptionApiException, AccountApiException, CatalogApiException;
+ Response doResponseOk(final T operationResponse) throws SubscriptionApiException, AccountApiException, CatalogApiException;
}
private class EntitlementCallCompletion<T> {
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/UsageResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/UsageResource.java
index b9f1c65..7aa2203 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/UsageResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/UsageResource.java
@@ -59,9 +59,12 @@ import org.killbill.billing.util.callcontext.TenantContext;
import org.killbill.clock.Clock;
import org.killbill.commons.metrics.TimedResource;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
+import com.google.common.collect.Ordering;
import com.google.inject.Singleton;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@@ -125,8 +128,11 @@ public class UsageResource extends JaxRsResourceBase {
final CallContext callContext = context.createCallContextNoAccountId(createdBy, reason, comment, request);
// Verify subscription exists..
final Entitlement entitlement = entitlementApi.getEntitlementForId(json.getSubscriptionId(), callContext);
- if (entitlement.getState() != EntitlementState.ACTIVE) {
- return Response.status(Status.BAD_REQUEST).build();
+ if (entitlement.getEffectiveEndDate() != null) {
+ final LocalDate highestRecordDate = getHighestRecordDate(json.getUnitUsageRecords());
+ if (entitlement.getEffectiveEndDate().compareTo(highestRecordDate) < 0) {
+ return Response.status(Status.BAD_REQUEST).build();
+ }
}
final SubscriptionUsageRecord record = json.toSubscriptionUsageRecord();
@@ -134,6 +140,28 @@ public class UsageResource extends JaxRsResourceBase {
return Response.status(Status.CREATED).build();
}
+ @VisibleForTesting
+ LocalDate getHighestRecordDate(final List<UnitUsageRecordJson> records) {
+ final Iterable<Iterable<LocalDate>> recordedDates = Iterables.transform(records, new Function<UnitUsageRecordJson, Iterable<LocalDate>>() {
+
+ @Override
+ public Iterable<LocalDate> apply(final UnitUsageRecordJson input) {
+ final Iterable<LocalDate> result = Iterables.transform(input.getUsageRecords(), new Function<UsageRecordJson, LocalDate>() {
+ @Override
+ public LocalDate apply(final UsageRecordJson input) {
+ return input.getRecordDate();
+ }
+ });
+ return result;
+ }
+ });
+ final Iterable<LocalDate> sortedRecordedDates = Ordering.<LocalDate>natural()
+ .reverse()
+ .sortedCopy(Iterables.concat(recordedDates));
+
+ return Iterables.getFirst(sortedRecordedDates, null);
+ }
+
@TimedResource
@GET
@Path("/{subscriptionId:" + UUID_PATTERN + "}/{unitType}")
diff --git a/jaxrs/src/test/java/org/killbill/billing/jaxrs/JaxrsTestSuiteNoDB.java b/jaxrs/src/test/java/org/killbill/billing/jaxrs/JaxrsTestSuiteNoDB.java
index ee72582..4d4eb02 100644
--- a/jaxrs/src/test/java/org/killbill/billing/jaxrs/JaxrsTestSuiteNoDB.java
+++ b/jaxrs/src/test/java/org/killbill/billing/jaxrs/JaxrsTestSuiteNoDB.java
@@ -34,6 +34,10 @@ public abstract class JaxrsTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
@BeforeClass(groups = "fast")
protected void beforeClass() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
final Injector injector = Guice.createInjector(new TestJaxrsModuleNoDB(configSource));
injector.injectMembers(this);
}
diff --git a/jaxrs/src/test/java/org/killbill/billing/jaxrs/JaxrsTestUtils.java b/jaxrs/src/test/java/org/killbill/billing/jaxrs/JaxrsTestUtils.java
index 3f88ac8..1055887 100644
--- a/jaxrs/src/test/java/org/killbill/billing/jaxrs/JaxrsTestUtils.java
+++ b/jaxrs/src/test/java/org/killbill/billing/jaxrs/JaxrsTestUtils.java
@@ -31,7 +31,7 @@ public abstract class JaxrsTestUtils {
final List<AuditLogJson> auditLogs = new ArrayList<AuditLogJson>();
for (int i = 0; i < 20; i++) {
auditLogs.add(new AuditLogJson(UUIDs.randomUUID().toString(), changeDate, ObjectType.BUNDLE, UUIDs.randomUUID(), UUIDs.randomUUID().toString(),
- UUIDs.randomUUID().toString(), UUIDs.randomUUID().toString(), UUIDs.randomUUID().toString()));
+ UUIDs.randomUUID().toString(), UUIDs.randomUUID().toString(), UUIDs.randomUUID().toString(), null));
}
return auditLogs;
diff --git a/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestAuditLogJson.java b/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestAuditLogJson.java
index 6dce63f..92b77c5 100644
--- a/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestAuditLogJson.java
+++ b/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestAuditLogJson.java
@@ -44,7 +44,7 @@ public class TestAuditLogJson extends JaxrsTestSuiteNoDB {
final UUID objectId = UUID.randomUUID();
final ObjectType objectType = ObjectType.BUNDLE;
- final AuditLogJson auditLogJson = new AuditLogJson(changeType, changeDate, objectType, objectId, changedBy, reasonCode, comments, userToken);
+ final AuditLogJson auditLogJson = new AuditLogJson(changeType, changeDate, objectType, objectId, changedBy, reasonCode, comments, userToken, null);
Assert.assertEquals(auditLogJson.getChangeType(), changeType);
Assert.assertEquals(auditLogJson.getChangeDate(), changeDate);
Assert.assertEquals(auditLogJson.getChangedBy(), changedBy);
@@ -63,7 +63,8 @@ public class TestAuditLogJson extends JaxrsTestSuiteNoDB {
"\"changedBy\":\"" + auditLogJson.getChangedBy() + "\"," +
"\"reasonCode\":\"" + auditLogJson.getReasonCode() + "\"," +
"\"comments\":\"" + auditLogJson.getComments() + "\"," +
- "\"userToken\":\"" + auditLogJson.getUserToken() + "\"}");
+ "\"userToken\":\"" + auditLogJson.getUserToken() + "\"," +
+ "\"history\":" + auditLogJson.getHistory() + "}");
final AuditLogJson fromJson = mapper.readValue(asJson, AuditLogJson.class);
Assert.assertEquals(fromJson, auditLogJson);
diff --git a/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestInvoiceItemJsonSimple.java b/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestInvoiceItemJsonSimple.java
index b02eb98..534f0dc 100644
--- a/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestInvoiceItemJsonSimple.java
+++ b/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestInvoiceItemJsonSimple.java
@@ -93,8 +93,9 @@ public class TestInvoiceItemJsonSimple extends JaxrsTestSuiteNoDB {
Mockito.when(invoiceItem.getPhaseName()).thenReturn(UUID.randomUUID().toString());
Mockito.when(invoiceItem.getUsageName()).thenReturn(UUID.randomUUID().toString());
Mockito.when(invoiceItem.getDescription()).thenReturn(UUID.randomUUID().toString());
- Mockito.when(invoiceItem.getStartDate()).thenReturn(clock.getUTCToday());
- Mockito.when(invoiceItem.getEndDate()).thenReturn(clock.getUTCToday());
+ final LocalDate utcToday = clock.getUTCToday();
+ Mockito.when(invoiceItem.getStartDate()).thenReturn(utcToday);
+ Mockito.when(invoiceItem.getEndDate()).thenReturn(utcToday);
Mockito.when(invoiceItem.getAmount()).thenReturn(BigDecimal.TEN);
Mockito.when(invoiceItem.getCurrency()).thenReturn(Currency.EUR);
Mockito.when(invoiceItem.getInvoiceItemType()).thenReturn(InvoiceItemType.FIXED);
diff --git a/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestInvoiceJsonWithBundleKeys.java b/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestInvoiceJsonWithBundleKeys.java
index 4633d08..003e52b 100644
--- a/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestInvoiceJsonWithBundleKeys.java
+++ b/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestInvoiceJsonWithBundleKeys.java
@@ -84,8 +84,9 @@ public class TestInvoiceJsonWithBundleKeys extends JaxrsTestSuiteNoDB {
Mockito.when(invoice.getCreditedAmount()).thenReturn(BigDecimal.ONE);
Mockito.when(invoice.getRefundedAmount()).thenReturn(BigDecimal.ONE);
Mockito.when(invoice.getId()).thenReturn(UUID.randomUUID());
- Mockito.when(invoice.getInvoiceDate()).thenReturn(clock.getUTCToday());
- Mockito.when(invoice.getTargetDate()).thenReturn(clock.getUTCToday());
+ final LocalDate utcToday = clock.getUTCToday();
+ Mockito.when(invoice.getInvoiceDate()).thenReturn(utcToday);
+ Mockito.when(invoice.getTargetDate()).thenReturn(utcToday);
Mockito.when(invoice.getInvoiceNumber()).thenReturn(Integer.MAX_VALUE);
Mockito.when(invoice.getBalance()).thenReturn(BigDecimal.ZERO);
Mockito.when(invoice.getAccountId()).thenReturn(UUID.randomUUID());
diff --git a/jaxrs/src/test/java/org/killbill/billing/jaxrs/resources/TestJaxRsResourceBase.java b/jaxrs/src/test/java/org/killbill/billing/jaxrs/resources/TestJaxRsResourceBase.java
index 85195c0..979f012 100644
--- a/jaxrs/src/test/java/org/killbill/billing/jaxrs/resources/TestJaxRsResourceBase.java
+++ b/jaxrs/src/test/java/org/killbill/billing/jaxrs/resources/TestJaxRsResourceBase.java
@@ -19,7 +19,10 @@ package org.killbill.billing.jaxrs.resources;
import java.util.List;
+import org.joda.time.LocalDate;
import org.killbill.billing.jaxrs.JaxrsTestSuiteNoDB;
+import org.killbill.billing.jaxrs.json.SubscriptionUsageRecordJson.UnitUsageRecordJson;
+import org.killbill.billing.jaxrs.json.SubscriptionUsageRecordJson.UsageRecordJson;
import org.killbill.billing.payment.api.PluginProperty;
import org.testng.Assert;
import org.testng.annotations.Test;
@@ -67,4 +70,50 @@ public class TestJaxRsResourceBase extends JaxrsTestSuiteNoDB {
super(null, null, null, null, null, null, null, null, null);
}
}
+
+
+ @Test(groups = "fast")
+ public void testGetHighestRecordDate() throws Exception {
+ final UsageResourceTest usageResource = new UsageResourceTest();
+
+ final List<UsageRecordJson> fooRecords = ImmutableList.<UsageRecordJson>builder()
+ .add(new UsageRecordJson(new LocalDate(2018, 03, 04), 28L))
+ .add(new UsageRecordJson(new LocalDate(2018, 03, 05), 2L))
+ .add(new UsageRecordJson(new LocalDate(2018, 03, 01), 1L))
+ .add(new UsageRecordJson(new LocalDate(2018, 04, 06), 24L))
+ .build();
+ final UnitUsageRecordJson unitRecordFoo = new UnitUsageRecordJson("foo", fooRecords);
+
+ final List<UsageRecordJson> barRecords = ImmutableList.<UsageRecordJson>builder()
+ .add(new UsageRecordJson(new LocalDate(2018, 02, 04), 28L))
+ .add(new UsageRecordJson(new LocalDate(2018, 03, 06), 2L))
+ .add(new UsageRecordJson(new LocalDate(2018, 04, 18), 1L)) // Highest date point
+ .add(new UsageRecordJson(new LocalDate(2018, 04, 13), 24L))
+ .build();
+ final UnitUsageRecordJson unitRecordBar = new UnitUsageRecordJson("bar", barRecords);
+
+ final List<UsageRecordJson> zooRecords = ImmutableList.<UsageRecordJson>builder()
+ .add(new UsageRecordJson(new LocalDate(2018, 02, 04), 28L))
+ .add(new UsageRecordJson(new LocalDate(2018, 03, 06), 2L))
+ .add(new UsageRecordJson(new LocalDate(2018, 04, 17), 1L))
+ .add(new UsageRecordJson(new LocalDate(2018, 04, 12), 24L))
+ .build();
+ final UnitUsageRecordJson unitRecordZoo = new UnitUsageRecordJson("zoo", zooRecords);
+
+ final List<UnitUsageRecordJson> input = ImmutableList.<UnitUsageRecordJson>builder()
+ .add(unitRecordFoo)
+ .add(unitRecordBar)
+ .add(unitRecordZoo)
+ .build();
+ final LocalDate result = usageResource.getHighestRecordDate(input);
+
+ Assert.assertTrue(result.compareTo(new LocalDate(2018, 04, 18)) == 0);
+ }
+
+
+ private static class UsageResourceTest extends UsageResource {
+ public UsageResourceTest() {
+ super(null, null, null, null, null, null, null, null, null, null);
+ }
+ }
}
junction/pom.xml 2(+1 -1)
diff --git a/junction/pom.xml b/junction/pom.xml
index e861302..6c36aed 100644
--- a/junction/pom.xml
+++ b/junction/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.19.11-SNAPSHOT</version>
+ <version>0.19.13-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-junction</artifactId>
diff --git a/junction/src/test/java/org/killbill/billing/junction/glue/TestJunctionModuleWithEmbeddedDB.java b/junction/src/test/java/org/killbill/billing/junction/glue/TestJunctionModuleWithEmbeddedDB.java
index f90fcd4..32b2ffc 100644
--- a/junction/src/test/java/org/killbill/billing/junction/glue/TestJunctionModuleWithEmbeddedDB.java
+++ b/junction/src/test/java/org/killbill/billing/junction/glue/TestJunctionModuleWithEmbeddedDB.java
@@ -25,6 +25,7 @@ import org.killbill.billing.catalog.glue.CatalogModule;
import org.killbill.billing.entitlement.glue.DefaultEntitlementModule;
import org.killbill.billing.platform.api.KillbillConfigSource;
import org.killbill.billing.subscription.glue.DefaultSubscriptionModule;
+import org.killbill.billing.util.glue.AuditModule;
import org.killbill.billing.util.glue.NonEntityDaoModule;
import org.killbill.billing.util.glue.TagStoreModule;
@@ -45,6 +46,7 @@ public class TestJunctionModuleWithEmbeddedDB extends TestJunctionModule {
install(new DefaultEntitlementModule(configSource));
install(new DefaultSubscriptionModule(configSource));
install(new TagStoreModule(configSource));
+ install(new AuditModule(configSource));
bind(TestApiListener.class).asEagerSingleton();
}
diff --git a/junction/src/test/java/org/killbill/billing/junction/JunctionTestSuiteNoDB.java b/junction/src/test/java/org/killbill/billing/junction/JunctionTestSuiteNoDB.java
index c471a7d..eaa44c9 100644
--- a/junction/src/test/java/org/killbill/billing/junction/JunctionTestSuiteNoDB.java
+++ b/junction/src/test/java/org/killbill/billing/junction/JunctionTestSuiteNoDB.java
@@ -60,17 +60,29 @@ public abstract class JunctionTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
@BeforeClass(groups = "fast")
protected void beforeClass() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
final Injector injector = Guice.createInjector(new TestJunctionModuleNoDB(configSource));
injector.injectMembers(this);
}
@BeforeMethod(groups = "fast")
public void beforeMethod() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
bus.start();
}
@AfterMethod(groups = "fast")
public void afterMethod() {
+ if (hasFailed()) {
+ return;
+ }
+
bus.stop();
}
}
diff --git a/junction/src/test/java/org/killbill/billing/junction/JunctionTestSuiteWithEmbeddedDB.java b/junction/src/test/java/org/killbill/billing/junction/JunctionTestSuiteWithEmbeddedDB.java
index 859d024..499fd9a 100644
--- a/junction/src/test/java/org/killbill/billing/junction/JunctionTestSuiteWithEmbeddedDB.java
+++ b/junction/src/test/java/org/killbill/billing/junction/JunctionTestSuiteWithEmbeddedDB.java
@@ -99,12 +99,20 @@ public abstract class JunctionTestSuiteWithEmbeddedDB extends GuicyKillbillTestS
@BeforeClass(groups = "slow")
protected void beforeClass() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
final Injector injector = Guice.createInjector(Stage.PRODUCTION, new TestJunctionModuleWithEmbeddedDB(configSource));
injector.injectMembers(this);
}
@BeforeMethod(groups = "slow")
public void beforeMethod() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
super.beforeMethod();
startTestFamework();
this.catalog = initCatalog(catalogService);
@@ -112,6 +120,10 @@ public abstract class JunctionTestSuiteWithEmbeddedDB extends GuicyKillbillTestS
@AfterMethod(groups = "slow")
public void afterMethod() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
stopTestFramework();
}
NEWS 8(+8 -0)
diff --git a/NEWS b/NEWS
index cc38732..28f753d 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,11 @@
+0.19.12
+ New API to retrieve history info from any objects
+ Entitlement/subscription refactoring and incremental perf improvements
+
+0.19.11
+ Subscription and entitlement rework mostly targeted around performance
+ Add ability to use read only instances from JAXRS and plugin layer
+
0.19.10
Entitlement and Subscription performance
Fix issue in invoice plugin ordering
overdue/pom.xml 2(+1 -1)
diff --git a/overdue/pom.xml b/overdue/pom.xml
index 50e64d0..9659f11 100644
--- a/overdue/pom.xml
+++ b/overdue/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.19.11-SNAPSHOT</version>
+ <version>0.19.13-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-overdue</artifactId>
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/calculator/BillingStateCalculator.java b/overdue/src/main/java/org/killbill/billing/overdue/calculator/BillingStateCalculator.java
index 5c2a254..a9b1632 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/calculator/BillingStateCalculator.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/calculator/BillingStateCalculator.java
@@ -30,6 +30,7 @@ import java.util.UUID;
import org.joda.time.LocalDate;
import org.killbill.billing.ObjectType;
import org.killbill.billing.account.api.ImmutableAccountData;
+import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.invoice.api.Invoice;
import org.killbill.billing.invoice.api.InvoiceInternalApi;
@@ -68,7 +69,7 @@ public class BillingStateCalculator {
this.tagApi = tagApi;
}
- public BillingState calculateBillingState(final ImmutableAccountData account, final InternalTenantContext context) throws OverdueException {
+ public BillingState calculateBillingState(final ImmutableAccountData account, final InternalCallContext context) throws OverdueException {
final SortedSet<Invoice> unpaidInvoices = unpaidInvoicesForAccount(account.getId(), context);
final int numberOfUnpaidInvoices = unpaidInvoices.size();
@@ -104,8 +105,8 @@ public class BillingStateCalculator {
return sum;
}
- SortedSet<Invoice> unpaidInvoicesForAccount(final UUID accountId, final InternalTenantContext context) {
- final Collection<Invoice> invoices = invoiceApi.getUnpaidInvoicesByAccountId(accountId, context.toLocalDate(clock.getUTCNow()), context);
+ SortedSet<Invoice> unpaidInvoicesForAccount(final UUID accountId, final InternalCallContext context) {
+ final Collection<Invoice> invoices = invoiceApi.getUnpaidInvoicesByAccountId(accountId, context.toLocalDate(context.getCreatedDate()), context);
final SortedSet<Invoice> sortedInvoices = new TreeSet<Invoice>(new InvoiceDateComparator());
sortedInvoices.addAll(invoices);
return sortedInvoices;
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/listener/OverdueListener.java b/overdue/src/main/java/org/killbill/billing/overdue/listener/OverdueListener.java
index 8bed766..7ecdb5c 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/listener/OverdueListener.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/listener/OverdueListener.java
@@ -155,7 +155,7 @@ public class OverdueListener {
if (shouldInsertNotification) {
OverdueAsyncBusNotificationKey notificationKey = new OverdueAsyncBusNotificationKey(accountId, action);
- asyncPoster.insertOverdueNotification(accountId, clock.getUTCNow(), OverdueAsyncBusNotifier.OVERDUE_ASYNC_BUS_NOTIFIER_QUEUE, notificationKey, callContext);
+ asyncPoster.insertOverdueNotification(accountId, callContext.getCreatedDate(), OverdueAsyncBusNotifier.OVERDUE_ASYNC_BUS_NOTIFIER_QUEUE, notificationKey, callContext);
try {
final List<Account> childrenAccounts = accountApi.getChildrenAccounts(accountId, callContext);
@@ -166,7 +166,7 @@ public class OverdueListener {
final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(childAccount.getId(), callContext);
final InternalCallContext accountContext = internalCallContextFactory.createInternalCallContext(internalTenantContext.getAccountRecordId(), callContext);
notificationKey = new OverdueAsyncBusNotificationKey(childAccount.getId(), action);
- asyncPoster.insertOverdueNotification(childAccount.getId(), clock.getUTCNow(), OverdueAsyncBusNotifier.OVERDUE_ASYNC_BUS_NOTIFIER_QUEUE, notificationKey, accountContext);
+ asyncPoster.insertOverdueNotification(childAccount.getId(), callContext.getCreatedDate(), OverdueAsyncBusNotifier.OVERDUE_ASYNC_BUS_NOTIFIER_QUEUE, notificationKey, accountContext);
}
}
}
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/wrapper/OverdueWrapper.java b/overdue/src/main/java/org/killbill/billing/overdue/wrapper/OverdueWrapper.java
index 11481f9..750ad19 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/wrapper/OverdueWrapper.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/wrapper/OverdueWrapper.java
@@ -104,7 +104,7 @@ public class OverdueWrapper {
final BlockingState blockingStateForService = api.getBlockingStateForService(overdueable.getId(), BlockingStateType.ACCOUNT, OverdueService.OVERDUE_SERVICE_NAME, context);
final String previousOverdueStateName = blockingStateForService != null ? blockingStateForService.getStateName() : OverdueWrapper.CLEAR_STATE_NAME;
final OverdueState currentOverdueState = overdueStateSet.findState(previousOverdueStateName);
- final OverdueState nextOverdueState = overdueStateSet.calculateOverdueState(billingState, context.toLocalDate(clock.getUTCNow()));
+ final OverdueState nextOverdueState = overdueStateSet.calculateOverdueState(billingState, context.toLocalDate(context.getCreatedDate()));
overdueStateApplicator.apply(effectiveDate, overdueStateSet, billingState, overdueable, currentOverdueState, nextOverdueState, context);
diff --git a/overdue/src/test/java/org/killbill/billing/overdue/calculator/TestBillingStateCalculator.java b/overdue/src/test/java/org/killbill/billing/overdue/calculator/TestBillingStateCalculator.java
index 0ac1feb..598f22d 100644
--- a/overdue/src/test/java/org/killbill/billing/overdue/calculator/TestBillingStateCalculator.java
+++ b/overdue/src/test/java/org/killbill/billing/overdue/calculator/TestBillingStateCalculator.java
@@ -26,6 +26,7 @@ import java.util.UUID;
import org.joda.time.DateTimeZone;
import org.joda.time.LocalDate;
import org.killbill.billing.account.api.ImmutableAccountData;
+import org.killbill.billing.callcontext.InternalCallContext;
import org.mockito.Mockito;
import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
@@ -63,7 +64,7 @@ public class TestBillingStateCalculator extends OverdueTestSuiteNoDB {
return new BillingStateCalculator(invoiceApi, clock, tagInternalApi) {
@Override
public BillingState calculateBillingState(final ImmutableAccountData overdueable,
- final InternalTenantContext context) {
+ final InternalCallContext context) {
return null;
}
};
diff --git a/overdue/src/test/java/org/killbill/billing/overdue/OverdueTestSuiteNoDB.java b/overdue/src/test/java/org/killbill/billing/overdue/OverdueTestSuiteNoDB.java
index d7d6462..5e2f224 100644
--- a/overdue/src/test/java/org/killbill/billing/overdue/OverdueTestSuiteNoDB.java
+++ b/overdue/src/test/java/org/killbill/billing/overdue/OverdueTestSuiteNoDB.java
@@ -108,12 +108,20 @@ public abstract class OverdueTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
@BeforeClass(groups = "fast")
protected void beforeClass() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
final Injector injector = Guice.createInjector(new TestOverdueModuleNoDB(configSource));
injector.injectMembers(this);
}
@BeforeMethod(groups = "fast")
public void beforeMethod() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
bus.start();
service.initialize();
service.start();
@@ -121,6 +129,10 @@ public abstract class OverdueTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
@AfterMethod(groups = "fast")
public void afterMethod() {
+ if (hasFailed()) {
+ return;
+ }
+
service.stop();
bus.stop();
}
diff --git a/overdue/src/test/java/org/killbill/billing/overdue/OverdueTestSuiteWithEmbeddedDB.java b/overdue/src/test/java/org/killbill/billing/overdue/OverdueTestSuiteWithEmbeddedDB.java
index b74d629..da37b6c 100644
--- a/overdue/src/test/java/org/killbill/billing/overdue/OverdueTestSuiteWithEmbeddedDB.java
+++ b/overdue/src/test/java/org/killbill/billing/overdue/OverdueTestSuiteWithEmbeddedDB.java
@@ -102,12 +102,20 @@ public abstract class OverdueTestSuiteWithEmbeddedDB extends GuicyKillbillTestSu
@BeforeClass(groups = "slow")
protected void beforeClass() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
final Injector injector = Guice.createInjector(new TestOverdueModuleWithEmbeddedDB(configSource));
injector.injectMembers(this);
}
@BeforeMethod(groups = "slow")
public void beforeMethod() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
super.beforeMethod();
cacheControllerDispatcher.clearAll();
bus.start();
@@ -119,6 +127,10 @@ public abstract class OverdueTestSuiteWithEmbeddedDB extends GuicyKillbillTestSu
@AfterMethod(groups = "slow")
public void afterMethod() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
service.stop();
bus.unregister(listener);
bus.stop();
payment/pom.xml 2(+1 -1)
diff --git a/payment/pom.xml b/payment/pom.xml
index 66b0100..27f27cf 100644
--- a/payment/pom.xml
+++ b/payment/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.19.11-SNAPSHOT</version>
+ <version>0.19.13-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-payment</artifactId>
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentTransactionTask.java b/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentTransactionTask.java
index 1e101e1..ff535d7 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentTransactionTask.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentTransactionTask.java
@@ -276,13 +276,13 @@ public class IncompletePaymentTransactionTask extends CompletionTaskBase<Payment
}
@VisibleForTesting
- DateTime getNextNotificationTime(final TransactionStatus transactionStatus, final Integer attemptNumber, final InternalTenantContext tenantContext) {
+ DateTime getNextNotificationTime(final TransactionStatus transactionStatus, final Integer attemptNumber, final InternalTenantContext internalTenantContext) {
final List<TimeSpan> retries;
if (TransactionStatus.UNKNOWN.equals(transactionStatus)) {
- retries = paymentConfig.getUnknownTransactionsRetries(tenantContext);
+ retries = paymentConfig.getUnknownTransactionsRetries(internalTenantContext);
} else if (TransactionStatus.PENDING.equals(transactionStatus)) {
- retries = paymentConfig.getPendingTransactionsRetries(tenantContext);
+ retries = paymentConfig.getPendingTransactionsRetries(internalTenantContext);
} else {
retries = ImmutableList.of();
log.warn("Unexpected transactionStatus='{}' from janitor, ignore...", transactionStatus);
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/PluginControlPaymentProcessor.java b/payment/src/main/java/org/killbill/billing/payment/core/PluginControlPaymentProcessor.java
index 651d2d1..367db03 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/PluginControlPaymentProcessor.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/PluginControlPaymentProcessor.java
@@ -327,6 +327,7 @@ public class PluginControlPaymentProcessor extends ProcessorBase {
callContext,
internalCallContext);
+ log.debug("retryPaymentTransaction result: payment='{}'", payment);
paymentTransaction = Iterables.<PaymentTransaction>find(Lists.<PaymentTransaction>reverse(payment.getTransactions()),
new Predicate<PaymentTransaction>() {
@Override
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/CompletionControlOperation.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/CompletionControlOperation.java
index 7c503ea..91fea81 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/CompletionControlOperation.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/CompletionControlOperation.java
@@ -35,6 +35,8 @@ import org.killbill.billing.payment.dispatcher.PluginDispatcher;
import org.killbill.billing.payment.dispatcher.PluginDispatcher.PluginDispatcherReturnType;
import org.killbill.billing.util.config.definition.PaymentConfig;
import org.killbill.commons.locker.GlobalLocker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
@@ -44,6 +46,8 @@ import com.google.common.collect.ImmutableList;
//
public class CompletionControlOperation extends OperationControlCallback {
+ private static final Logger logger = LoggerFactory.getLogger(CompletionControlOperation.class);
+
private static final Joiner JOINER = Joiner.on(", ");
public CompletionControlOperation(final GlobalLocker locker,
@@ -84,6 +88,7 @@ public class CompletionControlOperation extends OperationControlCallback {
paymentStateContext.getCallContext());
try {
final Payment result = doCallSpecificOperationCallback();
+ logger.debug("doOperationCallback payment='{}', transaction='{}'", result, transaction);
((PaymentStateControlContext) paymentStateContext).setResult(result);
final boolean success = transaction.getTransactionStatus() == TransactionStatus.SUCCESS || transaction.getTransactionStatus() == TransactionStatus.PENDING;
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/DefaultControlCompleted.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/DefaultControlCompleted.java
index 4f67980..7c50297 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/DefaultControlCompleted.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/DefaultControlCompleted.java
@@ -34,6 +34,8 @@ import org.killbill.billing.payment.dao.PaymentTransactionModelDao;
import org.killbill.billing.payment.dao.PluginPropertySerializer;
import org.killbill.billing.payment.dao.PluginPropertySerializer.PluginPropertySerializerException;
import org.killbill.billing.payment.retry.BaseRetryService.RetryServiceScheduler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
@@ -41,6 +43,8 @@ import com.google.common.collect.Iterables;
public class DefaultControlCompleted implements EnteringStateCallback {
+ private static final Logger logger = LoggerFactory.getLogger(DefaultControlCompleted.class);
+
private final PaymentStateControlContext paymentStateContext;
private final RetryServiceScheduler retryServiceScheduler;
private final State retriedState;
@@ -61,6 +65,7 @@ public class DefaultControlCompleted implements EnteringStateCallback {
paymentStateContext.getCurrentTransaction().getId() :
null;
+ logger.debug("enteringState attemptId='{}', transactionId='{}', state='{}'", attempt.getId(), transactionId, state.getName());
// At this stage we can update the paymentAttempt state AND serialize the plugin properties. Control plugins will have had the opportunity to erase sensitive data if required.
retryablePaymentAutomatonRunner.getPaymentDao().updatePaymentAttemptWithProperties(attempt.getId(),
paymentStateContext.getPaymentMethodId(),
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonRunner.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonRunner.java
index 109d91e..3098a97 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonRunner.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonRunner.java
@@ -157,7 +157,7 @@ public class PaymentAutomatonRunner {
public PaymentAutomatonDAOHelper buildDaoHelper(final PaymentStateContext paymentStateContext,
final InternalCallContext internalCallContext) throws PaymentApiException {
- final DateTime utcNow = clock.getUTCNow();
+ final DateTime utcNow = internalCallContext.getCreatedDate();
return new PaymentAutomatonDAOHelper(paymentStateContext, utcNow, paymentDao, paymentPluginServiceRegistration, internalCallContext, eventBus, paymentSMHelper);
}
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/PaymentOperation.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/PaymentOperation.java
index cd77f0c..f6e42fa 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/PaymentOperation.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/PaymentOperation.java
@@ -123,27 +123,6 @@ public abstract class PaymentOperation extends OperationCallbackBase<PaymentTran
@Override
protected abstract PaymentTransactionInfoPlugin doCallSpecificOperationCallback() throws PaymentPluginApiException;
- protected Iterable<PaymentTransactionModelDao> getOnLeavingStateExistingTransactionsForType(final TransactionType transactionType) {
- if (paymentStateContext.getOnLeavingStateExistingTransactions() == null || paymentStateContext.getOnLeavingStateExistingTransactions().isEmpty()) {
- return ImmutableList.of();
- }
- return Iterables.filter(paymentStateContext.getOnLeavingStateExistingTransactions(), new Predicate<PaymentTransactionModelDao>() {
- @Override
- public boolean apply(final PaymentTransactionModelDao input) {
- return input.getTransactionStatus() == TransactionStatus.SUCCESS && input.getTransactionType() == transactionType;
- }
- });
- }
-
- protected BigDecimal getSumAmount(final Iterable<PaymentTransactionModelDao> transactions) {
- BigDecimal result = BigDecimal.ZERO;
- final Iterator<PaymentTransactionModelDao> iterator = transactions.iterator();
- while (iterator.hasNext()) {
- result = result.add(iterator.next().getAmount());
- }
- return result;
- }
-
private OperationResult doOperationCallbackWithDispatchAndAccountLock(String pluginName) throws OperationException {
return dispatchWithAccountLockAndTimeout(pluginName, new DispatcherCallback<PluginDispatcherReturnType<OperationResult>, OperationException>() {
@Override
@@ -181,6 +160,7 @@ public abstract class PaymentOperation extends OperationCallbackBase<PaymentTran
throw new IllegalStateException("Payment plugin returned a null result");
}
+ logger.debug("Plugin returned paymentTransactionInfoPlugin='{}'", paymentInfoPlugin);
paymentStateContext.setPaymentTransactionInfoPlugin(paymentInfoPlugin);
return PaymentTransactionInfoPluginConverter.toOperationResult(paymentStateContext.getPaymentTransactionInfoPlugin());
} else {
@@ -194,6 +174,7 @@ public abstract class PaymentOperation extends OperationCallbackBase<PaymentTran
buildPaymentPluginStatusFromOperationResult(paymentStateContext.getOverridePluginOperationResult()),
null,
null);
+ logger.debug("Plugin bypassed, paymentTransactionInfoPlugin='{}'", paymentInfoPlugin);
paymentStateContext.setPaymentTransactionInfoPlugin(paymentInfoPlugin);
return paymentStateContext.getOverridePluginOperationResult();
}
diff --git a/payment/src/main/java/org/killbill/billing/payment/dao/DefaultPaymentDao.java b/payment/src/main/java/org/killbill/billing/payment/dao/DefaultPaymentDao.java
index 7ab31a6..114744a 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dao/DefaultPaymentDao.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dao/DefaultPaymentDao.java
@@ -398,7 +398,7 @@ public class DefaultPaymentDao extends EntityDaoBase<PaymentModelDao, Payment, P
}
paymentAndTransactionModelDao.setPaymentModelDao(paymentModelDao);
- postPaymentEventFromTransaction(accountId, transactionStatus, transactionType, paymentId, transactionId, processedAmount, processedCurrency, clock.getUTCNow(), gatewayErrorCode, entitySqlDaoWrapperFactory, context);
+ postPaymentEventFromTransaction(accountId, transactionStatus, transactionType, paymentId, transactionId, processedAmount, processedCurrency, contextWithUpdatedDate.getCreatedDate(), gatewayErrorCode, entitySqlDaoWrapperFactory, context);
return paymentAndTransactionModelDao;
}
@@ -731,7 +731,7 @@ public class DefaultPaymentDao extends EntityDaoBase<PaymentModelDao, Payment, P
}
private InternalCallContext contextWithUpdatedDate(final InternalCallContext input) {
- return new InternalCallContext(input, clock.getUTCNow());
+ return new InternalCallContext(input, input.getCreatedDate());
}
@Override
diff --git a/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java b/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java
index dced1af..d87a2f0 100644
--- a/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java
+++ b/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java
@@ -297,7 +297,7 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
final List<PluginAutoPayOffModelDao> entries = controlDao.getAutoPayOffEntry(accountId);
for (final PluginAutoPayOffModelDao cur : entries) {
// TODO In theory we should pass not only PLUGIN_NAME, but also all the plugin list associated which the original call
- retryServiceScheduler.scheduleRetry(ObjectType.ACCOUNT, accountId, cur.getAttemptId(), internalCallContext.getTenantRecordId(), ImmutableList.<String>of(PLUGIN_NAME), clock.getUTCNow());
+ retryServiceScheduler.scheduleRetry(ObjectType.ACCOUNT, accountId, cur.getAttemptId(), internalCallContext.getTenantRecordId(), ImmutableList.<String>of(PLUGIN_NAME), internalCallContext.getCreatedDate());
}
controlDao.removeAutoPayOffEntry(accountId);
}
@@ -522,11 +522,11 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
final int retryCount = (attemptsInState - 1) >= 0 ? (attemptsInState - 1) : 0;
if (retryCount < retryDays.size()) {
final int retryInDays;
- final DateTime nextRetryDate = clock.getUTCNow();
+ final DateTime nextRetryDate = internalContext.getCreatedDate();
try {
retryInDays = retryDays.get(retryCount);
result = nextRetryDate.plusDays(retryInDays);
- log.debug("Next retryDate={}, retryInDays={}, retryCount={}, now={}", result, retryInDays, retryCount, clock.getUTCNow());
+ log.debug("Next retryDate={}, retryInDays={}, retryCount={}, now={}", result, retryInDays, retryCount, internalContext.getCreatedDate());
} catch (final NumberFormatException ex) {
log.error("Could not get retry day for retry count {}", retryCount);
}
@@ -546,8 +546,8 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
while (--remainingAttempts > 0) {
nbSec = nbSec * paymentConfig.getPluginFailureRetryMultiplier(internalContext);
}
- result = clock.getUTCNow().plusSeconds(nbSec);
- log.debug("Next retryDate={}, retryAttempt={}, now={}", result, retryAttempt, clock.getUTCNow());
+ result = internalContext.getCreatedDate().plusSeconds(nbSec);
+ log.debug("Next retryDate={}, retryAttempt={}, now={}", result, retryAttempt, internalContext.getCreatedDate());
}
return result;
}
@@ -674,7 +674,7 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
final PluginAutoPayOffModelDao data = new PluginAutoPayOffModelDao(paymentControlContext.getAttemptPaymentId(), paymentControlContext.getPaymentExternalKey(), paymentControlContext.getTransactionExternalKey(),
paymentControlContext.getAccountId(), PLUGIN_NAME,
paymentControlContext.getPaymentId(), paymentControlContext.getPaymentMethodId(),
- computedAmount, paymentControlContext.getCurrency(), CREATED_BY, clock.getUTCNow());
+ computedAmount, paymentControlContext.getCurrency(), CREATED_BY, paymentControlContext.getCreatedDate());
controlDao.insertAutoPayOff(data);
return true;
}
diff --git a/payment/src/main/java/org/killbill/billing/payment/provider/ExternalPaymentProviderPlugin.java b/payment/src/main/java/org/killbill/billing/payment/provider/ExternalPaymentProviderPlugin.java
index db8a69e..5871b80 100644
--- a/payment/src/main/java/org/killbill/billing/payment/provider/ExternalPaymentProviderPlugin.java
+++ b/payment/src/main/java/org/killbill/billing/payment/provider/ExternalPaymentProviderPlugin.java
@@ -65,27 +65,27 @@ public class ExternalPaymentProviderPlugin implements PaymentPluginApi {
@Override
public PaymentTransactionInfoPlugin authorizePayment(final UUID kbAccountId, final UUID kbPaymentId, final UUID kbTransactionId, final UUID kbPaymentMethodId, final BigDecimal amount, final Currency currency, final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentPluginApiException {
- return new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, TransactionType.AUTHORIZE, amount, currency, clock.getUTCNow(), clock.getUTCNow(), getPaymentPluginStatus(properties), null, null);
+ return new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, TransactionType.AUTHORIZE, amount, currency, callContext.getCreatedDate(), callContext.getCreatedDate(), getPaymentPluginStatus(properties), null, null);
}
@Override
public PaymentTransactionInfoPlugin capturePayment(final UUID kbAccountId, final UUID kbPaymentId, final UUID kbTransactionId, final UUID kbPaymentMethodId, final BigDecimal amount, final Currency currency, final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentPluginApiException {
- return new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, TransactionType.CAPTURE, amount, currency, clock.getUTCNow(), clock.getUTCNow(), getPaymentPluginStatus(properties), null, null);
+ return new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, TransactionType.CAPTURE, amount, currency, callContext.getCreatedDate(), callContext.getCreatedDate(), getPaymentPluginStatus(properties), null, null);
}
@Override
public PaymentTransactionInfoPlugin purchasePayment(final UUID kbAccountId, final UUID kbPaymentId, final UUID kbTransactionId, final UUID kbPaymentMethodId, final BigDecimal amount, final Currency currency, final Iterable<PluginProperty> properties, final CallContext context) throws PaymentPluginApiException {
- return new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, TransactionType.PURCHASE, amount, currency, clock.getUTCNow(), clock.getUTCNow(), getPaymentPluginStatus(properties), null, null);
+ return new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, TransactionType.PURCHASE, amount, currency, context.getCreatedDate(), context.getCreatedDate(), getPaymentPluginStatus(properties), null, null);
}
@Override
public PaymentTransactionInfoPlugin voidPayment(final UUID kbAccountId, final UUID kbPaymentId, final UUID kbTransactionId, final UUID kbPaymentMethodId, final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentPluginApiException {
- return new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, TransactionType.VOID, BigDecimal.ZERO, null, clock.getUTCNow(), clock.getUTCNow(), getPaymentPluginStatus(properties), null, null);
+ return new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, TransactionType.VOID, BigDecimal.ZERO, null, callContext.getCreatedDate(), callContext.getCreatedDate(), getPaymentPluginStatus(properties), null, null);
}
@Override
public PaymentTransactionInfoPlugin creditPayment(final UUID kbAccountId, final UUID kbPaymentId, final UUID kbTransactionId, final UUID kbPaymentMethodId, final BigDecimal amount, final Currency currency, final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentPluginApiException {
- return new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, TransactionType.CREDIT, amount, currency, clock.getUTCNow(), clock.getUTCNow(), getPaymentPluginStatus(properties), null, null);
+ return new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, TransactionType.CREDIT, amount, currency, callContext.getCreatedDate(), callContext.getCreatedDate(), getPaymentPluginStatus(properties), null, null);
}
@Override
@@ -100,7 +100,7 @@ public class ExternalPaymentProviderPlugin implements PaymentPluginApi {
@Override
public PaymentTransactionInfoPlugin refundPayment(final UUID kbAccountId, final UUID kbPaymentId, final UUID kbTransactionId, final UUID kbPaymentMethodId, final BigDecimal refundAmount, final Currency currency, final Iterable<PluginProperty> properties, final CallContext context) throws PaymentPluginApiException {
- return new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, TransactionType.REFUND, refundAmount, currency, clock.getUTCNow(), clock.getUTCNow(), getPaymentPluginStatus(properties), null, null);
+ return new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, TransactionType.REFUND, refundAmount, currency, context.getCreatedDate(), context.getCreatedDate(), getPaymentPluginStatus(properties), null, null);
}
@Override
diff --git a/payment/src/test/java/org/killbill/billing/payment/api/TestDefaultAdminPaymentApi.java b/payment/src/test/java/org/killbill/billing/payment/api/TestDefaultAdminPaymentApi.java
index 3e7d30a..c2d81d2 100644
--- a/payment/src/test/java/org/killbill/billing/payment/api/TestDefaultAdminPaymentApi.java
+++ b/payment/src/test/java/org/killbill/billing/payment/api/TestDefaultAdminPaymentApi.java
@@ -49,12 +49,20 @@ public class TestDefaultAdminPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
@BeforeClass(groups = "slow")
protected void beforeClass() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
super.beforeClass();
mockPaymentProviderPlugin = (MockPaymentProviderPlugin) registry.getServiceForName(MockPaymentProviderPlugin.PLUGIN_NAME);
}
@BeforeMethod(groups = "slow")
public void beforeMethod() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
super.beforeMethod();
mockPaymentProviderPlugin.clear();
@@ -104,6 +112,7 @@ public class TestDefaultAdminPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
ImmutableList.<PluginProperty>of(),
paymentOptions,
callContext);
+ Assert.assertEquals(payment.getTransactions().size(), 1);
final PaymentModelDao paymentModelDao = paymentDao.getPayment(payment.getId(), internalCallContext);
final PaymentTransactionModelDao paymentTransactionModelDao = paymentDao.getPaymentTransaction(payment.getTransactions().get(0).getId(), internalCallContext);
@@ -117,6 +126,7 @@ public class TestDefaultAdminPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
adminPaymentApi.fixPaymentTransactionState(payment, payment.getTransactions().get(0), TransactionStatus.PAYMENT_FAILURE, null, "AUTH_ERRORED", ImmutableList.<PluginProperty>of(), callContext);
+ Assert.assertEquals(paymentApi.getPayment(payment.getId(), false, false, ImmutableList.<PluginProperty>of(), callContext).getTransactions().size(), 1);
final PaymentModelDao refreshedPaymentModelDao = paymentDao.getPayment(payment.getId(), internalCallContext);
final PaymentTransactionModelDao refreshedPaymentTransactionModelDao = paymentDao.getPaymentTransaction(payment.getTransactions().get(0).getId(), internalCallContext);
Assert.assertEquals(refreshedPaymentModelDao.getStateName(), "AUTH_ERRORED");
@@ -127,6 +137,10 @@ public class TestDefaultAdminPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
Assert.assertEquals(refreshedPaymentTransactionModelDao.getGatewayErrorCode(), "");
Assert.assertEquals(refreshedPaymentTransactionModelDao.getGatewayErrorMsg(), "");
+ // Advance the clock to make sure the effective date of the new transaction is after the first one
+ clock.addDays(1);
+ assertListenerStatus();
+
// Verify subsequent payment retries work
retryService.retryPaymentTransaction(refreshedPaymentTransactionModelDao.getAttemptId(), ImmutableList.<String>of(MockPaymentControlProviderPlugin.PLUGIN_NAME), internalCallContext);
diff --git a/payment/src/test/java/org/killbill/billing/payment/glue/TestPaymentModuleWithEmbeddedDB.java b/payment/src/test/java/org/killbill/billing/payment/glue/TestPaymentModuleWithEmbeddedDB.java
index b7ba850..b08cab7 100644
--- a/payment/src/test/java/org/killbill/billing/payment/glue/TestPaymentModuleWithEmbeddedDB.java
+++ b/payment/src/test/java/org/killbill/billing/payment/glue/TestPaymentModuleWithEmbeddedDB.java
@@ -22,6 +22,7 @@ import org.killbill.billing.GuicyKillbillTestWithEmbeddedDBModule;
import org.killbill.billing.account.glue.DefaultAccountModule;
import org.killbill.billing.api.TestApiListener;
import org.killbill.billing.platform.api.KillbillConfigSource;
+import org.killbill.billing.util.glue.AuditModule;
import org.killbill.billing.util.glue.NonEntityDaoModule;
import org.killbill.clock.Clock;
@@ -36,6 +37,7 @@ public class TestPaymentModuleWithEmbeddedDB extends TestPaymentModule {
install(new GuicyKillbillTestWithEmbeddedDBModule(configSource));
install(new NonEntityDaoModule(configSource));
install(new DefaultAccountModule(configSource));
+ install(new AuditModule(configSource));
bind(TestApiListener.class).asEagerSingleton();
super.configure();
}
diff --git a/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteNoDB.java b/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteNoDB.java
index a41e524..c429872 100644
--- a/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteNoDB.java
+++ b/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteNoDB.java
@@ -115,12 +115,20 @@ public abstract class PaymentTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
@BeforeClass(groups = "fast")
protected void beforeClass() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
final Injector injector = Guice.createInjector(new TestPaymentModuleNoDB(configSource, getClock()));
injector.injectMembers(this);
}
@BeforeMethod(groups = "fast")
public void beforeMethod() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
stateMachineConfigCache.clearPaymentStateMachineConfig(PLUGIN_NAME, internalCallContext);
stateMachineConfigCache.loadDefaultPaymentStateMachineConfig(PaymentModule.DEFAULT_STATE_MACHINE_PAYMENT_XML);
@@ -132,6 +140,10 @@ public abstract class PaymentTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
@AfterMethod(groups = "fast")
public void afterMethod() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
paymentExecutors.stop();
eventBus.stop();
}
diff --git a/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteWithEmbeddedDB.java b/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteWithEmbeddedDB.java
index cf7454d..61dca40 100644
--- a/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteWithEmbeddedDB.java
+++ b/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteWithEmbeddedDB.java
@@ -119,12 +119,20 @@ public abstract class PaymentTestSuiteWithEmbeddedDB extends GuicyKillbillTestSu
@BeforeClass(groups = "slow")
protected void beforeClass() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
final Injector injector = Guice.createInjector(new TestPaymentModuleWithEmbeddedDB(configSource, getClock()));
injector.injectMembers(this);
}
@BeforeMethod(groups = "slow")
public void beforeMethod() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
super.beforeMethod();
stateMachineConfigCache.clearPaymentStateMachineConfig(PLUGIN_NAME, internalCallContext);
@@ -141,6 +149,10 @@ public abstract class PaymentTestSuiteWithEmbeddedDB extends GuicyKillbillTestSu
@AfterMethod(groups = "slow")
public void afterMethod() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
janitor.stop();
eventBus.stop();
paymentExecutors.stop();
diff --git a/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentProviderPlugin.java b/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentProviderPlugin.java
index b86f476..32bba1c 100644
--- a/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentProviderPlugin.java
+++ b/payment/src/test/java/org/killbill/billing/payment/provider/MockPaymentProviderPlugin.java
@@ -48,6 +48,8 @@ import org.killbill.billing.util.callcontext.CallContext;
import org.killbill.billing.util.callcontext.TenantContext;
import org.killbill.billing.util.entity.DefaultPagination;
import org.killbill.billing.util.entity.Pagination;
+import org.killbill.billing.util.entity.dao.DBRouterUntyped;
+import org.killbill.billing.util.entity.dao.DBRouterUntyped.THREAD_STATE;
import org.killbill.clock.Clock;
import com.google.common.base.Preconditions;
@@ -87,6 +89,8 @@ public class MockPaymentProviderPlugin implements PaymentPluginApi {
private final Clock clock;
+ private THREAD_STATE lastThreadState = null;
+
private class InternalPaymentInfo {
private BigDecimal authAmount;
@@ -272,48 +276,60 @@ public class MockPaymentProviderPlugin implements PaymentPluginApi {
}
}
+ public THREAD_STATE getLastThreadState() {
+ return lastThreadState;
+ }
+
@Override
public PaymentTransactionInfoPlugin authorizePayment(final UUID kbAccountId, final UUID kbPaymentId, final UUID kbTransactionId, final UUID kbPaymentMethodId, final BigDecimal amount, final Currency currency, final Iterable<PluginProperty> properties, final CallContext context)
throws PaymentPluginApiException {
+ updateLastThreadState();
return getPaymentTransactionInfoPluginResult(kbPaymentId, kbTransactionId, TransactionType.AUTHORIZE, amount, currency, properties);
}
@Override
public PaymentTransactionInfoPlugin capturePayment(final UUID kbAccountId, final UUID kbPaymentId, final UUID kbTransactionId, final UUID kbPaymentMethodId, final BigDecimal amount, final Currency currency, final Iterable<PluginProperty> properties, final CallContext context)
throws PaymentPluginApiException {
+ updateLastThreadState();
return getPaymentTransactionInfoPluginResult(kbPaymentId, kbTransactionId, TransactionType.CAPTURE, amount, currency, properties);
}
@Override
public PaymentTransactionInfoPlugin purchasePayment(final UUID kbAccountId, final UUID kbPaymentId, final UUID kbTransactionId, final UUID kbPaymentMethodId, final BigDecimal amount, final Currency currency, final Iterable<PluginProperty> properties, final CallContext context) throws PaymentPluginApiException {
+ updateLastThreadState();
return getPaymentTransactionInfoPluginResult(kbPaymentId, kbTransactionId, TransactionType.PURCHASE, amount, currency, properties);
}
@Override
public PaymentTransactionInfoPlugin voidPayment(final UUID kbAccountId, final UUID kbPaymentId, final UUID kbTransactionId, final UUID kbPaymentMethodId, final Iterable<PluginProperty> properties, final CallContext context)
throws PaymentPluginApiException {
+ updateLastThreadState();
return getPaymentTransactionInfoPluginResult(kbPaymentId, kbTransactionId, TransactionType.VOID, null, null, properties);
}
@Override
public PaymentTransactionInfoPlugin creditPayment(final UUID kbAccountId, final UUID kbPaymentId, final UUID kbTransactionId, final UUID kbPaymentMethodId, final BigDecimal amount, final Currency currency, final Iterable<PluginProperty> properties, final CallContext context)
throws PaymentPluginApiException {
+ updateLastThreadState();
return getPaymentTransactionInfoPluginResult(kbPaymentId, kbTransactionId, TransactionType.CREDIT, amount, currency, properties);
}
@Override
public List<PaymentTransactionInfoPlugin> getPaymentInfo(final UUID kbAccountId, final UUID kbPaymentId, final Iterable<PluginProperty> properties, final TenantContext context) throws PaymentPluginApiException {
+ updateLastThreadState();
final List<PaymentTransactionInfoPlugin> result = paymentTransactions.get(kbPaymentId.toString());
return result != null ? result : ImmutableList.<PaymentTransactionInfoPlugin>of();
}
@Override
public Pagination<PaymentTransactionInfoPlugin> searchPayments(final String searchKey, final Long offset, final Long limit, final Iterable<PluginProperty> properties, final TenantContext tenantContext) throws PaymentPluginApiException {
+ updateLastThreadState();
throw new IllegalStateException("Not implemented");
}
@Override
public void addPaymentMethod(final UUID kbAccountId, final UUID kbPaymentMethodId, final PaymentMethodPlugin paymentMethodProps, final boolean setDefault, final Iterable<PluginProperty> properties, final CallContext context) throws PaymentPluginApiException {
+ updateLastThreadState();
// externalPaymentMethodId is set to a random value
final PaymentMethodPlugin realWithID = new TestPaymentMethodPlugin(kbPaymentMethodId, paymentMethodProps, UUID.randomUUID().toString());
paymentMethods.put(kbPaymentMethodId.toString(), realWithID);
@@ -324,26 +340,31 @@ public class MockPaymentProviderPlugin implements PaymentPluginApi {
@Override
public void deletePaymentMethod(final UUID kbAccountId, final UUID kbPaymentMethodId, final Iterable<PluginProperty> properties, final CallContext context) throws PaymentPluginApiException {
+ updateLastThreadState();
paymentMethods.remove(kbPaymentMethodId.toString());
paymentMethodsInfo.remove(kbPaymentMethodId.toString());
}
@Override
public PaymentMethodPlugin getPaymentMethodDetail(final UUID kbAccountId, final UUID kbPaymentMethodId, final Iterable<PluginProperty> properties, final TenantContext context) throws PaymentPluginApiException {
+ updateLastThreadState();
return paymentMethods.get(kbPaymentMethodId.toString());
}
@Override
public void setDefaultPaymentMethod(final UUID kbAccountId, final UUID kbPaymentMethodId, final Iterable<PluginProperty> properties, final CallContext context) throws PaymentPluginApiException {
+ updateLastThreadState();
}
@Override
public List<PaymentMethodInfoPlugin> getPaymentMethods(final UUID kbAccountId, final boolean refreshFromGateway, final Iterable<PluginProperty> properties, final CallContext context) {
+ updateLastThreadState();
return ImmutableList.<PaymentMethodInfoPlugin>copyOf(paymentMethodsInfo.values());
}
@Override
public Pagination<PaymentMethodPlugin> searchPaymentMethods(final String searchKey, final Long offset, final Long limit, final Iterable<PluginProperty> properties, final TenantContext tenantContext) throws PaymentPluginApiException {
+ updateLastThreadState();
final ImmutableList<PaymentMethodPlugin> results = ImmutableList.<PaymentMethodPlugin>copyOf(Iterables.<PaymentMethodPlugin>filter(paymentMethods.values(), new Predicate<PaymentMethodPlugin>() {
@Override
public boolean apply(final PaymentMethodPlugin input) {
@@ -362,6 +383,7 @@ public class MockPaymentProviderPlugin implements PaymentPluginApi {
@Override
public void resetPaymentMethods(final UUID kbAccountId, final List<PaymentMethodInfoPlugin> input, final Iterable<PluginProperty> properties, final CallContext callContext) {
+ updateLastThreadState();
paymentMethodsInfo.clear();
if (input != null) {
for (final PaymentMethodInfoPlugin cur : input) {
@@ -372,16 +394,19 @@ public class MockPaymentProviderPlugin implements PaymentPluginApi {
@Override
public HostedPaymentPageFormDescriptor buildFormDescriptor(final UUID kbAccountId, final Iterable<PluginProperty> customFields, final Iterable<PluginProperty> properties, final CallContext callContext) {
+ updateLastThreadState();
return new DefaultNoOpHostedPaymentPageFormDescriptor(kbAccountId);
}
@Override
public GatewayNotification processNotification(final String notification, final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentPluginApiException {
+ updateLastThreadState();
return new DefaultNoOpGatewayNotification();
}
@Override
public PaymentTransactionInfoPlugin refundPayment(final UUID kbAccountId, final UUID kbPaymentId, final UUID kbTransactionId, final UUID kbPaymentMethodId, final BigDecimal refundAmount, final Currency currency, final Iterable<PluginProperty> properties, final CallContext context) throws PaymentPluginApiException {
+ updateLastThreadState();
final InternalPaymentInfo info = payments.get(kbPaymentId.toString());
if (info == null) {
@@ -479,4 +504,8 @@ public class MockPaymentProviderPlugin implements PaymentPluginApi {
return result;
}
+
+ private void updateLastThreadState() {
+ lastThreadState = DBRouterUntyped.getCurrentState();
+ }
}
diff --git a/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java b/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java
index ecaecf1..1f2e03a 100644
--- a/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java
+++ b/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java
@@ -56,7 +56,10 @@ import org.killbill.billing.payment.provider.DefaultNoOpPaymentInfoPlugin;
import org.killbill.billing.payment.provider.MockPaymentProviderPlugin;
import org.killbill.billing.platform.api.KillbillConfigSource;
import org.killbill.billing.util.callcontext.InternalCallContextFactory;
+import org.killbill.billing.util.entity.dao.DBRouterUntyped;
+import org.killbill.billing.util.entity.dao.DBRouterUntyped.THREAD_STATE;
import org.killbill.bus.api.PersistentBus.EventBusException;
+import org.killbill.commons.profiling.Profiling.WithProfilingCallback;
import org.killbill.notificationq.api.NotificationEvent;
import org.killbill.notificationq.api.NotificationEventWithMetadata;
import org.killbill.notificationq.api.NotificationQueueService;
@@ -75,8 +78,8 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.inject.Inject;
-import static org.awaitility.Awaitility.await;
import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.awaitility.Awaitility.await;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.fail;
@@ -472,6 +475,45 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
});
}
+ @Test(groups = "slow")
+ public void testDBRouterThreadState() throws Throwable {
+ final Payment payment = (Payment) DBRouterUntyped.withRODBIAllowed(true,
+ new WithProfilingCallback<Object, Throwable>() {
+ @Override
+ public Payment execute() throws Throwable {
+ // Shouldn't happen in practice, but it's just to verify the behavior
+ assertEquals(DBRouterUntyped.getCurrentState(), THREAD_STATE.RO_ALLOWED);
+
+ final BigDecimal requestedAmount = BigDecimal.TEN;
+ testListener.pushExpectedEvent(NextEvent.PAYMENT);
+ final Payment payment = paymentApi.createAuthorization(account, account.getPaymentMethodId(), null, requestedAmount, account.getCurrency(), null, UUID.randomUUID().toString(),
+ UUID.randomUUID().toString(), ImmutableList.<PluginProperty>of(), callContext);
+ testListener.assertListenerStatus();
+
+ // Thread switch, RW by default
+ assertEquals(mockPaymentProviderPlugin.getLastThreadState(), THREAD_STATE.RW_ONLY);
+ // Switched to RW, because of RW DAO call
+ assertEquals(DBRouterUntyped.getCurrentState(), THREAD_STATE.RW_ONLY);
+ return payment;
+ }
+ });
+
+ DBRouterUntyped.withRODBIAllowed(true,
+ new WithProfilingCallback<Object, Throwable>() {
+ @Override
+ public Object execute() throws Throwable {
+ assertEquals(DBRouterUntyped.getCurrentState(), THREAD_STATE.RO_ALLOWED);
+
+ final Payment retrievedPayment2 = paymentApi.getPayment(payment.getId(), true, false, ImmutableList.<PluginProperty>of(), callContext);
+ Assert.assertEquals(retrievedPayment2.getTransactions().get(0).getTransactionStatus(), TransactionStatus.SUCCESS);
+
+ // No thread switch, RO as well
+ assertEquals(mockPaymentProviderPlugin.getLastThreadState(), THREAD_STATE.RO_ALLOWED);
+ assertEquals(DBRouterUntyped.getCurrentState(), THREAD_STATE.RO_ALLOWED);
+ return null;
+ }
+ });
+ }
private List<PluginProperty> createPropertiesForInvoice(final Invoice invoice) {
final List<PluginProperty> result = new ArrayList<PluginProperty>();
pom.xml 4(+2 -2)
diff --git a/pom.xml b/pom.xml
index b059775..26a2b46 100644
--- a/pom.xml
+++ b/pom.xml
@@ -21,10 +21,10 @@
<parent>
<artifactId>killbill-oss-parent</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.141.59</version>
+ <version>0.141.61</version>
</parent>
<artifactId>killbill</artifactId>
- <version>0.19.11-SNAPSHOT</version>
+ <version>0.19.13-SNAPSHOT</version>
<packaging>pom</packaging>
<name>killbill</name>
<description>Library for managing recurring subscriptions and the associated billing</description>
profiles/killbill/pom.xml 2(+1 -1)
diff --git a/profiles/killbill/pom.xml b/profiles/killbill/pom.xml
index 89381c8..d0a9c8c 100644
--- a/profiles/killbill/pom.xml
+++ b/profiles/killbill/pom.xml
@@ -21,7 +21,7 @@
<parent>
<artifactId>killbill-profiles</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.19.11-SNAPSHOT</version>
+ <version>0.19.13-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-profiles-killbill</artifactId>
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/modules/JaxRSAopModule.java b/profiles/killbill/src/main/java/org/killbill/billing/server/modules/JaxRSAopModule.java
new file mode 100644
index 0000000..30becf9
--- /dev/null
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/modules/JaxRSAopModule.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.server.modules;
+
+import java.lang.reflect.Method;
+
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.HEAD;
+import javax.ws.rs.OPTIONS;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+import org.killbill.billing.jaxrs.resources.JaxrsResource;
+import org.killbill.billing.util.entity.dao.DBRouterUntyped;
+import org.killbill.billing.util.glue.KillbillApiAopModule;
+import org.killbill.commons.profiling.Profiling.WithProfilingCallback;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.matcher.Matcher;
+import com.google.inject.matcher.Matchers;
+
+public class JaxRSAopModule extends AbstractModule {
+
+ private static final Logger logger = LoggerFactory.getLogger(KillbillApiAopModule.class);
+
+ private static final Matcher<Method> API_RESOURCE_METHOD_MATCHER = new Matcher<Method>() {
+ @Override
+ public boolean matches(final Method method) {
+ return !method.isSynthetic() &&
+ (
+ method.getAnnotation(DELETE.class) != null ||
+ method.getAnnotation(GET.class) != null ||
+ method.getAnnotation(HEAD.class) != null ||
+ method.getAnnotation(OPTIONS.class) != null ||
+ method.getAnnotation(POST.class) != null ||
+ method.getAnnotation(PUT.class) != null
+ );
+ }
+
+ @Override
+ public Matcher<Method> and(final Matcher<? super Method> other) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Matcher<Method> or(final Matcher<? super Method> other) {
+ throw new UnsupportedOperationException();
+ }
+ };
+
+ @Override
+ protected void configure() {
+ bindInterceptor(Matchers.subclassesOf(JaxrsResource.class),
+ API_RESOURCE_METHOD_MATCHER,
+ new JaxRsMethodInterceptor());
+ }
+
+ public static class JaxRsMethodInterceptor implements MethodInterceptor {
+
+ @Override
+ public Object invoke(final MethodInvocation invocation) throws Throwable {
+ return DBRouterUntyped.withRODBIAllowed(isRODBIAllowed(invocation),
+ new WithProfilingCallback<Object, Throwable>() {
+ @Override
+ public Object execute() throws Throwable {
+ logger.debug("Entering JAX-RS call {}, arguments: {}", invocation.getMethod(), invocation.getArguments());
+ final Object proceed = invocation.proceed();
+ logger.debug("Exiting JXA-RS call {}, returning: {}", invocation.getMethod(), proceed);
+ return proceed;
+ }
+ });
+ }
+
+ private boolean isRODBIAllowed(final MethodInvocation invocation) {
+ return invocation.getMethod().getAnnotation(GET.class) != null;
+ }
+ }
+}
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillbillServerModule.java b/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillbillServerModule.java
index 6ffbc2a..5d43a8f 100644
--- a/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillbillServerModule.java
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillbillServerModule.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2017 Groupon, Inc
- * Copyright 2014-2017 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
*
* The Billing Project 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
@@ -179,6 +179,7 @@ public class KillbillServerModule extends KillbillPlatformModule {
install(new GlobalLockerModule(configSource));
install(new KillBillShiroAopModule());
install(new KillbillApiAopModule());
+ install(new JaxRSAopModule());
install(new KillBillShiroWebModule(servletContext, skifeConfigSource));
install(new NonEntityDaoModule(configSource));
install(new PaymentModule(configSource));
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/resources/TestDBRouterResource.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/resources/TestDBRouterResource.java
new file mode 100644
index 0000000..254d526
--- /dev/null
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/resources/TestDBRouterResource.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.jaxrs.resources;
+
+import java.util.UUID;
+
+import javax.inject.Inject;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Response;
+
+import org.joda.time.DateTimeZone;
+import org.killbill.billing.GuicyKillbillTestSuite;
+import org.killbill.billing.beatrix.integration.db.TestDBRouterAPI;
+import org.killbill.billing.callcontext.MutableCallContext;
+import org.killbill.billing.callcontext.MutableInternalCallContext;
+import org.killbill.billing.util.callcontext.CallOrigin;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
+import org.killbill.billing.util.callcontext.UserType;
+
+@Path("/testDbResource")
+public class TestDBRouterResource implements JaxrsResource {
+
+ private final MutableInternalCallContext internalCallContext = new MutableInternalCallContext(InternalCallContextFactory.INTERNAL_TENANT_RECORD_ID,
+ 1687L,
+ DateTimeZone.UTC,
+ GuicyKillbillTestSuite.getClock().getUTCNow(),
+ UUID.randomUUID(),
+ UUID.randomUUID().toString(),
+ CallOrigin.TEST,
+ UserType.TEST,
+ "Testing",
+ "This is a test",
+ GuicyKillbillTestSuite.getClock().getUTCNow(),
+ GuicyKillbillTestSuite.getClock().getUTCNow());
+
+ private final MutableCallContext callContext = new MutableCallContext(internalCallContext);
+
+ private final TestDBRouterAPI testDBRouterAPI;
+
+ @Inject
+ public TestDBRouterResource(final TestDBRouterAPI testDBRouterAPI) {
+ this.testDBRouterAPI = testDBRouterAPI;
+ }
+
+ @POST
+ public Response doChainedRWROCalls() {
+ testDBRouterAPI.reset();
+ testDBRouterAPI.doRWCall(callContext);
+ testDBRouterAPI.doROCall(callContext);
+ return Response.ok().build();
+ }
+
+ @GET
+ public Response doChainedROROCalls() {
+ testDBRouterAPI.reset();
+ testDBRouterAPI.doROCall(callContext);
+ testDBRouterAPI.doROCall(callContext);
+ return Response.ok().build();
+ }
+
+ @GET
+ @Path("/chained")
+ public Response doChainedRORWROCalls() {
+ testDBRouterAPI.reset();
+ testDBRouterAPI.doROCall(callContext);
+ // Naughty: @GET method doing a RW call... Verify the underlying code will detect it and mark the thread as dirty
+ testDBRouterAPI.doRWCall(callContext);
+ testDBRouterAPI.doROCall(callContext);
+ return Response.ok().build();
+ }
+}
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestDBRouterResources.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestDBRouterResources.java
new file mode 100644
index 0000000..aeabe3d
--- /dev/null
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestDBRouterResources.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.jaxrs;
+
+import javax.inject.Inject;
+
+import org.killbill.billing.beatrix.integration.db.TestDBRouterAPI;
+import org.killbill.billing.jaxrs.resources.TestDBRouterResource;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+public class TestDBRouterResources extends TestJaxrsBase {
+
+ @Inject
+ private TestDBRouterAPI testDBRouterAPI;
+
+ @Inject
+ private TestDBRouterResource testDBRouterResource;
+
+ @Test(groups = "slow")
+ public void testJaxRSAoPRouting() throws Exception {
+ testDBRouterResource.doChainedROROCalls();
+ assertNbCalls(0, 2);
+
+ testDBRouterResource.doChainedRWROCalls();
+ assertNbCalls(2, 0);
+
+ testDBRouterResource.doChainedRORWROCalls();
+ assertNbCalls(2, 1);
+ }
+
+ private void assertNbCalls(final int expectedNbRWCalls, final int expectedNbROCalls) {
+ assertEquals(testDBRouterAPI.getNbRWCalls(), expectedNbRWCalls);
+ assertEquals(testDBRouterAPI.getNbRoCalls(), expectedNbROCalls);
+ }
+}
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestEntitlement.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestEntitlement.java
index f77d91e..99df7c7 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestEntitlement.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestEntitlement.java
@@ -73,7 +73,7 @@ public class TestEntitlement extends TestJaxrsBase {
final BillingPeriod term = BillingPeriod.MONTHLY;
final Subscription entitlementJson = createSubscription(accountJson.getAccountId(), "99999", productName,
- ProductCategory.BASE, term, true);
+ ProductCategory.BASE, term, true);
// Retrieves with GET
Subscription objFromJson = subscriptionApi.getSubscription(entitlementJson.getSubscriptionId(), requestOptions);
@@ -127,7 +127,7 @@ public class TestEntitlement extends TestJaxrsBase {
final BillingPeriod term = BillingPeriod.MONTHLY;
final Subscription entitlementJson = createSubscription(accountJson.getAccountId(), "99999", productName,
- ProductCategory.BASE, term, true);
+ ProductCategory.BASE, term, true);
// Retrieves with GET
Subscription objFromJson = subscriptionApi.getSubscription(entitlementJson.getSubscriptionId(), requestOptions);
@@ -153,7 +153,7 @@ public class TestEntitlement extends TestJaxrsBase {
// Cancel EOT
subscriptionApi.cancelSubscriptionPlan(entitlementJson.getSubscriptionId(), null, EntitlementActionPolicy.END_OF_TERM,
- BillingActionPolicy.END_OF_TERM, NULL_PLUGIN_PROPERTIES, requestOptions);
+ BillingActionPolicy.END_OF_TERM, NULL_PLUGIN_PROPERTIES, requestOptions);
// Retrieves to check EndDate
objFromJson = subscriptionApi.getSubscription(entitlementJson.getSubscriptionId(), requestOptions);
@@ -209,7 +209,7 @@ public class TestEntitlement extends TestJaxrsBase {
final BillingPeriod term = BillingPeriod.ANNUAL;
final Subscription subscriptionJson = createSubscription(accountJson.getAccountId(), "99999", productName,
- ProductCategory.BASE, term, true);
+ ProductCategory.BASE, term, true);
// Retrieves with GET
Subscription objFromJson = subscriptionApi.getSubscription(subscriptionJson.getSubscriptionId(), requestOptions);
@@ -344,7 +344,6 @@ public class TestEntitlement extends TestJaxrsBase {
final Subscription addOn1 = new Subscription();
addOn1.setAccountId(accountJson.getAccountId());
- addOn1.setExternalKey("");
addOn1.setProductName("Telescopic-Scope");
addOn1.setProductCategory(ProductCategory.ADD_ON);
addOn1.setBillingPeriod(BillingPeriod.MONTHLY);
@@ -352,7 +351,6 @@ public class TestEntitlement extends TestJaxrsBase {
final Subscription addOn2 = new Subscription();
addOn2.setAccountId(accountJson.getAccountId());
- addOn2.setExternalKey("");
addOn2.setProductName("Laser-Scope");
addOn2.setProductCategory(ProductCategory.ADD_ON);
addOn2.setBillingPeriod(BillingPeriod.MONTHLY);
@@ -469,7 +467,7 @@ public class TestEntitlement extends TestJaxrsBase {
}
@Test(groups = "slow", description = "Create a bulk of base entitlements and addOns under the same transaction",
- expectedExceptions = KillBillClientException.class, expectedExceptionsMessageRegExp = "Missing Base Subscription for bundle 12345")
+ expectedExceptions = KillBillClientException.class, expectedExceptionsMessageRegExp = "SubscriptionJson productName needs to be set when no planName is specified")
public void testcreateSubscriptionsWithoutBase() throws Exception {
final DateTime initialDate = new DateTime(2012, 4, 25, 0, 3, 42, 0);
clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
@@ -520,13 +518,9 @@ public class TestEntitlement extends TestJaxrsBase {
input.setPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
final Subscription subscription = subscriptionApi.createSubscription(input, null, null, null, null, null, true, DEFAULT_WAIT_COMPLETION_TIMEOUT_SEC, NULL_PLUGIN_PROPERTIES, requestOptions);
- final Subscription base = new Subscription();
- base.setAccountId(accountJson.getAccountId());
- base.setProductCategory(ProductCategory.BASE);
- base.setExternalKey("foobarxyz");
-
final Subscription addOn1 = new Subscription();
addOn1.setAccountId(accountJson.getAccountId());
+ addOn1.setBundleId(subscription.getBundleId());
addOn1.setProductName("Telescopic-Scope");
addOn1.setProductCategory(ProductCategory.ADD_ON);
addOn1.setBillingPeriod(BillingPeriod.MONTHLY);
@@ -534,13 +528,13 @@ public class TestEntitlement extends TestJaxrsBase {
final Subscription addOn2 = new Subscription();
addOn2.setAccountId(accountJson.getAccountId());
+ addOn2.setBundleId(subscription.getBundleId());
addOn2.setProductName("Laser-Scope");
addOn2.setProductCategory(ProductCategory.ADD_ON);
addOn2.setBillingPeriod(BillingPeriod.MONTHLY);
addOn2.setPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
final List<Subscription> subscriptions = new ArrayList<Subscription>();
- subscriptions.add(base);
subscriptions.add(addOn1);
subscriptions.add(addOn2);
@@ -633,7 +627,7 @@ public class TestEntitlement extends TestJaxrsBase {
final BillingPeriod term = BillingPeriod.MONTHLY;
final Subscription entitlementJson = createSubscription(accountJson.getAccountId(), "99999", productName,
- ProductCategory.BASE, term, true);
+ ProductCategory.BASE, term, true);
Assert.assertEquals(entitlementJson.getBillCycleDayLocal(), new Integer(25));
@@ -696,7 +690,7 @@ public class TestEntitlement extends TestJaxrsBase {
final BillingPeriod term = BillingPeriod.MONTHLY;
final Subscription entitlementJson = createSubscription(accountJson.getAccountId(), "99999", productName,
- ProductCategory.BASE, term, true);
+ ProductCategory.BASE, term, true);
// Change plan in the future
final String newProductName = "Assault-Rifle";
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestJaxrsBase.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestJaxrsBase.java
index 319a0f9..0fc073a 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestJaxrsBase.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestJaxrsBase.java
@@ -30,7 +30,6 @@ import java.util.List;
import java.util.Map;
import java.util.UUID;
-import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Named;
import javax.servlet.Servlet;
@@ -42,6 +41,7 @@ import org.eclipse.jetty.servlet.FilterHolder;
import org.joda.time.LocalDate;
import org.killbill.billing.GuicyKillbillTestWithEmbeddedDBModule;
import org.killbill.billing.api.TestApiListener;
+import org.killbill.billing.beatrix.integration.db.TestDBRouterAPI;
import org.killbill.billing.client.KillBillClientException;
import org.killbill.billing.client.KillBillHttpClient;
import org.killbill.billing.client.api.gen.AccountApi;
@@ -72,6 +72,7 @@ import org.killbill.billing.client.model.gen.Payment;
import org.killbill.billing.client.model.gen.PaymentTransaction;
import org.killbill.billing.client.model.gen.Tenant;
import org.killbill.billing.invoice.glue.DefaultInvoiceModule;
+import org.killbill.billing.jaxrs.resources.TestDBRouterResource;
import org.killbill.billing.jetty.HttpServer;
import org.killbill.billing.jetty.HttpServerConfig;
import org.killbill.billing.lifecycle.glue.BusModule;
@@ -106,6 +107,7 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.io.Files;
import com.google.common.io.Resources;
+import com.google.inject.Binder;
import com.google.inject.Module;
import com.google.inject.util.Modules;
@@ -177,7 +179,14 @@ public class TestJaxrsBase extends KillbillClient {
protected Module getModule(final ServletContext servletContext) {
return Modules.override(new KillbillServerModule(servletContext, serverConfig, configSource)).with(new GuicyKillbillTestWithEmbeddedDBModule(configSource),
new InvoiceModuleWithMockSender(configSource),
- new PaymentMockModule(configSource));
+ new PaymentMockModule(configSource),
+ new Module() {
+ @Override
+ public void configure(final Binder binder) {
+ binder.bind(TestDBRouterAPI.class).asEagerSingleton();
+ binder.bind(TestDBRouterResource.class).asEagerSingleton();
+ }
+ });
}
}
@@ -294,6 +303,10 @@ public class TestJaxrsBase extends KillbillClient {
@BeforeClass(groups = "slow")
public void beforeClass() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
// TODO PIERRE Unclear why both are needed in beforeClass and beforeSuite
if (config == null) {
config = new ConfigurationObjectFactory(System.getProperties()).build(HttpServerConfig.class);
@@ -306,6 +319,10 @@ public class TestJaxrsBase extends KillbillClient {
@BeforeSuite(groups = "slow")
public void beforeSuite() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
super.beforeSuite();
if (config == null) {
@@ -323,7 +340,6 @@ public class TestJaxrsBase extends KillbillClient {
server.start();
}
-
protected Iterable<EventListener> getListeners() {
return new Iterable<EventListener>() {
@Override
@@ -357,7 +373,6 @@ public class TestJaxrsBase extends KillbillClient {
return filterTransactions(transform, transactionType);
}
-
protected static List<PaymentTransaction> getPaymentTransactions(final List<Payment> payments, final TransactionType transactionType) {
final Iterable<PaymentTransaction> transform = Iterables.concat(Iterables.transform(payments, new Function<Payment, Iterable<PaymentTransaction>>() {
@Override
@@ -368,7 +383,6 @@ public class TestJaxrsBase extends KillbillClient {
return filterTransactions(transform, transactionType);
}
-
protected static List<PaymentTransaction> filterTransactions(final Iterable<PaymentTransaction> paymentTransaction, final TransactionType transactionType) {
return ImmutableList.copyOf(Iterables.filter(paymentTransaction, new Predicate<PaymentTransaction>() {
@Override
@@ -384,7 +398,6 @@ public class TestJaxrsBase extends KillbillClient {
return fetch ? catalogApi.getCatalogXml(null, requestOptions) : null;
}
-
protected void uploadTenantOverdueConfig(final String overdue) throws IOException, KillBillClientException {
final String body = getResourceBodyString(overdue);
overdueApi.uploadOverdueConfigXml(body, requestOptions);
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/server/log/ServerTestSuiteNoDB.java b/profiles/killbill/src/test/java/org/killbill/billing/server/log/ServerTestSuiteNoDB.java
index f0e5e86..9d12db6 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/server/log/ServerTestSuiteNoDB.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/server/log/ServerTestSuiteNoDB.java
@@ -28,6 +28,10 @@ public abstract class ServerTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
@BeforeClass(groups = "fast")
protected void beforeClass() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
final Injector injector = Guice.createInjector(new TestServerModuleNoDB(configSource));
injector.injectMembers(this);
}
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/server/security/TestKillbillJdbcTenantRealm.java b/profiles/killbill/src/test/java/org/killbill/billing/server/security/TestKillbillJdbcTenantRealm.java
index 39b1b13..292cd25 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/server/security/TestKillbillJdbcTenantRealm.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/server/security/TestKillbillJdbcTenantRealm.java
@@ -51,7 +51,7 @@ public class TestKillbillJdbcTenantRealm extends TestJaxrsBase {
super.beforeMethod();
// Create the tenant
- final DefaultTenantDao tenantDao = new DefaultTenantDao(dbi, roDbi, clock, cacheControllerDispatcher, new DefaultNonEntityDao(dbi), Mockito.mock(InternalCallContextFactory.class), securityConfig);
+ final DefaultTenantDao tenantDao = new DefaultTenantDao(dbi, roDbi, clock, cacheControllerDispatcher, new DefaultNonEntityDao(dbi, roDbi), Mockito.mock(InternalCallContextFactory.class), securityConfig);
tenant = new DefaultTenant(UUID.randomUUID(), null, null, UUID.randomUUID().toString(),
UUID.randomUUID().toString(), UUID.randomUUID().toString());
tenantDao.create(new TenantModelDao(tenant), internalCallContext);
profiles/killpay/pom.xml 2(+1 -1)
diff --git a/profiles/killpay/pom.xml b/profiles/killpay/pom.xml
index 7bd7494..048b66c 100644
--- a/profiles/killpay/pom.xml
+++ b/profiles/killpay/pom.xml
@@ -20,7 +20,7 @@
<parent>
<artifactId>killbill-profiles</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.19.11-SNAPSHOT</version>
+ <version>0.19.13-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-profiles-killpay</artifactId>
diff --git a/profiles/killpay/src/main/java/org/killbill/billing/server/modules/KillpayServerModule.java b/profiles/killpay/src/main/java/org/killbill/billing/server/modules/KillpayServerModule.java
index b4b8f6d..38f2e8e 100644
--- a/profiles/killpay/src/main/java/org/killbill/billing/server/modules/KillpayServerModule.java
+++ b/profiles/killpay/src/main/java/org/killbill/billing/server/modules/KillpayServerModule.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
*
* The Billing Project 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
@@ -91,6 +91,7 @@ public class KillpayServerModule extends KillbillServerModule {
install(new GlobalLockerModule(configSource));
install(new KillBillShiroAopModule());
install(new KillbillApiAopModule());
+ install(new JaxRSAopModule());
install(new KillBillShiroWebModule(servletContext, skifeConfigSource));
install(new NonEntityDaoModule(configSource));
install(new PaymentModule(configSource));
profiles/pom.xml 2(+1 -1)
diff --git a/profiles/pom.xml b/profiles/pom.xml
index afe81c3..b8b4fb4 100644
--- a/profiles/pom.xml
+++ b/profiles/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.19.11-SNAPSHOT</version>
+ <version>0.19.13-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-profiles</artifactId>
subscription/pom.xml 2(+1 -1)
diff --git a/subscription/pom.xml b/subscription/pom.xml
index a2d2459..1e4001f 100644
--- a/subscription/pom.xml
+++ b/subscription/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.19.11-SNAPSHOT</version>
+ <version>0.19.13-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-subscription</artifactId>
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java b/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
index 9794ac6..d5c4bb6 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
@@ -148,7 +148,7 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
}
private List<SubscriptionSpecifier> verifyAndBuildSubscriptionSpecifiers(final SubscriptionBaseBundle bundle,
- @Nullable final EntitlementSpecifier baseOrFirstStandalonePlanSpecifier,
+ final boolean hasBaseOrStandalonePlanSpecifier,
final Iterable<EntitlementSpecifier> entitlements,
final boolean isMigrated,
final InternalCallContext context,
@@ -188,7 +188,7 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
}
final DateTime bundleStartDate;
- if (baseOrFirstStandalonePlanSpecifier != null) {
+ if (hasBaseOrStandalonePlanSpecifier) {
bundleStartDate = effectiveDate;
} else {
final SubscriptionBase baseSubscription = dao.getBaseSubscription(bundle.getId(), catalog, context);
@@ -215,13 +215,13 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
return subscriptions;
}
- private EntitlementSpecifier sanityAndReorderBPOrStandaloneSpecFirst(final Catalog catalog,
- final SubscriptionBaseWithAddOnsSpecifier subscriptionBaseWithAddOnsSpecifier,
- final DateTime effectiveDate,
- final Collection<EntitlementSpecifier> outputEntitlementSpecifier) throws SubscriptionBaseApiException {
+ private boolean sanityAndReorderBPOrStandaloneSpecFirst(final Catalog catalog,
+ final SubscriptionBaseWithAddOnsSpecifier subscriptionBaseWithAddOnsSpecifier,
+ final DateTime effectiveDate,
+ final Collection<EntitlementSpecifier> outputEntitlementSpecifier) throws SubscriptionBaseApiException {
EntitlementSpecifier basePlanSpecifier = null;
final Collection<EntitlementSpecifier> addOnSpecifiers = new ArrayList<EntitlementSpecifier>();
- final List<EntitlementSpecifier> standaloneSpecifiers = new ArrayList<EntitlementSpecifier>();
+ final Collection<EntitlementSpecifier> standaloneSpecifiers = new ArrayList<EntitlementSpecifier>();
try {
for (final EntitlementSpecifier cur : subscriptionBaseWithAddOnsSpecifier.getEntitlementSpecifiers()) {
final boolean isBase = isBaseSpecifier(catalog, effectiveDate, cur);
@@ -252,10 +252,10 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
}
if (standaloneSpecifiers.isEmpty()) {
- return basePlanSpecifier;
+ return basePlanSpecifier != null;
} else {
outputEntitlementSpecifier.addAll(standaloneSpecifiers);
- return standaloneSpecifiers.get(0);
+ return true;
}
}
@@ -275,15 +275,15 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
final Catalog catalog = catalogInternalApi.getFullCatalog(true, true, context);
final CallContext callContext = internalCallContextFactory.createCallContext(context);
final UUID accountId = callContext.getAccountId();
- final DateTime now = clock.getUTCNow();
final Collection<SubscriptionAndAddOnsSpecifier> subscriptionAndAddOns = new ArrayList<SubscriptionAndAddOnsSpecifier>();
for (final SubscriptionBaseWithAddOnsSpecifier subscriptionBaseWithAddOnsSpecifier : subscriptionWithAddOnsSpecifiers) {
- final DateTime billingRequestedDateRaw = subscriptionBaseWithAddOnsSpecifier.getBillingEffectiveDate() != null ? subscriptionBaseWithAddOnsSpecifier.getBillingEffectiveDate() : now;
+ final DateTime billingRequestedDateRaw = (subscriptionBaseWithAddOnsSpecifier.getBillingEffectiveDate() != null) ?
+ context.toUTCDateTime(subscriptionBaseWithAddOnsSpecifier.getBillingEffectiveDate()) : context.getCreatedDate();
final Collection<EntitlementSpecifier> reorderedSpecifiers = new ArrayList<EntitlementSpecifier>();
// Note: billingRequestedDateRaw might not be accurate here (add-on with a too early date passed)?
- final EntitlementSpecifier baseOrFirstStandalonePlanSpecifier = sanityAndReorderBPOrStandaloneSpecFirst(catalog, subscriptionBaseWithAddOnsSpecifier, billingRequestedDateRaw, reorderedSpecifiers);
+ final boolean hasBaseOrStandalonePlanSpecifier = sanityAndReorderBPOrStandaloneSpecFirst(catalog, subscriptionBaseWithAddOnsSpecifier, billingRequestedDateRaw, reorderedSpecifiers);
DateTime billingRequestedDate = billingRequestedDateRaw;
SubscriptionBaseBundle bundle = null;
@@ -294,9 +294,8 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_INVALID_ENTITLEMENT_SPECIFIER);
}
} else if (subscriptionBaseWithAddOnsSpecifier.getBundleExternalKey() != null &&
- baseOrFirstStandalonePlanSpecifier == null) { // Skip the expensive checks if we are about to create the bundle (validation will be done in SubscriptionDao#createSubscriptionBundle)
- final List<SubscriptionBaseBundle> existingBundles = dao.getSubscriptionBundlesForKey(subscriptionBaseWithAddOnsSpecifier.getBundleExternalKey(), context);
- final SubscriptionBaseBundle tmp = getActiveBundleForKeyNotException(existingBundles, dao, clock, catalog, context);
+ !hasBaseOrStandalonePlanSpecifier) { // Skip the expensive checks if we are about to create the bundle (validation will be done in SubscriptionDao#createSubscriptionBundle)
+ final SubscriptionBaseBundle tmp = getActiveBundleForKey(subscriptionBaseWithAddOnsSpecifier.getBundleExternalKey(), catalog, context);
if (tmp == null) {
throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_NO_BP, subscriptionBaseWithAddOnsSpecifier.getBundleExternalKey());
} else if (!tmp.getAccountId().equals(accountId)) {
@@ -315,25 +314,26 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
}
}
- if (bundle == null && baseOrFirstStandalonePlanSpecifier != null) {
+ if (bundle == null && hasBaseOrStandalonePlanSpecifier) {
bundle = createBundleForAccount(accountId,
subscriptionBaseWithAddOnsSpecifier.getBundleExternalKey(),
renameCancelledBundleIfExist,
context);
- } else if (bundle != null && baseSubscription != null && baseOrFirstStandalonePlanSpecifier != null && isBaseSpecifier(catalog, billingRequestedDateRaw, baseOrFirstStandalonePlanSpecifier)) {
+ } else if (bundle != null && baseSubscription != null && hasBaseOrStandalonePlanSpecifier) {
throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_BP_EXISTS, bundle.getExternalKey());
} else if (bundle == null) {
+ log.warn("Invalid specifier: {}", subscriptionBaseWithAddOnsSpecifier);
throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_INVALID_ENTITLEMENT_SPECIFIER);
}
final SubscriptionAndAddOnsSpecifier subscriptionAndAddOnsSpecifier = new SubscriptionAndAddOnsSpecifier(bundle,
billingRequestedDate,
verifyAndBuildSubscriptionSpecifiers(bundle,
- baseOrFirstStandalonePlanSpecifier,
+ hasBaseOrStandalonePlanSpecifier,
reorderedSpecifiers,
subscriptionBaseWithAddOnsSpecifier.isMigrated(),
context,
- now,
+ context.getCreatedDate(),
billingRequestedDate,
catalog,
callContext));
@@ -400,7 +400,7 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
@VisibleForTesting
@Override
public SubscriptionBaseBundle createBundleForAccount(final UUID accountId, final String bundleKey, final boolean renameCancelledBundleIfExist, final InternalCallContext context) throws SubscriptionBaseApiException {
- final DateTime now = clock.getUTCNow();
+ final DateTime now = context.getCreatedDate();
final DefaultSubscriptionBaseBundle bundle = new DefaultSubscriptionBaseBundle(bundleKey, accountId, now, now, now, now);
if (null != bundleKey && bundleKey.length() > 255) {
throw new SubscriptionBaseApiException(ErrorCode.EXTERNAL_KEY_LIMIT_EXCEEDED);
@@ -473,7 +473,9 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
return dao.getNonAOSubscriptionIdsForKey(bundleKey, context);
}
- public static SubscriptionBaseBundle getActiveBundleForKeyNotException(final Iterable<SubscriptionBaseBundle> existingBundles, final SubscriptionDao dao, final Clock clock, final Catalog catalog, final InternalTenantContext context) {
+ @Override
+ public SubscriptionBaseBundle getActiveBundleForKey(final String bundleKey, final Catalog catalog, final InternalTenantContext context) {
+ final List<SubscriptionBaseBundle> existingBundles = dao.getSubscriptionBundlesForKey(bundleKey, context);
for (final SubscriptionBaseBundle cur : existingBundles) {
final List<SubscriptionBase> subscriptions;
try {
@@ -614,9 +616,8 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
// verify the number of subscriptions (of the same kind) allowed per bundle
final Catalog catalog = catalogInternalApi.getFullCatalog(true, true, context);
- final DateTime now = clock.getUTCNow();
final DateTime effectiveDate = (requestedDateWithMs != null) ? DefaultClock.truncateMs(requestedDateWithMs) : null;
- final DateTime effectiveCatalogDate = effectiveDate != null ? effectiveDate : now;
+ final DateTime effectiveCatalogDate = effectiveDate != null ? effectiveDate : context.getCreatedDate();
final PlanPhasePriceOverridesWithCallContext overridesWithContext = new DefaultPlanPhasePriceOverridesWithCallContext(overrides, callContext);
final Plan plan = catalog.createOrFindPlan(spec, overridesWithContext, effectiveCatalogDate);
if (ProductCategory.ADD_ON.toString().equalsIgnoreCase(plan.getProduct().getCategory().toString())) {
@@ -877,7 +878,7 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
}
// Today as seen by this account
- final LocalDate startDate = effectiveFromDate != null ? effectiveFromDate : internalCallContext.toLocalDate(clock.getUTCNow());
+ final LocalDate startDate = effectiveFromDate != null ? effectiveFromDate : internalCallContext.toLocalDate(internalCallContext.getCreatedDate());
// We want to compute a LocalDate in account TZ which maps to the provided 'bcd' and then compute an effectiveDate for when that BCD_CHANGE event needs to be triggered
//
@@ -899,7 +900,7 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
} else /* bcd > lastDayOfMonth && bcd > currentDay */ {
requestedDate = new LocalDate(startDate.getYear(), startDate.getMonthOfYear(), lastDayOfMonth);
}
- return requestedDate == null ? clock.getUTCNow() : internalCallContext.toUTCDateTime(requestedDate);
+ return requestedDate == null ? internalCallContext.getCreatedDate() : internalCallContext.toUTCDateTime(requestedDate);
}
private DateTime getBundleStartDateWithSanity(final UUID bundleId, @Nullable final SubscriptionBase baseSubscription, final Plan plan,
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/transfer/DefaultSubscriptionBaseTransferApi.java b/subscription/src/main/java/org/killbill/billing/subscription/api/transfer/DefaultSubscriptionBaseTransferApi.java
index f93883e..a075ae9 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/transfer/DefaultSubscriptionBaseTransferApi.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/transfer/DefaultSubscriptionBaseTransferApi.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
*
* The Billing Project 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
@@ -35,6 +35,7 @@ import org.killbill.billing.catalog.api.ProductCategory;
import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
import org.killbill.billing.subscription.api.SubscriptionApiBase;
import org.killbill.billing.subscription.api.SubscriptionBaseApiService;
+import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
import org.killbill.billing.subscription.api.svcs.DefaultSubscriptionInternalApi;
import org.killbill.billing.subscription.api.timeline.BundleBaseTimeline;
import org.killbill.billing.subscription.api.timeline.SubscriptionBaseRepairException;
@@ -66,14 +67,16 @@ public class DefaultSubscriptionBaseTransferApi extends SubscriptionApiBase impl
private final CatalogInternalApi catalogInternalApi;
private final SubscriptionBaseTimelineApi timelineApi;
+ private final SubscriptionBaseInternalApi subscriptionBaseInternalApi;
private final InternalCallContextFactory internalCallContextFactory;
@Inject
public DefaultSubscriptionBaseTransferApi(final Clock clock, final SubscriptionDao dao, final SubscriptionBaseTimelineApi timelineApi, final CatalogInternalApi catalogInternalApi,
- final SubscriptionBaseApiService apiService, final InternalCallContextFactory internalCallContextFactory) {
+ final SubscriptionBaseInternalApi subscriptionBaseInternalApi, final SubscriptionBaseApiService apiService, final InternalCallContextFactory internalCallContextFactory) {
super(dao, apiService, clock);
this.catalogInternalApi = catalogInternalApi;
this.timelineApi = timelineApi;
+ this.subscriptionBaseInternalApi = subscriptionBaseInternalApi;
this.internalCallContextFactory = internalCallContextFactory;
}
@@ -181,30 +184,27 @@ public class DefaultSubscriptionBaseTransferApi extends SubscriptionApiBase impl
final InternalCallContext fromInternalCallContext = internalCallContextFactory.createInternalCallContext(sourceAccountId, context);
final InternalCallContext toInternalCallContext = internalCallContextFactory.createInternalCallContext(destAccountId, context);
-
try {
final Catalog catalog = catalogInternalApi.getFullCatalog(true, true, fromInternalCallContext);
- final DateTime effectiveTransferDate = transferDate == null ? clock.getUTCNow() : transferDate;
- if (effectiveTransferDate.isAfter(clock.getUTCNow())) {
+ final DateTime effectiveTransferDate = transferDate == null ? context.getCreatedDate() : transferDate;
+ if (effectiveTransferDate.isAfter(context.getCreatedDate())) {
// The transfer event for the migrated bundle will be the first one, which cannot be in the future
// (subscription always expects the first event to be in the past)
throw new SubscriptionBaseTransferApiException(ErrorCode.SUB_TRANSFER_INVALID_EFF_DATE, effectiveTransferDate);
}
- final SubscriptionBaseBundle bundleForAccountAndKey = dao.getSubscriptionBundlesForAccountAndKey(sourceAccountId, bundleKey, fromInternalCallContext);
- final SubscriptionBaseBundle bundle = DefaultSubscriptionInternalApi.getActiveBundleForKeyNotException(ImmutableList.of(bundleForAccountAndKey), dao, clock, catalog, fromInternalCallContext);
+ final SubscriptionBaseBundle bundle = subscriptionBaseInternalApi.getActiveBundleForKey(bundleKey, catalog, fromInternalCallContext);
if (bundle == null) {
throw new SubscriptionBaseTransferApiException(ErrorCode.SUB_CREATE_NO_BUNDLE, bundleKey);
}
-
// Get the bundle timeline for the old account
final BundleBaseTimeline bundleBaseTimeline = timelineApi.getBundleTimeline(bundle, context);
final DefaultSubscriptionBaseBundle subscriptionBundleData = new DefaultSubscriptionBaseBundle(bundleKey, destAccountId, effectiveTransferDate,
- bundle.getOriginalCreatedDate(), clock.getUTCNow(), clock.getUTCNow());
+ bundle.getOriginalCreatedDate(), context.getCreatedDate(), context.getCreatedDate());
final List<SubscriptionTransferData> subscriptionTransferDataList = new LinkedList<SubscriptionTransferData>();
final List<TransferCancelData> transferCancelDataList = new LinkedList<TransferCancelData>();
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBaseApiService.java b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBaseApiService.java
index 29cedce..6f3b516 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBaseApiService.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBaseApiService.java
@@ -167,7 +167,6 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
if (currentState == EntitlementState.CANCELLED) {
throw new SubscriptionBaseApiException(ErrorCode.SUB_CANCEL_BAD_STATE, subscription.getId(), currentState);
}
- final DateTime now = clock.getUTCNow();
final Plan currentPlan = subscription.getCurrentOrPendingPlan();
final PlanPhaseSpecifier planPhase = new PlanPhaseSpecifier(currentPlan.getName(), null);
@@ -197,8 +196,7 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
final Catalog fullCatalog;
try {
fullCatalog = catalogInternalApi.getFullCatalog(true, true, internalCallContext);
- final DateTime now = clock.getUTCNow();
- final DateTime effectiveDate = (requestedDateWithMs != null) ? DefaultClock.truncateMs(requestedDateWithMs) : now;
+ final DateTime effectiveDate = (requestedDateWithMs != null) ? DefaultClock.truncateMs(requestedDateWithMs) : context.getCreatedDate();
return doCancelPlan(ImmutableMap.<DefaultSubscriptionBase, DateTime>of(subscription, effectiveDate), fullCatalog, internalCallContext);
} catch (final CatalogApiException e) {
@@ -280,10 +278,9 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
final InternalCallContext internalCallContext = createCallContextFromBundleId(subscription.getBundleId(), context);
final Catalog fullCatalog = catalogInternalApi.getFullCatalog(true, true, internalCallContext);
- final DateTime now = clock.getUTCNow();
final SubscriptionBaseEvent uncancelEvent = new ApiEventUncancel(new ApiEventBuilder()
.setSubscriptionId(subscription.getId())
- .setEffectiveDate(now)
+ .setEffectiveDate(context.getCreatedDate())
.setFromDisk(true));
final List<SubscriptionBaseEvent> uncancelEvents = new ArrayList<SubscriptionBaseEvent>();
@@ -294,7 +291,7 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
// In case of a pending subscription we don't want to pass an effective date prior the CREATE event as we would end up with the wrong
// transition in PlanAligner (next transition would be CREATE instead of potential next PHASE)
//
- final DateTime planAlignerEffectiveDate = subscription.getState() == EntitlementState.PENDING ? subscription.getStartDate() : now;
+ final DateTime planAlignerEffectiveDate = subscription.getState() == EntitlementState.PENDING ? subscription.getStartDate() : context.getCreatedDate();
final TimedPhase nextTimedPhase = planAligner.getNextTimedPhase(subscription, planAlignerEffectiveDate, fullCatalog, internalCallContext);
final PhaseEvent nextPhaseEvent = (nextTimedPhase != null) ?
@@ -337,11 +334,9 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
@Override
public DateTime changePlan(final DefaultSubscriptionBase subscription, final PlanPhaseSpecifier spec, final List<PlanPhasePriceOverride> overrides, final CallContext context) throws SubscriptionBaseApiException {
- final DateTime now = clock.getUTCNow();
-
validateSubscriptionStateForChangePlan(subscription, null);
- final PlanChangeResult planChangeResult = getPlanChangeResult(subscription, spec, now, context);
+ final PlanChangeResult planChangeResult = getPlanChangeResult(subscription, spec, context.getCreatedDate(), context);
final DateTime effectiveDate = dryRunChangePlan(subscription, spec, null, planChangeResult.getPolicy(), context);
validateEffectiveDate(subscription, effectiveDate);
@@ -562,10 +557,9 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
final InternalCallContext internalCallContext = createCallContextFromBundleId(subscription.getBundleId(), context);
final Catalog fullCatalog = catalogInternalApi.getFullCatalog(true, true, internalCallContext);
- final DateTime now = clock.getUTCNow();
final SubscriptionBaseEvent undoChangePlanEvent = new ApiEventUndoChange(new ApiEventBuilder()
.setSubscriptionId(subscription.getId())
- .setEffectiveDate(now)
+ .setEffectiveDate(context.getCreatedDate())
.setFromDisk(true));
final List<SubscriptionBaseEvent> undoChangePlanEvents = new ArrayList<SubscriptionBaseEvent>();
@@ -576,7 +570,7 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
// In case of a pending subscription we don't want to pass an effective date prior the CREATE event as we would end up with the wrong
// transition in PlanAligner (next transition would be CREATE instead of potential next PHASE)
//
- final DateTime planAlignerEffectiveDate = subscription.getState() == EntitlementState.PENDING ? subscription.getStartDate() : now;
+ final DateTime planAlignerEffectiveDate = subscription.getState() == EntitlementState.PENDING ? subscription.getStartDate() : context.getCreatedDate();
final TimedPhase nextTimedPhase = planAligner.getNextTimedPhase(subscription, planAlignerEffectiveDate, fullCatalog, internalCallContext);
final PhaseEvent nextPhaseEvent = (nextTimedPhase != null) ?
@@ -614,8 +608,7 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
private List<DefaultSubscriptionBase> computeAddOnsToCancel(final Collection<SubscriptionBaseEvent> cancelEvents, final CatalogEntity baseProduct, final UUID bundleId, final DateTime effectiveDate, final Catalog catalog, final InternalCallContext internalCallContext) throws CatalogApiException {
// If cancellation/change occur in the future, there is nothing to do
- final DateTime now = clock.getUTCNow();
- if (effectiveDate.compareTo(now) > 0) {
+ if (effectiveDate.compareTo(internalCallContext.getCreatedDate()) > 0) {
return ImmutableList.<DefaultSubscriptionBase>of();
} else {
return addCancellationAddOnForEventsIfRequired(cancelEvents, baseProduct, bundleId, effectiveDate, catalog, internalCallContext);
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java
index 065e0c8..98ed638 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java
@@ -471,7 +471,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
final BundleSqlDao bundleSqlDao = entitySqlDaoWrapperFactory.become(BundleSqlDao.class);
final String bundleId = subscription.getBundleId().toString();
- bundleSqlDao.updateBundleLastSysTime(bundleId, clock.getUTCNow().toDate(), contextWithUpdatedDate);
+ bundleSqlDao.updateBundleLastSysTime(bundleId, context.getCreatedDate().toDate(), contextWithUpdatedDate);
return null;
}
});
@@ -553,7 +553,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
for (final SubscriptionBaseEvent cur : initialEvents) {
createdEvents.add(createAndRefresh(eventsDaoFromSameTransaction, new SubscriptionEventModelDao(cur), context));
- final boolean isBusEvent = cur.getEffectiveDate().compareTo(clock.getUTCNow()) <= 0 && (cur.getType() == EventType.API_USER);
+ final boolean isBusEvent = cur.getEffectiveDate().compareTo(context.getCreatedDate()) <= 0 && (cur.getType() == EventType.API_USER);
recordBusOrFutureNotificationFromTransaction(defaultSubscriptionBase, cur, entitySqlDaoWrapperFactory, isBusEvent, 0, catalog, context);
}
@@ -633,7 +633,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
final UUID subscriptionId = subscription.getId();
Set<SubscriptionEventModelDao> targetEvents = new HashSet<SubscriptionEventModelDao>();
- final Date now = clock.getUTCNow().toDate();
+ final Date now = context.getCreatedDate().toDate();
final List<SubscriptionEventModelDao> eventModels = transactional.getFutureActiveEventForSubscription(subscriptionId.toString(), now, contextWithUpdatedDate);
for (final SubscriptionEventModelDao cur : eventModels) {
@@ -719,7 +719,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
for (final SubscriptionBaseEvent cur : inputChangeEvents) {
createAndRefresh(transactional, new SubscriptionEventModelDao(cur), context);
- final boolean isBusEvent = cur.getEffectiveDate().compareTo(clock.getUTCNow()) <= 0 && (cur.getType() == EventType.API_USER);
+ final boolean isBusEvent = cur.getEffectiveDate().compareTo(context.getCreatedDate()) <= 0 && (cur.getType() == EventType.API_USER);
recordBusOrFutureNotificationFromTransaction(subscription, cur, entitySqlDaoWrapperFactory, isBusEvent, 0, catalog, context);
}
@@ -772,7 +772,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
final SubscriptionEventModelDao cancelEventWithUpdatedTotalOrdering = createAndRefresh(subscriptionEventSqlDao, new SubscriptionEventModelDao(cancelEvent), context);
final SubscriptionBaseEvent refreshedSubscriptionEvent = SubscriptionEventModelDao.toSubscriptionEvent(cancelEventWithUpdatedTotalOrdering);
- final boolean isBusEvent = refreshedSubscriptionEvent.getEffectiveDate().compareTo(clock.getUTCNow()) <= 0;
+ final boolean isBusEvent = refreshedSubscriptionEvent.getEffectiveDate().compareTo(context.getCreatedDate()) <= 0;
recordBusOrFutureNotificationFromTransaction(subscription, refreshedSubscriptionEvent, entitySqlDaoWrapperFactory, isBusEvent, seqId, catalog, context);
// Notify the Bus of the requested change
@@ -811,7 +811,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
@Nullable final ApiEventType apiType, final InternalCallContext context) {
SubscriptionEventModelDao futureEvent = null;
- final Date now = clock.getUTCNow().toDate();
+ final Date now = context.getCreatedDate().toDate();
final List<SubscriptionEventModelDao> eventModels = dao.become(SubscriptionEventSqlDao.class).getFutureActiveEventForSubscription(subscriptionId.toString(), now, context);
for (final SubscriptionEventModelDao cur : eventModels) {
@@ -923,7 +923,6 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
}
if (baseTriggerEventForAddOnCancellation != null) {
- final DateTime now = clock.getUTCNow();
final SubscriptionBaseEvent addOnCancelEvent = new ApiEventCancel(new ApiEventBuilder()
.setSubscriptionId(reloaded.getId())
.setEffectiveDate(baseTriggerEventForAddOnCancellation.getEffectiveDate())
@@ -1043,7 +1042,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
// Notify the Bus
notifyBusOfRequestedChange(entitySqlDaoWrapperFactory, subscription, bcdEvent, SubscriptionBaseTransitionType.BCD_CHANGE, context);
- final boolean isBusEvent = bcdEvent.getEffectiveDate().compareTo(clock.getUTCNow()) <= 0;
+ final boolean isBusEvent = bcdEvent.getEffectiveDate().compareTo(context.getCreatedDate()) <= 0;
recordBusOrFutureNotificationFromTransaction(subscription, bcdEvent, entitySqlDaoWrapperFactory, isBusEvent, 0, catalog, context);
return null;
@@ -1186,7 +1185,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
}
private InternalCallContext contextWithUpdatedDate(final InternalCallContext input) {
- return new InternalCallContext(input, clock.getUTCNow());
+ return new InternalCallContext(input, input.getCreatedDate());
}
}
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/api/transfer/TestDefaultSubscriptionTransferApi.java b/subscription/src/test/java/org/killbill/billing/subscription/api/transfer/TestDefaultSubscriptionTransferApi.java
index 95ce7e2..9c3818b 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/api/transfer/TestDefaultSubscriptionTransferApi.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/api/transfer/TestDefaultSubscriptionTransferApi.java
@@ -73,7 +73,7 @@ public class TestDefaultSubscriptionTransferApi extends SubscriptionTestSuiteNoD
final CatalogInternalApi catalogInternalApiWithMockCatalogService = new DefaultCatalogInternalApi(catalogService, internalCallContextFactory);
final SubscriptionBaseApiService apiService = Mockito.mock(SubscriptionBaseApiService.class);
final SubscriptionBaseTimelineApi timelineApi = Mockito.mock(SubscriptionBaseTimelineApi.class);
- transferApi = new DefaultSubscriptionBaseTransferApi(clock, dao, timelineApi, catalogInternalApiWithMockCatalogService, apiService, internalCallContextFactory);
+ transferApi = new DefaultSubscriptionBaseTransferApi(clock, dao, timelineApi, catalogInternalApiWithMockCatalogService, subscriptionInternalApi, apiService, internalCallContextFactory);
// Overrride catalog with our Mock CatalogService
this.catalog = catalogInternalApiWithMockCatalogService.getFullCatalog(true, true, internalCallContext);
}
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/api/transfer/TestTransfer.java b/subscription/src/test/java/org/killbill/billing/subscription/api/transfer/TestTransfer.java
index b9a0c21..7f3dc38 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/api/transfer/TestTransfer.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/api/transfer/TestTransfer.java
@@ -305,7 +305,7 @@ public class TestTransfer extends SubscriptionTestSuiteWithEmbeddedDB {
clock.addDays(3);
final String aoProduct1 = "Telescopic-Scope";
final BillingPeriod aoTerm1 = BillingPeriod.MONTHLY;
- final DefaultSubscriptionBase aoSubscription1 = testUtil.createSubscription(bundle, baseSubscription, aoProduct1, aoTerm1, basePriceList);
+ final DefaultSubscriptionBase aoSubscription1 = testUtil.createSubscription(bundle, aoProduct1, aoTerm1, basePriceList);
assertEquals(aoSubscription1.getState(), EntitlementState.ACTIVE);
// MOVE ANOTHER 25 DAYS AND CREATE AO2 [ BP STILL IN TRIAL]
@@ -313,7 +313,7 @@ public class TestTransfer extends SubscriptionTestSuiteWithEmbeddedDB {
clock.addDays(25);
final String aoProduct2 = "Laser-Scope";
final BillingPeriod aoTerm2 = BillingPeriod.MONTHLY;
- final DefaultSubscriptionBase aoSubscription2 = testUtil.createSubscription(bundle, baseSubscription, aoProduct2, aoTerm2, basePriceList);
+ final DefaultSubscriptionBase aoSubscription2 = testUtil.createSubscription(bundle, aoProduct2, aoTerm2, basePriceList);
assertEquals(aoSubscription2.getState(), EntitlementState.ACTIVE);
// MOVE AFTER TRIAL AND AO DISCOUNT PHASE [LASER SCOPE STILL IN DISCOUNT]
@@ -397,7 +397,7 @@ public class TestTransfer extends SubscriptionTestSuiteWithEmbeddedDB {
clock.addDays(3);
final String aoProduct1 = "Telescopic-Scope";
final BillingPeriod aoTerm1 = BillingPeriod.MONTHLY;
- final DefaultSubscriptionBase aoSubscription1 = testUtil.createSubscription(bundle, baseSubscription, aoProduct1, aoTerm1, basePriceList);
+ final DefaultSubscriptionBase aoSubscription1 = testUtil.createSubscription(bundle, aoProduct1, aoTerm1, basePriceList);
assertEquals(aoSubscription1.getState(), EntitlementState.ACTIVE);
testListener.pushExpectedEvent(NextEvent.PHASE);
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestSubscriptionHelper.java b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestSubscriptionHelper.java
index 87a882a..48f3c80 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestSubscriptionHelper.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestSubscriptionHelper.java
@@ -44,7 +44,6 @@ import org.killbill.billing.entitlement.api.EntitlementSpecifier;
import org.killbill.billing.entitlement.api.SubscriptionEventType;
import org.killbill.billing.invoice.api.DryRunArguments;
import org.killbill.billing.invoice.api.DryRunType;
-import org.killbill.billing.subscription.api.SubscriptionBase;
import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
import org.killbill.billing.subscription.api.SubscriptionBaseWithAddOns;
import org.killbill.billing.subscription.api.SubscriptionBaseWithAddOnsSpecifier;
@@ -138,40 +137,27 @@ public class TestSubscriptionHelper {
};
}
- public DefaultSubscriptionBase createSubscription(final SubscriptionBaseBundle bundle, final String productName, final BillingPeriod term, final String planSet, final DateTime requestedDate)
+ public DefaultSubscriptionBase createSubscription(final SubscriptionBaseBundle bundle, final String productName, final BillingPeriod term, final String planSet, final LocalDate requestedDate)
throws SubscriptionBaseApiException {
return createSubscription(bundle, productName, term, planSet, null, requestedDate);
}
- public DefaultSubscriptionBase createSubscription(final SubscriptionBaseBundle bundle, final String productName, final BillingPeriod term, final String planSet, final PhaseType phaseType, final DateTime requestedDate)
- throws SubscriptionBaseApiException {
- return createSubscription(bundle, null, productName, term, planSet, phaseType, requestedDate);
- }
-
- public DefaultSubscriptionBase createSubscription(final SubscriptionBaseBundle bundle, final SubscriptionBase baseSubscription, final String aoProduct, final BillingPeriod aoTerm, final String aoPriceList) throws SubscriptionBaseApiException {
- return createSubscription(bundle, baseSubscription, aoProduct, aoTerm, aoPriceList, null, null);
- }
-
public DefaultSubscriptionBase createSubscription(final SubscriptionBaseBundle bundle, final String productName, final BillingPeriod term, final String planSet)
throws SubscriptionBaseApiException {
- return createSubscription(bundle, null, productName, term, planSet, null, null);
- }
-
- public DefaultSubscriptionBase createSubscription(final SubscriptionBaseBundle bundle, final SubscriptionBase baseSubscription, final String productName, final BillingPeriod term, final String planSet, final DateTime requestedDate) throws SubscriptionBaseApiException {
- return createSubscription(bundle, baseSubscription, productName, term, planSet, null, requestedDate);
+ return createSubscription(bundle, productName, term, planSet, null, null);
}
public DefaultSubscriptionBase createSubscription(final boolean noEvents, final SubscriptionBaseBundle bundle, final String productName, final BillingPeriod term, final String planSet) throws SubscriptionBaseApiException {
- return createSubscription(noEvents, bundle, null, productName, term, planSet, null, null);
+ return createSubscription(noEvents, bundle, productName, term, planSet, null, null);
}
- public DefaultSubscriptionBase createSubscription(final SubscriptionBaseBundle bundle, final SubscriptionBase baseSubscription, final String productName, final BillingPeriod term, final String planSet, final PhaseType phaseType, final DateTime requestedDate)
+ public DefaultSubscriptionBase createSubscription(final SubscriptionBaseBundle bundle, final String productName, final BillingPeriod term, final String planSet, final PhaseType phaseType, final LocalDate requestedDate)
throws SubscriptionBaseApiException {
- return createSubscription(false, bundle, baseSubscription, productName, term, planSet, phaseType, requestedDate);
+ return createSubscription(false, bundle, productName, term, planSet, phaseType, requestedDate);
}
- public DefaultSubscriptionBase createSubscription(final boolean noEvents, @Nullable final SubscriptionBaseBundle bundle, final SubscriptionBase baseSubscription, final String productName, final BillingPeriod term, final String planSet, final PhaseType phaseType, final DateTime requestedDate)
- throws SubscriptionBaseApiException {
+ private DefaultSubscriptionBase createSubscription(final boolean noEvents, @Nullable final SubscriptionBaseBundle bundle, final String productName, final BillingPeriod term, final String planSet, final PhaseType phaseType, final LocalDate requestedDate)
+ throws SubscriptionBaseApiException {
// Make sure the right account information is used
final InternalCallContext internalCallContext = bundle == null ? this.internalCallContext : internalCallContextFactory.createInternalCallContext(bundle.getAccountId(),
ObjectType.ACCOUNT,
@@ -189,7 +175,7 @@ public class TestSubscriptionHelper {
}
}
- if (!noEvents && (requestedDate == null || requestedDate.compareTo(clock.getUTCNow()) <= 0)) {
+ if (!noEvents && (requestedDate == null || requestedDate.compareTo(clock.getUTCToday()) <= 0)) {
testListener.pushExpectedEvent(NextEvent.CREATE);
}
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiAddOn.java b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiAddOn.java
index cfa1bde..4503630 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiAddOn.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiAddOn.java
@@ -59,7 +59,7 @@ public class TestUserApiAddOn extends SubscriptionTestSuiteWithEmbeddedDB {
final BillingPeriod aoTerm = BillingPeriod.MONTHLY;
final String aoPriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
- DefaultSubscriptionBase aoSubscription = testUtil.createSubscription(bundle, baseSubscription, aoProduct, aoTerm, aoPriceList);
+ DefaultSubscriptionBase aoSubscription = testUtil.createSubscription(bundle, aoProduct, aoTerm, aoPriceList);
assertEquals(aoSubscription.getState(), EntitlementState.ACTIVE);
testListener.pushExpectedEvent(NextEvent.CANCEL);
@@ -85,7 +85,7 @@ public class TestUserApiAddOn extends SubscriptionTestSuiteWithEmbeddedDB {
final BillingPeriod aoTerm = BillingPeriod.MONTHLY;
final String aoPriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
- DefaultSubscriptionBase aoSubscription = testUtil.createSubscription(bundle, baseSubscription, aoProduct, aoTerm, aoPriceList);
+ DefaultSubscriptionBase aoSubscription = testUtil.createSubscription(bundle, aoProduct, aoTerm, aoPriceList);
assertEquals(aoSubscription.getState(), EntitlementState.ACTIVE);
// Move clock after a month
@@ -159,7 +159,7 @@ public class TestUserApiAddOn extends SubscriptionTestSuiteWithEmbeddedDB {
final BillingPeriod aoTerm = BillingPeriod.MONTHLY;
final String aoPriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
- DefaultSubscriptionBase aoSubscription = testUtil.createSubscription(bundle, baseSubscription, aoProduct, aoTerm, aoPriceList);
+ DefaultSubscriptionBase aoSubscription = testUtil.createSubscription(bundle, aoProduct, aoTerm, aoPriceList);
testListener.pushExpectedEvent(NextEvent.PHASE);
testListener.pushExpectedEvent(NextEvent.PHASE);
@@ -213,7 +213,7 @@ public class TestUserApiAddOn extends SubscriptionTestSuiteWithEmbeddedDB {
final BillingPeriod aoTerm = BillingPeriod.MONTHLY;
final String aoPriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
- DefaultSubscriptionBase aoSubscription = testUtil.createSubscription(bundle, baseSubscription, aoProduct, aoTerm, aoPriceList);
+ DefaultSubscriptionBase aoSubscription = testUtil.createSubscription(bundle, aoProduct, aoTerm, aoPriceList);
testListener.pushExpectedEvent(NextEvent.PHASE);
testListener.pushExpectedEvent(NextEvent.PHASE);
@@ -277,7 +277,7 @@ public class TestUserApiAddOn extends SubscriptionTestSuiteWithEmbeddedDB {
final BillingPeriod aoTerm = BillingPeriod.MONTHLY;
final String aoPriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
- DefaultSubscriptionBase aoSubscription = testUtil.createSubscription(bundle, baseSubscription, aoProduct, aoTerm, aoPriceList);
+ DefaultSubscriptionBase aoSubscription = testUtil.createSubscription(bundle, aoProduct, aoTerm, aoPriceList);
testListener.pushExpectedEvent(NextEvent.PHASE);
testListener.pushExpectedEvent(NextEvent.PHASE);
@@ -335,7 +335,7 @@ public class TestUserApiAddOn extends SubscriptionTestSuiteWithEmbeddedDB {
final String aoPriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
// CREATE AO
- DefaultSubscriptionBase aoSubscription = testUtil.createSubscription(bundle, baseSubscription, aoProduct, aoTerm, aoPriceList);
+ DefaultSubscriptionBase aoSubscription = testUtil.createSubscription(bundle, aoProduct, aoTerm, aoPriceList);
testListener.pushExpectedEvent(NextEvent.PHASE);
testListener.pushExpectedEvent(NextEvent.PHASE);
@@ -434,7 +434,7 @@ public class TestUserApiAddOn extends SubscriptionTestSuiteWithEmbeddedDB {
// CREATE ADDON
final DateTime beforeAOCreation = clock.getUTCNow();
- DefaultSubscriptionBase aoSubscription = testUtil.createSubscription(bundle, baseSubscription, aoProduct, aoTerm, aoPriceList);
+ DefaultSubscriptionBase aoSubscription = testUtil.createSubscription(bundle, aoProduct, aoTerm, aoPriceList);
final DateTime afterAOCreation = clock.getUTCNow();
// CHECK EVERYTHING
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiCancel.java b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiCancel.java
index 63114db..b32981b 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiCancel.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiCancel.java
@@ -24,7 +24,6 @@ import org.joda.time.DateTime;
import org.joda.time.Interval;
import org.joda.time.LocalDate;
import org.killbill.billing.ErrorCode;
-import org.killbill.billing.api.FlakyRetryAnalyzer;
import org.killbill.billing.api.TestApiListener.NextEvent;
import org.killbill.billing.catalog.api.BillingActionPolicy;
import org.killbill.billing.catalog.api.BillingPeriod;
@@ -394,13 +393,13 @@ public class TestUserApiCancel extends SubscriptionTestSuiteWithEmbeddedDB {
@Test(groups = "slow")
public void testCancelUncancelFutureSubscription() throws SubscriptionBaseApiException {
- final DateTime init = clock.getUTCNow();
+ final LocalDate init = clock.getUTCToday();
final String productName = "Shotgun";
final BillingPeriod term = BillingPeriod.MONTHLY;
final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
- final DateTime futureCreationDate = init.plusDays(10);
+ final LocalDate futureCreationDate = init.plusDays(10);
DefaultSubscriptionBase subscription = testUtil.createSubscription(bundle, productName, term, planSetName, futureCreationDate);
assertListenerStatus();
@@ -441,11 +440,11 @@ public class TestUserApiCancel extends SubscriptionTestSuiteWithEmbeddedDB {
final BillingPeriod baseTerm = BillingPeriod.MONTHLY;
final String basePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
- final DateTime startDate = clock.getUTCNow().plusDays(5);
+ final LocalDate startDate = clock.getUTCToday().plusDays(5);
final DefaultSubscriptionBase subscription = testUtil.createSubscription(bundle, baseProduct, baseTerm, basePriceList, startDate);
assertEquals(subscription.getState(), Entitlement.EntitlementState.PENDING);
- assertEquals(subscription.getStartDate().compareTo(startDate), 0);
+ assertEquals(subscription.getStartDate().compareTo(startDate.toDateTime(accountData.getReferenceTime())), 0);
// The code will be smart to infer the cancelation date as being the future startDate
subscription.cancel(callContext);
@@ -455,7 +454,7 @@ public class TestUserApiCancel extends SubscriptionTestSuiteWithEmbeddedDB {
assertListenerStatus();
final DefaultSubscriptionBase subscription2 = (DefaultSubscriptionBase) subscriptionInternalApi.getSubscriptionFromId(subscription.getId(), internalCallContext);
- assertEquals(subscription2.getStartDate().compareTo(startDate), 0);
+ assertEquals(subscription2.getStartDate().compareTo(subscription.getStartDate()), 0);
assertEquals(subscription2.getState(), Entitlement.EntitlementState.CANCELLED);
assertNull(subscription2.getCurrentPlan());
}
@@ -467,11 +466,11 @@ public class TestUserApiCancel extends SubscriptionTestSuiteWithEmbeddedDB {
final BillingPeriod baseTerm = BillingPeriod.MONTHLY;
final String basePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
- final DateTime startDate = clock.getUTCNow().plusDays(5);
+ final LocalDate startDate = clock.getUTCToday().plusDays(5);
final DefaultSubscriptionBase subscription = testUtil.createSubscription(bundle, baseProduct, baseTerm, basePriceList, startDate);
assertEquals(subscription.getState(), Entitlement.EntitlementState.PENDING);
- assertEquals(subscription.getStartDate().compareTo(startDate), 0);
+ assertEquals(subscription.getStartDate().compareTo(startDate.toDateTime(accountData.getReferenceTime())), 0);
try {
subscription.cancelWithDate(null, callContext);
@@ -481,20 +480,20 @@ public class TestUserApiCancel extends SubscriptionTestSuiteWithEmbeddedDB {
}
try {
- subscription.cancelWithDate(startDate.minusDays(1), callContext);
+ subscription.cancelWithDate(subscription.getStartDate().minusDays(1), callContext);
fail("Cancel plan should have failed : subscription PENDING");
} catch (SubscriptionBaseApiException e) {
assertEquals(e.getCode(), ErrorCode.SUB_INVALID_REQUESTED_DATE.getCode());
}
- subscription.cancelWithDate(startDate, callContext);
+ subscription.cancelWithDate(subscription.getStartDate(), callContext);
testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.CANCEL);
clock.addDays(5);
assertListenerStatus();
final DefaultSubscriptionBase subscription2 = (DefaultSubscriptionBase) subscriptionInternalApi.getSubscriptionFromId(subscription.getId(), internalCallContext);
- assertEquals(subscription2.getStartDate().compareTo(startDate), 0);
+ assertEquals(subscription2.getStartDate().compareTo(subscription.getStartDate()), 0);
assertEquals(subscription2.getState(), Entitlement.EntitlementState.CANCELLED);
assertNull(subscription2.getCurrentPlan());
}
@@ -507,11 +506,11 @@ public class TestUserApiCancel extends SubscriptionTestSuiteWithEmbeddedDB {
final BillingPeriod baseTerm = BillingPeriod.MONTHLY;
final String basePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
- final DateTime startDate = clock.getUTCNow().plusDays(5);
+ final LocalDate startDate = clock.getUTCToday().plusDays(5);
final DefaultSubscriptionBase subscription = testUtil.createSubscription(bundle, baseProduct, baseTerm, basePriceList, startDate);
assertEquals(subscription.getState(), Entitlement.EntitlementState.PENDING);
- assertEquals(subscription.getStartDate().compareTo(startDate), 0);
+ assertEquals(subscription.getStartDate().compareTo(startDate.toDateTime(accountData.getReferenceTime())), 0);
subscription.cancelWithPolicy(BillingActionPolicy.IMMEDIATE, 1, callContext);
@@ -520,7 +519,7 @@ public class TestUserApiCancel extends SubscriptionTestSuiteWithEmbeddedDB {
assertListenerStatus();
final DefaultSubscriptionBase subscription2 = (DefaultSubscriptionBase) subscriptionInternalApi.getSubscriptionFromId(subscription.getId(), internalCallContext);
- assertEquals(subscription2.getStartDate().compareTo(startDate), 0);
+ assertEquals(subscription2.getStartDate().compareTo(subscription.getStartDate()), 0);
assertEquals(subscription2.getState(), Entitlement.EntitlementState.CANCELLED);
assertNull(subscription2.getCurrentPlan());
}
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiChangePlan.java b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiChangePlan.java
index 9b83b81..04e31de 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiChangePlan.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiChangePlan.java
@@ -467,7 +467,7 @@ public class TestUserApiChangePlan extends SubscriptionTestSuiteWithEmbeddedDB {
final String aoProduct = "Laser-Scope";
final BillingPeriod aoTerm = BillingPeriod.MONTHLY;
final String aoPriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
- DefaultSubscriptionBase aoSubscription = testUtil.createSubscription(bundle, baseSubscription, aoProduct, aoTerm, aoPriceList);
+ DefaultSubscriptionBase aoSubscription = testUtil.createSubscription(bundle, aoProduct, aoTerm, aoPriceList);
try {
aoSubscription.changePlanWithDate(new PlanPhaseSpecifier(baseProduct, baseTerm, basePriceList), null, clock.getUTCNow(), callContext);
@@ -484,11 +484,11 @@ public class TestUserApiChangePlan extends SubscriptionTestSuiteWithEmbeddedDB {
final BillingPeriod baseTerm = BillingPeriod.MONTHLY;
final String basePriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
- final DateTime startDate = clock.getUTCNow().plusDays(5);
+ final LocalDate startDate = clock.getUTCToday().plusDays(5);
final DefaultSubscriptionBase subscription = testUtil.createSubscription(bundle, baseProduct, baseTerm, basePriceList, startDate);
assertEquals(subscription.getState(), Entitlement.EntitlementState.PENDING);
- assertEquals(subscription.getStartDate().compareTo(startDate), 0);
+ assertEquals(subscription.getStartDate().compareTo(startDate.toDateTime(accountData.getReferenceTime())), 0);
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Pistol", baseTerm, basePriceList, null);
@@ -502,21 +502,21 @@ public class TestUserApiChangePlan extends SubscriptionTestSuiteWithEmbeddedDB {
// Second try with date prior to startDate => Call should fail because subscription is PENDING
try {
- final DryRunArguments dryRunArguments2 = testUtil.createDryRunArguments(subscription.getId(), subscription.getBundleId(), spec, new LocalDate(startDate.minusDays(1)), SubscriptionEventType.CHANGE, null);
+ final DryRunArguments dryRunArguments2 = testUtil.createDryRunArguments(subscription.getId(), subscription.getBundleId(), spec, startDate.minusDays(1), SubscriptionEventType.CHANGE, null);
subscriptionInternalApi.getSubscriptionsForBundle(subscription.getBundleId(), dryRunArguments2, internalCallContext);
fail("Change plan should have failed : subscription PENDING");
} catch (final SubscriptionBaseApiException e) {
assertEquals(e.getCode(), ErrorCode.SUB_CHANGE_NON_ACTIVE.getCode());
}
try {
- subscription.changePlanWithDate(spec, null, startDate.minusDays(1), callContext);
+ subscription.changePlanWithDate(spec, null, subscription.getStartDate().minusDays(1), callContext);
fail("Change plan should have failed : subscription PENDING");
} catch (final SubscriptionBaseApiException e) {
assertEquals(e.getCode(), ErrorCode.SUB_INVALID_REQUESTED_DATE.getCode());
}
// Third try with date equals to startDate Call should succeed, but no event because action in future
- final DryRunArguments dryRunArguments3 = testUtil.createDryRunArguments(subscription.getId(), subscription.getBundleId(), spec, internalCallContext.toLocalDate(startDate), SubscriptionEventType.CHANGE, null);
+ final DryRunArguments dryRunArguments3 = testUtil.createDryRunArguments(subscription.getId(), subscription.getBundleId(), spec, startDate, SubscriptionEventType.CHANGE, null);
final List<SubscriptionBase> result2 = subscriptionInternalApi.getSubscriptionsForBundle(subscription.getBundleId(), dryRunArguments3, internalCallContext);
// Check we are seeing the right PENDING transition (pistol-monthly), not the START but the CHANGE on the same date
assertEquals(((DefaultSubscriptionBase) result2.get(0)).getCurrentOrPendingPlan().getName(), "pistol-monthly");
@@ -540,7 +540,7 @@ public class TestUserApiChangePlan extends SubscriptionTestSuiteWithEmbeddedDB {
final DefaultSubscriptionBase refreshed1 = (DefaultSubscriptionBase) subscriptionInternalApi.getSubscriptionFromId(subscription.getId(), internalCallContext);
assertEquals(refreshed1.getEvents().size(), subscription.getEvents().size() + 1);
- subscription.changePlanWithDate(spec, null, startDate, callContext);
+ subscription.changePlanWithDate(spec, null, subscription.getStartDate(), callContext);
assertListenerStatus();
// Move clock to startDate
@@ -549,7 +549,7 @@ public class TestUserApiChangePlan extends SubscriptionTestSuiteWithEmbeddedDB {
assertListenerStatus();
final DefaultSubscriptionBase subscription2 = (DefaultSubscriptionBase) subscriptionInternalApi.getSubscriptionFromId(subscription.getId(), internalCallContext);
- assertEquals(subscription2.getStartDate().compareTo(startDate), 0);
+ assertEquals(subscription2.getStartDate().compareTo(subscription.getStartDate()), 0);
assertEquals(subscription2.getState(), Entitlement.EntitlementState.ACTIVE);
assertEquals(subscription2.getCurrentPlan().getProduct().getName(), "Pistol");
// Same original # active events
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiCreate.java b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiCreate.java
index 1cdba9a..d659598 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiCreate.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiCreate.java
@@ -24,6 +24,7 @@ import java.util.List;
import org.joda.time.DateTime;
import org.joda.time.Interval;
+import org.joda.time.LocalDate;
import org.killbill.billing.ErrorCode;
import org.killbill.billing.api.TestApiListener.NextEvent;
import org.killbill.billing.catalog.api.BillingPeriod;
@@ -55,8 +56,8 @@ public class TestUserApiCreate extends SubscriptionTestSuiteWithEmbeddedDB {
@Test(groups = "slow")
public void testCreateBundlesWithSameExternalKeys() throws SubscriptionBaseApiException {
- final DateTime init = clock.getUTCNow();
- final DateTime requestedDate = init.minusYears(1);
+ final LocalDate init = clock.getUTCToday();
+ final LocalDate requestedDate = init.minusYears(1);
final String productName = "Shotgun";
final BillingPeriod term = BillingPeriod.MONTHLY;
@@ -67,7 +68,6 @@ public class TestUserApiCreate extends SubscriptionTestSuiteWithEmbeddedDB {
assertListenerStatus();
assertNotNull(subscription);
-
// Verify we can't create a second bundle with the same key
try {
subscriptionInternalApi.createBundleForAccount(bundle.getAccountId(), DefaultSubscriptionTestInitializer.DEFAULT_BUNDLE_KEY, true, internalCallContext);
@@ -100,8 +100,8 @@ public class TestUserApiCreate extends SubscriptionTestSuiteWithEmbeddedDB {
@Test(groups = "slow")
public void testCreateWithRequestedDate() throws SubscriptionBaseApiException {
- final DateTime init = clock.getUTCNow();
- final DateTime requestedDate = init.minusYears(1);
+ final LocalDate init = clock.getUTCToday();
+ final LocalDate requestedDate = init.minusYears(1);
final String productName = "Shotgun";
final BillingPeriod term = BillingPeriod.MONTHLY;
@@ -122,7 +122,7 @@ public class TestUserApiCreate extends SubscriptionTestSuiteWithEmbeddedDB {
final SubscriptionBaseEvent phaseEvent = events.get(1);
assertEquals(subscription.getBundleExternalKey(), bundle.getExternalKey());
- assertEquals(subscription.getStartDate(), requestedDate);
+ assertEquals(subscription.getStartDate(), requestedDate.toDateTime(accountData.getReferenceTime()));
assertListenerStatus();
@@ -143,7 +143,7 @@ public class TestUserApiCreate extends SubscriptionTestSuiteWithEmbeddedDB {
final BillingPeriod term = BillingPeriod.MONTHLY;
final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
- final DefaultSubscriptionBase subscription = testUtil.createSubscription(bundle, productName, term, planSetName, PhaseType.EVERGREEN, clock.getUTCNow());
+ final DefaultSubscriptionBase subscription = testUtil.createSubscription(bundle, productName, term, planSetName, PhaseType.EVERGREEN, null);
assertNotNull(subscription);
assertEquals(subscription.getBundleExternalKey(), bundle.getExternalKey());
@@ -171,7 +171,7 @@ public class TestUserApiCreate extends SubscriptionTestSuiteWithEmbeddedDB {
final BillingPeriod term = BillingPeriod.MONTHLY;
final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
- final DefaultSubscriptionBase subscription = testUtil.createSubscription(bundle, productName, term, planSetName, clock.getUTCNow());
+ final DefaultSubscriptionBase subscription = testUtil.createSubscription(bundle, productName, term, planSetName);
assertNotNull(subscription);
assertEquals(subscription.getBundleExternalKey(), bundle.getExternalKey());
@@ -215,7 +215,7 @@ public class TestUserApiCreate extends SubscriptionTestSuiteWithEmbeddedDB {
final String planSetName = "gunclubDiscount";
// CREATE SUBSCRIPTION
- DefaultSubscriptionBase subscription = testUtil.createSubscription(bundle, productName, term, planSetName, clock.getUTCNow());
+ DefaultSubscriptionBase subscription = testUtil.createSubscription(bundle, productName, term, planSetName);
assertNotNull(subscription);
PlanPhase currentPhase = subscription.getCurrentPhase();
@@ -252,22 +252,21 @@ public class TestUserApiCreate extends SubscriptionTestSuiteWithEmbeddedDB {
final BillingPeriod term = BillingPeriod.ANNUAL;
final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
- final DefaultSubscriptionBase subscription = testUtil.createSubscription(bundle, productName, term, planSetName, clock.getUTCNow());
+ final DefaultSubscriptionBase subscription = testUtil.createSubscription(bundle, productName, term, planSetName);
assertNotNull(subscription);
assertListenerStatus();
}
-
@Test(groups = "slow")
public void testCreateSubscriptionInTheFuture() throws SubscriptionBaseApiException {
- final DateTime init = clock.getUTCNow();
+ final LocalDate init = clock.getUTCToday();
final String productName = "Shotgun";
final BillingPeriod term = BillingPeriod.MONTHLY;
final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
- final DateTime futureCreationDate = init.plusDays(10);
+ final LocalDate futureCreationDate = init.plusDays(10);
DefaultSubscriptionBase subscription = testUtil.createSubscription(bundle, productName, term, planSetName, futureCreationDate);
assertListenerStatus();
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiError.java b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiError.java
index 3393722..4f0a717 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiError.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiError.java
@@ -23,7 +23,6 @@ import javax.annotation.Nullable;
import org.joda.time.DateTime;
import org.joda.time.Interval;
import org.killbill.billing.ErrorCode;
-import org.killbill.billing.api.FlakyRetryAnalyzer;
import org.killbill.billing.api.TestApiListener.NextEvent;
import org.killbill.billing.catalog.api.BillingActionPolicy;
import org.killbill.billing.catalog.api.BillingPeriod;
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/engine/dao/TestSubscriptionDao.java b/subscription/src/test/java/org/killbill/billing/subscription/engine/dao/TestSubscriptionDao.java
index 253bf4a..5933ced 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/engine/dao/TestSubscriptionDao.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/engine/dao/TestSubscriptionDao.java
@@ -40,7 +40,8 @@ import org.killbill.billing.subscription.events.SubscriptionBaseEvent;
import org.killbill.billing.subscription.events.user.ApiEventBuilder;
import org.killbill.billing.subscription.events.user.ApiEventCreate;
import org.killbill.billing.util.UUIDs;
-import org.killbill.billing.util.glue.KillbillApiAopModule;
+import org.killbill.billing.util.entity.dao.DBRouterUntyped;
+import org.killbill.commons.profiling.Profiling.WithProfilingCallback;
import org.mockito.Mockito;
import org.skife.jdbi.v2.IDBI;
import org.testng.Assert;
@@ -175,15 +176,10 @@ public class TestSubscriptionDao extends SubscriptionTestSuiteWithEmbeddedDB {
assertEquals(result5.get(0).getExternalKey(), bundle2.getExternalKey());
assertEquals(result5.get(1).getExternalKey(), bundle2.getExternalKey());
assertEquals(result5.get(2).getExternalKey(), bundle2.getExternalKey());
-
-
}
@Test(groups = "slow")
- public void testDirtyFlag() throws SubscriptionBaseApiException {
- // @BeforeMethod created the account
- KillbillApiAopModule.resetDirtyDBFlag();
-
+ public void testDirtyFlag() throws Throwable {
final IDBI dbiSpy = Mockito.spy(dbi);
final IDBI roDbiSpy = Mockito.spy(roDbi);
final SubscriptionDao subscriptionDao = new DefaultSubscriptionDao(dbiSpy,
@@ -198,34 +194,49 @@ public class TestSubscriptionDao extends SubscriptionTestSuiteWithEmbeddedDB {
Mockito.verify(dbiSpy, Mockito.times(0)).open();
Mockito.verify(roDbiSpy, Mockito.times(0)).open();
- Assert.assertEquals(subscriptionDao.getSubscriptionBundleForAccount(accountId, internalCallContext).size(), 0);
- Mockito.verify(dbiSpy, Mockito.times(0)).open();
- Mockito.verify(roDbiSpy, Mockito.times(1)).open();
-
- Assert.assertEquals(subscriptionDao.getSubscriptionBundleForAccount(accountId, internalCallContext).size(), 0);
- Mockito.verify(dbiSpy, Mockito.times(0)).open();
- Mockito.verify(roDbiSpy, Mockito.times(2)).open();
-
- final String externalKey = UUID.randomUUID().toString();
- final DateTime startDate = clock.getUTCNow();
- final DateTime createdDate = startDate.plusSeconds(10);
- final DefaultSubscriptionBaseBundle bundleDef = new DefaultSubscriptionBaseBundle(externalKey, accountId, startDate, startDate, createdDate, createdDate);
- final SubscriptionBaseBundle bundle = subscriptionDao.createSubscriptionBundle(bundleDef, catalog, false, internalCallContext);
- Mockito.verify(dbiSpy, Mockito.times(1)).open();
- Mockito.verify(roDbiSpy, Mockito.times(2)).open();
-
- Assert.assertEquals(subscriptionDao.getSubscriptionBundleForAccount(accountId, internalCallContext).size(), 1);
- Mockito.verify(dbiSpy, Mockito.times(2)).open();
- Mockito.verify(roDbiSpy, Mockito.times(2)).open();
-
- Assert.assertEquals(subscriptionDao.getSubscriptionBundleForAccount(accountId, internalCallContext).size(), 1);
- Mockito.verify(dbiSpy, Mockito.times(3)).open();
- Mockito.verify(roDbiSpy, Mockito.times(2)).open();
-
- KillbillApiAopModule.resetDirtyDBFlag();
-
- Assert.assertEquals(subscriptionDao.getSubscriptionBundleForAccount(accountId, internalCallContext).size(), 1);
- Mockito.verify(dbiSpy, Mockito.times(3)).open();
- Mockito.verify(roDbiSpy, Mockito.times(3)).open();
+ // @BeforeMethod created the account
+ DBRouterUntyped.withRODBIAllowed(true,
+ new WithProfilingCallback<Object, Throwable>() {
+ @Override
+ public Object execute() throws Throwable {
+ Assert.assertEquals(subscriptionDao.getSubscriptionBundleForAccount(accountId, internalCallContext).size(), 0);
+ Mockito.verify(dbiSpy, Mockito.times(0)).open();
+ Mockito.verify(roDbiSpy, Mockito.times(1)).open();
+
+ Assert.assertEquals(subscriptionDao.getSubscriptionBundleForAccount(accountId, internalCallContext).size(), 0);
+ Mockito.verify(dbiSpy, Mockito.times(0)).open();
+ Mockito.verify(roDbiSpy, Mockito.times(2)).open();
+
+ final String externalKey = UUID.randomUUID().toString();
+ final DateTime startDate = clock.getUTCNow();
+ final DateTime createdDate = startDate.plusSeconds(10);
+ final DefaultSubscriptionBaseBundle bundleDef = new DefaultSubscriptionBaseBundle(externalKey, accountId, startDate, startDate, createdDate, createdDate);
+ final SubscriptionBaseBundle bundle = subscriptionDao.createSubscriptionBundle(bundleDef, catalog, false, internalCallContext);
+ Mockito.verify(dbiSpy, Mockito.times(1)).open();
+ Mockito.verify(roDbiSpy, Mockito.times(2)).open();
+
+ Assert.assertEquals(subscriptionDao.getSubscriptionBundleForAccount(accountId, internalCallContext).size(), 1);
+ Mockito.verify(dbiSpy, Mockito.times(2)).open();
+ Mockito.verify(roDbiSpy, Mockito.times(2)).open();
+
+ Assert.assertEquals(subscriptionDao.getSubscriptionBundleForAccount(accountId, internalCallContext).size(), 1);
+ Mockito.verify(dbiSpy, Mockito.times(3)).open();
+ Mockito.verify(roDbiSpy, Mockito.times(2)).open();
+
+ return null;
+ }
+ });
+
+ DBRouterUntyped.withRODBIAllowed(true,
+ new WithProfilingCallback<Object, Throwable>() {
+ @Override
+ public Object execute() {
+ Assert.assertEquals(subscriptionDao.getSubscriptionBundleForAccount(accountId, internalCallContext).size(), 1);
+ Mockito.verify(dbiSpy, Mockito.times(3)).open();
+ Mockito.verify(roDbiSpy, Mockito.times(3)).open();
+
+ return null;
+ }
+ });
}
}
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/glue/TestDefaultSubscriptionModuleWithEmbeddedDB.java b/subscription/src/test/java/org/killbill/billing/subscription/glue/TestDefaultSubscriptionModuleWithEmbeddedDB.java
index 7918876..0d2cb09 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/glue/TestDefaultSubscriptionModuleWithEmbeddedDB.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/glue/TestDefaultSubscriptionModuleWithEmbeddedDB.java
@@ -23,8 +23,10 @@ import org.killbill.billing.account.glue.DefaultAccountModule;
import org.killbill.billing.platform.api.KillbillConfigSource;
import org.killbill.billing.subscription.engine.dao.MockSubscriptionDaoSql;
import org.killbill.billing.subscription.engine.dao.SubscriptionDao;
+import org.killbill.billing.util.glue.AuditModule;
import org.killbill.billing.util.glue.CustomFieldModule;
import org.killbill.billing.util.glue.NonEntityDaoModule;
+import org.killbill.billing.util.glue.TestUtilModuleNoDB;
public class TestDefaultSubscriptionModuleWithEmbeddedDB extends TestDefaultSubscriptionModule {
@@ -41,13 +43,10 @@ public class TestDefaultSubscriptionModuleWithEmbeddedDB extends TestDefaultSubs
protected void configure() {
install(new GuicyKillbillTestWithEmbeddedDBModule(configSource));
-
install(new NonEntityDaoModule(configSource));
-
install(new CustomFieldModule(configSource));
-
install(new DefaultAccountModule(configSource));
-
+ install(new AuditModule(configSource));
super.configure();
}
}
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteNoDB.java b/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteNoDB.java
index 5daabf1..3535366 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteNoDB.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteNoDB.java
@@ -91,8 +91,7 @@ public class SubscriptionTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
protected SubscriptionConfig config;
@Inject
protected SubscriptionDao dao;
- @Inject
- protected ClockMock clock;
+
@Inject
protected BusService busService;
@@ -127,6 +126,10 @@ public class SubscriptionTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
@BeforeClass(groups = "fast")
public void beforeClass() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
final Injector g = Guice.createInjector(Stage.PRODUCTION, new TestDefaultSubscriptionModuleNoDB(configSource));
g.injectMembers(this);
@@ -136,6 +139,10 @@ public class SubscriptionTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
@BeforeMethod(groups = "fast")
public void beforeMethod() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
// CLEANUP ALL DB TABLES OR IN MEMORY STRUCTURES
((MockSubscriptionDaoMemory) dao).reset();
@@ -168,6 +175,10 @@ public class SubscriptionTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
@AfterMethod(groups = "fast")
public void afterMethod() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
subscriptionTestInitializer.stopTestFramework(testListener, busService, subscriptionBaseService);
}
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteWithEmbeddedDB.java b/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteWithEmbeddedDB.java
index fb54d56..b22a8c2 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteWithEmbeddedDB.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteWithEmbeddedDB.java
@@ -81,8 +81,6 @@ public class SubscriptionTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuiteW
@Inject
protected SubscriptionDao dao;
@Inject
- protected ClockMock clock;
- @Inject
protected BusService busService;
@Inject
protected AddonUtils addonUtils;
@@ -107,6 +105,10 @@ public class SubscriptionTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuiteW
@BeforeClass(groups = "slow")
public void beforeClass() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
final Injector g = Guice.createInjector(Stage.PRODUCTION, new TestDefaultSubscriptionModuleWithEmbeddedDB(configSource));
g.injectMembers(this);
}
@@ -114,6 +116,10 @@ public class SubscriptionTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuiteW
@Override
@BeforeMethod(groups = "slow")
public void beforeMethod() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
super.beforeMethod();
subscriptionTestInitializer.startTestFramework(testListener, clock, busService, subscriptionBaseService);
@@ -126,6 +132,10 @@ public class SubscriptionTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuiteW
@AfterMethod(groups = "slow")
public void afterMethod() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
subscriptionTestInitializer.stopTestFramework(testListener, busService, subscriptionBaseService);
}
tenant/pom.xml 2(+1 -1)
diff --git a/tenant/pom.xml b/tenant/pom.xml
index 3936329..710dd0e 100644
--- a/tenant/pom.xml
+++ b/tenant/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.19.11-SNAPSHOT</version>
+ <version>0.19.13-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-tenant</artifactId>
diff --git a/tenant/src/test/java/org/killbill/billing/tenant/TenantTestSuiteNoDB.java b/tenant/src/test/java/org/killbill/billing/tenant/TenantTestSuiteNoDB.java
index dac6c5d..5d15d41 100644
--- a/tenant/src/test/java/org/killbill/billing/tenant/TenantTestSuiteNoDB.java
+++ b/tenant/src/test/java/org/killbill/billing/tenant/TenantTestSuiteNoDB.java
@@ -28,6 +28,10 @@ public class TenantTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
@BeforeClass(groups = "fast")
protected void beforeClass() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
final Injector injector = Guice.createInjector(new TestTenantModuleNoDB(configSource));
injector.injectMembers(this);
}
diff --git a/tenant/src/test/java/org/killbill/billing/tenant/TenantTestSuiteWithEmbeddedDb.java b/tenant/src/test/java/org/killbill/billing/tenant/TenantTestSuiteWithEmbeddedDb.java
index 45486dc..1c5b407 100644
--- a/tenant/src/test/java/org/killbill/billing/tenant/TenantTestSuiteWithEmbeddedDb.java
+++ b/tenant/src/test/java/org/killbill/billing/tenant/TenantTestSuiteWithEmbeddedDb.java
@@ -55,6 +55,10 @@ public class TenantTestSuiteWithEmbeddedDb extends GuicyKillbillTestSuiteWithEmb
@BeforeClass(groups = "slow")
protected void beforeClass() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
final Injector injector = Guice.createInjector(new TestTenantModuleWithEmbeddedDB(configSource));
injector.injectMembers(this);
}
usage/pom.xml 2(+1 -1)
diff --git a/usage/pom.xml b/usage/pom.xml
index 2858c6c..056478a 100644
--- a/usage/pom.xml
+++ b/usage/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.19.11-SNAPSHOT</version>
+ <version>0.19.13-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-usage</artifactId>
diff --git a/usage/src/main/java/org/killbill/billing/usage/dao/DefaultRolledUpUsageDao.java b/usage/src/main/java/org/killbill/billing/usage/dao/DefaultRolledUpUsageDao.java
index ea5aabd..c8987b8 100644
--- a/usage/src/main/java/org/killbill/billing/usage/dao/DefaultRolledUpUsageDao.java
+++ b/usage/src/main/java/org/killbill/billing/usage/dao/DefaultRolledUpUsageDao.java
@@ -27,43 +27,42 @@ import javax.inject.Named;
import org.joda.time.LocalDate;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.util.entity.dao.DBRouter;
import org.skife.jdbi.v2.IDBI;
import static org.killbill.billing.util.glue.IDBISetup.MAIN_RO_IDBI_NAMED;
public class DefaultRolledUpUsageDao implements RolledUpUsageDao {
- private final RolledUpUsageSqlDao rolledUpUsageSqlDao;
- private final RolledUpUsageSqlDao roRolledUpUsageSqlDao;
+ private final DBRouter<RolledUpUsageSqlDao> dbRouter;
@Inject
public DefaultRolledUpUsageDao(final IDBI dbi, @Named(MAIN_RO_IDBI_NAMED) final IDBI roDbi) {
- this.rolledUpUsageSqlDao = dbi.onDemand(RolledUpUsageSqlDao.class);
- this.roRolledUpUsageSqlDao = roDbi.onDemand(RolledUpUsageSqlDao.class);
+ this.dbRouter = new DBRouter<RolledUpUsageSqlDao>(dbi, roDbi, RolledUpUsageSqlDao.class);
}
@Override
public void record(final Iterable<RolledUpUsageModelDao> usages, final InternalCallContext context) {
- rolledUpUsageSqlDao.create(usages, context);
+ dbRouter.onDemand(false).create(usages, context);
}
@Override
public Boolean recordsWithTrackingIdExist(final UUID subscriptionId, final String trackingId, final InternalTenantContext context) {
- return rolledUpUsageSqlDao.recordsWithTrackingIdExist(subscriptionId, trackingId, context) != null;
+ return dbRouter.onDemand(false).recordsWithTrackingIdExist(subscriptionId, trackingId, context) != null;
}
@Override
public List<RolledUpUsageModelDao> getUsageForSubscription(final UUID subscriptionId, final LocalDate startDate, final LocalDate endDate, final String unitType, final InternalTenantContext context) {
- return roRolledUpUsageSqlDao.getUsageForSubscription(subscriptionId, startDate.toDate(), endDate.toDate(), unitType, context);
+ return dbRouter.onDemand(true).getUsageForSubscription(subscriptionId, startDate.toDate(), endDate.toDate(), unitType, context);
}
@Override
public List<RolledUpUsageModelDao> getAllUsageForSubscription(final UUID subscriptionId, final LocalDate startDate, final LocalDate endDate, final InternalTenantContext context) {
- return roRolledUpUsageSqlDao.getAllUsageForSubscription(subscriptionId, startDate.toDate(), endDate.toDate(), context);
+ return dbRouter.onDemand(true).getAllUsageForSubscription(subscriptionId, startDate.toDate(), endDate.toDate(), context);
}
@Override
public List<RolledUpUsageModelDao> getRawUsageForAccount(final LocalDate startDate, final LocalDate endDate, final InternalTenantContext context) {
- return roRolledUpUsageSqlDao.getRawUsageForAccount(startDate.toDate(), endDate.toDate(), context);
+ return dbRouter.onDemand(true).getRawUsageForAccount(startDate.toDate(), endDate.toDate(), context);
}
}
diff --git a/usage/src/test/java/org/killbill/billing/usage/glue/TestUsageModuleWithEmbeddedDB.java b/usage/src/test/java/org/killbill/billing/usage/glue/TestUsageModuleWithEmbeddedDB.java
index e34500f..cf71e96 100644
--- a/usage/src/test/java/org/killbill/billing/usage/glue/TestUsageModuleWithEmbeddedDB.java
+++ b/usage/src/test/java/org/killbill/billing/usage/glue/TestUsageModuleWithEmbeddedDB.java
@@ -21,6 +21,7 @@ package org.killbill.billing.usage.glue;
import org.killbill.billing.GuicyKillbillTestWithEmbeddedDBModule;
import org.killbill.billing.account.glue.DefaultAccountModule;
import org.killbill.billing.platform.api.KillbillConfigSource;
+import org.killbill.billing.util.glue.AuditModule;
import org.killbill.billing.util.glue.CacheModule;
import org.killbill.billing.util.glue.ConfigModule;
import org.killbill.billing.util.glue.NonEntityDaoModule;
@@ -40,5 +41,6 @@ public class TestUsageModuleWithEmbeddedDB extends TestUsageModule {
install(new ConfigModule(configSource));
install(new NonEntityDaoModule(configSource));
install(new DefaultAccountModule(configSource));
+ install(new AuditModule(configSource));
}
}
diff --git a/usage/src/test/java/org/killbill/billing/usage/UsageTestSuiteNoDB.java b/usage/src/test/java/org/killbill/billing/usage/UsageTestSuiteNoDB.java
index 2b58cea..54b2182 100644
--- a/usage/src/test/java/org/killbill/billing/usage/UsageTestSuiteNoDB.java
+++ b/usage/src/test/java/org/killbill/billing/usage/UsageTestSuiteNoDB.java
@@ -30,15 +30,11 @@ public class UsageTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
@BeforeClass(groups = "fast")
protected void beforeClass() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
final Injector injector = Guice.createInjector(new TestUsageModuleNoDB(configSource));
injector.injectMembers(this);
}
-
- @BeforeMethod(groups = "fast")
- public void beforeMethod() {
- }
-
- @AfterMethod(groups = "fast")
- public void afterMethod() {
- }
}
diff --git a/usage/src/test/java/org/killbill/billing/usage/UsageTestSuiteWithEmbeddedDB.java b/usage/src/test/java/org/killbill/billing/usage/UsageTestSuiteWithEmbeddedDB.java
index 81030d7..32c5aea 100644
--- a/usage/src/test/java/org/killbill/billing/usage/UsageTestSuiteWithEmbeddedDB.java
+++ b/usage/src/test/java/org/killbill/billing/usage/UsageTestSuiteWithEmbeddedDB.java
@@ -35,6 +35,10 @@ public class UsageTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuiteWithEmbe
@BeforeClass(groups = "slow")
protected void beforeClass() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
final Injector injector = Guice.createInjector(new TestUsageModuleWithEmbeddedDB(configSource));
injector.injectMembers(this);
}
util/pom.xml 2(+1 -1)
diff --git a/util/pom.xml b/util/pom.xml
index bb6f930..09fe09a 100644
--- a/util/pom.xml
+++ b/util/pom.xml
@@ -21,7 +21,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.19.11-SNAPSHOT</version>
+ <version>0.19.13-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-util</artifactId>
diff --git a/util/src/main/java/org/killbill/billing/util/audit/dao/AuditDao.java b/util/src/main/java/org/killbill/billing/util/audit/dao/AuditDao.java
index 6f19906..49892fe 100644
--- a/util/src/main/java/org/killbill/billing/util/audit/dao/AuditDao.java
+++ b/util/src/main/java/org/killbill/billing/util/audit/dao/AuditDao.java
@@ -22,8 +22,10 @@ import java.util.UUID;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.util.api.AuditLevel;
import org.killbill.billing.util.audit.AuditLog;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
import org.killbill.billing.util.audit.DefaultAccountAuditLogs;
import org.killbill.billing.util.audit.DefaultAccountAuditLogsForObjectType;
+import org.killbill.billing.util.dao.HistorySqlDao;
import org.killbill.billing.util.dao.TableName;
public interface AuditDao {
@@ -35,4 +37,6 @@ public interface AuditDao {
public DefaultAccountAuditLogsForObjectType getAuditLogsForAccountRecordId(TableName tableName, AuditLevel auditLevel, InternalTenantContext context);
public List<AuditLog> getAuditLogsForId(TableName tableName, UUID objectId, AuditLevel auditLevel, InternalTenantContext context);
+
+ List<AuditLogWithHistory> getAuditLogsWithHistoryForId(HistorySqlDao sqlDao, TableName tableName, UUID objectId, AuditLevel auditLevel, InternalTenantContext context);
}
diff --git a/util/src/main/java/org/killbill/billing/util/audit/dao/DefaultAuditDao.java b/util/src/main/java/org/killbill/billing/util/audit/dao/DefaultAuditDao.java
index 520309d..b5e08a8 100644
--- a/util/src/main/java/org/killbill/billing/util/audit/dao/DefaultAuditDao.java
+++ b/util/src/main/java/org/killbill/billing/util/audit/dao/DefaultAuditDao.java
@@ -31,16 +31,21 @@ import org.killbill.billing.ObjectType;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.util.api.AuditLevel;
import org.killbill.billing.util.audit.AuditLog;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
import org.killbill.billing.util.audit.ChangeType;
import org.killbill.billing.util.audit.DefaultAccountAuditLogs;
import org.killbill.billing.util.audit.DefaultAccountAuditLogsForObjectType;
import org.killbill.billing.util.audit.DefaultAuditLog;
+import org.killbill.billing.util.audit.DefaultAuditLogWithHistory;
import org.killbill.billing.util.cache.CacheControllerDispatcher;
import org.killbill.billing.util.callcontext.InternalCallContextFactory;
+import org.killbill.billing.util.dao.EntityHistoryModelDao;
+import org.killbill.billing.util.dao.HistorySqlDao;
import org.killbill.billing.util.dao.NonEntityDao;
import org.killbill.billing.util.dao.NonEntitySqlDao;
import org.killbill.billing.util.dao.RecordIdIdMappings;
import org.killbill.billing.util.dao.TableName;
+import org.killbill.billing.util.entity.dao.DBRouter;
import org.killbill.billing.util.entity.dao.EntitySqlDao;
import org.killbill.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
import org.killbill.billing.util.entity.dao.EntitySqlDaoTransactionalJdbiWrapper;
@@ -49,6 +54,7 @@ import org.killbill.clock.Clock;
import org.skife.jdbi.v2.IDBI;
import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;
@@ -57,18 +63,18 @@ import static org.killbill.billing.util.glue.IDBISetup.MAIN_RO_IDBI_NAMED;
public class DefaultAuditDao implements AuditDao {
- private final NonEntitySqlDao roNonEntitySqlDao;
+ private final DBRouter<NonEntitySqlDao> dbRouter;
private final EntitySqlDaoTransactionalJdbiWrapper transactionalSqlDao;
@Inject
public DefaultAuditDao(final IDBI dbi, @Named(MAIN_RO_IDBI_NAMED) final IDBI roDbi, final Clock clock, final CacheControllerDispatcher cacheControllerDispatcher, final NonEntityDao nonEntityDao, final InternalCallContextFactory internalCallContextFactory) {
- this.roNonEntitySqlDao = roDbi.onDemand(NonEntitySqlDao.class);
+ this.dbRouter = new DBRouter<NonEntitySqlDao>(dbi, roDbi, NonEntitySqlDao.class);
this.transactionalSqlDao = new EntitySqlDaoTransactionalJdbiWrapper(dbi, roDbi, clock, cacheControllerDispatcher, nonEntityDao, internalCallContextFactory);
}
@Override
public DefaultAccountAuditLogs getAuditLogsForAccountRecordId(final AuditLevel auditLevel, final InternalTenantContext context) {
- final UUID accountId = roNonEntitySqlDao.getIdFromObject(context.getAccountRecordId(), TableName.ACCOUNT.getTableName());
+ final UUID accountId = dbRouter.onDemand(true).getIdFromObject(context.getAccountRecordId(), TableName.ACCOUNT.getTableName());
// Lazy evaluate records to minimize the memory footprint (these can yield a lot of results)
// We usually always want to wrap our queries in an EntitySqlDaoTransactionWrapper... except here.
@@ -110,6 +116,7 @@ public class DefaultAuditDao implements AuditDao {
// For tables without history, e.g. TENANT, originalTableNameForHistoryTableName will be null
final TableName originalTableNameForHistoryTableName = findTableNameForHistoryTableName(input.getTableName());
+ final NonEntitySqlDao nonEntitySqlDao = dbRouter.onDemand(true);
final ObjectType objectType;
final UUID auditedEntityId;
if (originalTableNameForHistoryTableName != null) {
@@ -118,19 +125,19 @@ public class DefaultAuditDao implements AuditDao {
if (historyRecordIdIdsCache.get(originalTableNameForHistoryTableName) == null) {
if (TableName.ACCOUNT.equals(originalTableNameForHistoryTableName)) {
- final Iterable<RecordIdIdMappings> mappings = roNonEntitySqlDao.getHistoryRecordIdIdMappingsForAccountsTable(originalTableNameForHistoryTableName.getTableName(),
- input.getTableName().getTableName(),
- tenantContext);
+ final Iterable<RecordIdIdMappings> mappings = nonEntitySqlDao.getHistoryRecordIdIdMappingsForAccountsTable(originalTableNameForHistoryTableName.getTableName(),
+ input.getTableName().getTableName(),
+ tenantContext);
historyRecordIdIdsCache.put(originalTableNameForHistoryTableName, RecordIdIdMappings.toMap(mappings));
} else if (TableName.TAG_DEFINITIONS.equals(originalTableNameForHistoryTableName)) {
- final Iterable<RecordIdIdMappings> mappings = roNonEntitySqlDao.getHistoryRecordIdIdMappingsForTablesWithoutAccountRecordId(originalTableNameForHistoryTableName.getTableName(),
- input.getTableName().getTableName(),
- tenantContext);
+ final Iterable<RecordIdIdMappings> mappings = nonEntitySqlDao.getHistoryRecordIdIdMappingsForTablesWithoutAccountRecordId(originalTableNameForHistoryTableName.getTableName(),
+ input.getTableName().getTableName(),
+ tenantContext);
historyRecordIdIdsCache.put(originalTableNameForHistoryTableName, RecordIdIdMappings.toMap(mappings));
} else {
- final Iterable<RecordIdIdMappings> mappings = roNonEntitySqlDao.getHistoryRecordIdIdMappings(originalTableNameForHistoryTableName.getTableName(),
- input.getTableName().getTableName(),
- tenantContext);
+ final Iterable<RecordIdIdMappings> mappings = nonEntitySqlDao.getHistoryRecordIdIdMappings(originalTableNameForHistoryTableName.getTableName(),
+ input.getTableName().getTableName(),
+ tenantContext);
historyRecordIdIdsCache.put(originalTableNameForHistoryTableName, RecordIdIdMappings.toMap(mappings));
}
@@ -141,8 +148,8 @@ public class DefaultAuditDao implements AuditDao {
objectType = input.getTableName().getObjectType();
if (recordIdIdsCache.get(input.getTableName()) == null) {
- final Iterable<RecordIdIdMappings> mappings = roNonEntitySqlDao.getRecordIdIdMappings(input.getTableName().getTableName(),
- tenantContext);
+ final Iterable<RecordIdIdMappings> mappings = nonEntitySqlDao.getRecordIdIdMappings(input.getTableName().getTableName(),
+ tenantContext);
recordIdIdsCache.put(input.getTableName(), RecordIdIdMappings.toMap(mappings));
}
@@ -173,8 +180,45 @@ public class DefaultAuditDao implements AuditDao {
}
}
+ @Override
+ public List<AuditLogWithHistory> getAuditLogsWithHistoryForId(final HistorySqlDao transactional, final TableName tableName, final UUID objectId, final AuditLevel auditLevel, final InternalTenantContext context) {
+ final TableName historyTableName = tableName.getHistoryTableName();
+ if (historyTableName == null) {
+ throw new IllegalStateException("History table shouldn't be null for " + tableName);
+ }
+
+ return transactionalSqlDao.execute(true, new EntitySqlDaoTransactionWrapper<List<AuditLogWithHistory>>() {
+ @Override
+ public List<AuditLogWithHistory> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
+ final Long targetRecordId = dbRouter.onDemand(true).getRecordIdFromObject(objectId.toString(), tableName.getTableName());
+ final List<EntityHistoryModelDao> objectHistory = transactional.getHistoryForTargetRecordId(targetRecordId, context);
+
+ return ImmutableList.<AuditLogWithHistory>copyOf(Collections2.transform(entitySqlDaoWrapperFactory.become(EntitySqlDao.class).getAuditLogsViaHistoryForTargetRecordId(historyTableName.name(),
+ historyTableName.getTableName().toLowerCase(),
+ targetRecordId,
+ context),
+ new Function<AuditLogModelDao, AuditLogWithHistory>() {
+ @Override
+ public AuditLogWithHistory apply(final AuditLogModelDao inputAuditLog) {
+ EntityHistoryModelDao historyEntity = null;
+ if (objectHistory != null) {
+ for (final EntityHistoryModelDao history : objectHistory) {
+ if (history.getHistoryRecordId().equals(inputAuditLog.getTargetRecordId())) {
+ historyEntity = history;
+ break;
+ }
+ }
+ }
+
+ return new DefaultAuditLogWithHistory((historyEntity == null ? null : historyEntity.getEntity()), inputAuditLog, tableName.getObjectType(), objectId);
+ }
+ }));
+ }
+ });
+ }
+
private List<AuditLog> doGetAuditLogsForId(final TableName tableName, final UUID objectId, final AuditLevel auditLevel, final InternalTenantContext context) {
- final Long recordId = roNonEntitySqlDao.getRecordIdFromObject(objectId.toString(), tableName.getTableName());
+ final Long recordId = dbRouter.onDemand(true).getRecordIdFromObject(objectId.toString(), tableName.getTableName());
if (recordId == null) {
return ImmutableList.<AuditLog>of();
} else {
@@ -188,7 +232,7 @@ public class DefaultAuditDao implements AuditDao {
throw new IllegalStateException("History table shouldn't be null for " + tableName);
}
- final Long targetRecordId = roNonEntitySqlDao.getRecordIdFromObject(objectId.toString(), tableName.getTableName());
+ final Long targetRecordId = dbRouter.onDemand(true).getRecordIdFromObject(objectId.toString(), tableName.getTableName());
final List<AuditLog> allAuditLogs = transactionalSqlDao.execute(true, new EntitySqlDaoTransactionWrapper<List<AuditLog>>() {
@Override
public List<AuditLog> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
diff --git a/util/src/main/java/org/killbill/billing/util/audit/DefaultAuditLogWithHistory.java b/util/src/main/java/org/killbill/billing/util/audit/DefaultAuditLogWithHistory.java
new file mode 100644
index 0000000..1fa7929
--- /dev/null
+++ b/util/src/main/java/org/killbill/billing/util/audit/DefaultAuditLogWithHistory.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.util.audit;
+
+import java.util.UUID;
+
+import org.killbill.billing.ObjectType;
+import org.killbill.billing.util.audit.dao.AuditLogModelDao;
+import org.killbill.billing.util.entity.Entity;
+
+public class DefaultAuditLogWithHistory<E extends Entity> extends DefaultAuditLog implements AuditLogWithHistory<E> {
+
+ private E entity;
+
+ public DefaultAuditLogWithHistory(final E entity,final AuditLogModelDao auditLogModelDao, final ObjectType objectType, final UUID auditedEntityId) {
+ super(auditLogModelDao, objectType, auditedEntityId);
+ this.entity = entity;
+ }
+
+ @Override
+ public E getEntity() {
+ return this.entity;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("DefaultAuditLogWithHistory{");
+ sb.append("auditLog=").append(super.toString());
+ sb.append(", entity=").append(entity);
+ sb.append('}');
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ if (!super.equals(o)) {
+ return false;
+ }
+
+ final DefaultAuditLogWithHistory<E> that = (DefaultAuditLogWithHistory<E>) o;
+
+ if (entity != null ? !entity.equals(that.entity) : that.entity != null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ result = 31 * result + (entity != null ? entity.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/util/src/main/java/org/killbill/billing/util/broadcast/dao/DefaultBroadcastDao.java b/util/src/main/java/org/killbill/billing/util/broadcast/dao/DefaultBroadcastDao.java
index 834aeee..c2c4e5a 100644
--- a/util/src/main/java/org/killbill/billing/util/broadcast/dao/DefaultBroadcastDao.java
+++ b/util/src/main/java/org/killbill/billing/util/broadcast/dao/DefaultBroadcastDao.java
@@ -22,6 +22,7 @@ import java.util.List;
import javax.inject.Inject;
import javax.inject.Named;
+import org.killbill.billing.util.entity.dao.DBRouter;
import org.skife.jdbi.v2.Handle;
import org.skife.jdbi.v2.IDBI;
import org.skife.jdbi.v2.TransactionCallback;
@@ -31,18 +32,16 @@ import static org.killbill.billing.util.glue.IDBISetup.MAIN_RO_IDBI_NAMED;
public class DefaultBroadcastDao implements BroadcastDao {
- private final IDBI dbi;
- private final IDBI roDbi;
+ private final DBRouter<BroadcastSqlDao> dbRouter;
@Inject
public DefaultBroadcastDao(final IDBI dbi, @Named(MAIN_RO_IDBI_NAMED) final IDBI roDbi) {
- this.dbi = dbi;
- this.roDbi = roDbi;
+ this.dbRouter = new DBRouter<BroadcastSqlDao>(dbi, roDbi, BroadcastSqlDao.class);
}
@Override
public void create(final BroadcastModelDao broadcastModelDao) {
- dbi.inTransaction(new TransactionCallback<Void>() {
+ dbRouter.inTransaction(false, new TransactionCallback<Void>() {
@Override
public Void inTransaction(final Handle handle, final TransactionStatus status) throws Exception {
final BroadcastSqlDao sqlDao = handle.attach(BroadcastSqlDao.class);
@@ -54,7 +53,7 @@ public class DefaultBroadcastDao implements BroadcastDao {
@Override
public List<BroadcastModelDao> getLatestEntriesFrom(final Long recordId) {
- return roDbi.inTransaction(new TransactionCallback<List<BroadcastModelDao>>() {
+ return dbRouter.inTransaction(true, new TransactionCallback<List<BroadcastModelDao>>() {
@Override
public List<BroadcastModelDao> inTransaction(final Handle handle, final TransactionStatus status) throws Exception {
final BroadcastSqlDao sqlDao = handle.attach(BroadcastSqlDao.class);
@@ -65,7 +64,7 @@ public class DefaultBroadcastDao implements BroadcastDao {
@Override
public BroadcastModelDao getLatestEntry() {
- return roDbi.inTransaction(new TransactionCallback<BroadcastModelDao>() {
+ return dbRouter.inTransaction(true, new TransactionCallback<BroadcastModelDao>() {
@Override
public BroadcastModelDao inTransaction(final Handle handle, final TransactionStatus status) throws Exception {
final BroadcastSqlDao sqlDao = handle.attach(BroadcastSqlDao.class);
diff --git a/util/src/main/java/org/killbill/billing/util/cache/AuditLogCacheLoader.java b/util/src/main/java/org/killbill/billing/util/cache/AuditLogCacheLoader.java
index 875920d..7274eab 100644
--- a/util/src/main/java/org/killbill/billing/util/cache/AuditLogCacheLoader.java
+++ b/util/src/main/java/org/killbill/billing/util/cache/AuditLogCacheLoader.java
@@ -28,6 +28,7 @@ import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.util.audit.dao.AuditLogModelDao;
import org.killbill.billing.util.cache.Cachable.CacheType;
import org.killbill.billing.util.dao.AuditSqlDao;
+import org.killbill.billing.util.entity.dao.DBRouter;
import org.skife.jdbi.v2.IDBI;
import static org.killbill.billing.util.glue.IDBISetup.MAIN_RO_IDBI_NAMED;
@@ -35,12 +36,12 @@ import static org.killbill.billing.util.glue.IDBISetup.MAIN_RO_IDBI_NAMED;
@Singleton
public class AuditLogCacheLoader extends BaseCacheLoader<String, List<AuditLogModelDao>> {
- private final AuditSqlDao roAuditSqlDao;
+ private final DBRouter<AuditSqlDao> dbRouter;
@Inject
- public AuditLogCacheLoader(@Named(MAIN_RO_IDBI_NAMED) final IDBI roDbi) {
+ public AuditLogCacheLoader(final IDBI dbi, @Named(MAIN_RO_IDBI_NAMED) final IDBI roDbi) {
super();
- this.roAuditSqlDao = roDbi.onDemand(AuditSqlDao.class);
+ this.dbRouter = new DBRouter<AuditSqlDao>(dbi, roDbi, AuditSqlDao.class);
}
@Override
@@ -55,6 +56,6 @@ public class AuditLogCacheLoader extends BaseCacheLoader<String, List<AuditLogMo
final Long targetRecordId = (Long) args[1];
final InternalTenantContext internalTenantContext = (InternalTenantContext) args[2];
- return roAuditSqlDao.getAuditLogsForTargetRecordId(tableName, targetRecordId, internalTenantContext);
+ return dbRouter.onDemand(true).getAuditLogsForTargetRecordId(tableName, targetRecordId, internalTenantContext);
}
}
diff --git a/util/src/main/java/org/killbill/billing/util/callcontext/InternalCallContextFactory.java b/util/src/main/java/org/killbill/billing/util/callcontext/InternalCallContextFactory.java
index 41158ff..ec78672 100644
--- a/util/src/main/java/org/killbill/billing/util/callcontext/InternalCallContextFactory.java
+++ b/util/src/main/java/org/killbill/billing/util/callcontext/InternalCallContextFactory.java
@@ -247,7 +247,7 @@ public class InternalCallContextFactory {
// If tenant id is null, this will default to the default tenant record id (multi-tenancy disabled)
final Long tenantRecordId = getTenantRecordIdSafe(context);
populateMDCContext(context.getUserToken(), null, tenantRecordId);
- return new InternalCallContext(tenantRecordId, context, clock.getUTCNow());
+ return new InternalCallContext(tenantRecordId, context, context.getCreatedDate());
}
// Used when we need to re-hydrate the callcontext with the account_record_id (when creating the account)
@@ -256,7 +256,7 @@ public class InternalCallContextFactory {
final DateTimeZone fixedOffsetTimeZone = immutableAccountData.getFixedOffsetTimeZone();
final DateTime referenceTime = immutableAccountData.getReferenceTime();
populateMDCContext(context.getUserToken(), accountRecordId, context.getTenantRecordId());
- return new InternalCallContext(context, accountRecordId, fixedOffsetTimeZone, referenceTime, clock.getUTCNow());
+ return new InternalCallContext(context, accountRecordId, fixedOffsetTimeZone, referenceTime, context.getCreatedDate());
}
// Used during the account creation transaction (account not visible outside of the transaction yet)
@@ -265,12 +265,12 @@ public class InternalCallContextFactory {
final DateTimeZone fixedOffsetTimeZone = AccountDateTimeUtils.getFixedOffsetTimeZone(accountModelDao);
final DateTime referenceTime = accountModelDao.getReferenceTime();
populateMDCContext(context.getUserToken(), accountRecordId, context.getTenantRecordId());
- return new InternalCallContext(context, accountRecordId, fixedOffsetTimeZone, referenceTime, clock.getUTCNow());
+ return new InternalCallContext(context, accountRecordId, fixedOffsetTimeZone, referenceTime, context.getCreatedDate());
}
public InternalCallContext createInternalCallContext(final DateTimeZone fixedOffsetTimeZone, final DateTime referenceTime, final Long accountRecordId, final InternalCallContext context) {
populateMDCContext(context.getUserToken(), accountRecordId, context.getTenantRecordId());
- return new InternalCallContext(context, accountRecordId, fixedOffsetTimeZone, referenceTime, clock.getUTCNow());
+ return new InternalCallContext(context, accountRecordId, fixedOffsetTimeZone, referenceTime, context.getCreatedDate());
}
private InternalCallContext createInternalCallContext(final UUID objectId, final ObjectType objectType, final String userName,
diff --git a/util/src/main/java/org/killbill/billing/util/dao/DefaultNonEntityDao.java b/util/src/main/java/org/killbill/billing/util/dao/DefaultNonEntityDao.java
index 5e8c63d..41aa896 100644
--- a/util/src/main/java/org/killbill/billing/util/dao/DefaultNonEntityDao.java
+++ b/util/src/main/java/org/killbill/billing/util/dao/DefaultNonEntityDao.java
@@ -29,6 +29,7 @@ import org.killbill.billing.util.cache.CacheController;
import org.killbill.billing.util.cache.CacheControllerDispatcher;
import org.killbill.billing.util.cache.CacheLoaderArgument;
import org.killbill.billing.util.callcontext.InternalCallContextFactory;
+import org.killbill.billing.util.entity.dao.DBRouter;
import org.killbill.commons.profiling.Profiling;
import org.killbill.commons.profiling.Profiling.WithProfilingCallback;
import org.killbill.commons.profiling.ProfilingFeature.ProfilingFeatureType;
@@ -42,13 +43,13 @@ import static org.killbill.billing.util.glue.IDBISetup.MAIN_RO_IDBI_NAMED;
public class DefaultNonEntityDao implements NonEntityDao {
- private final NonEntitySqlDao roNonEntitySqlDao;
+ private final DBRouter<NonEntitySqlDao> dbRouter;
private final WithCaching<String, Long> withCachingObjectId;
private final WithCaching<String, UUID> withCachingRecordId;
@Inject
- public DefaultNonEntityDao(@Named(MAIN_RO_IDBI_NAMED) final IDBI roDbi) {
- this.roNonEntitySqlDao = roDbi.onDemand(NonEntitySqlDao.class);
+ public DefaultNonEntityDao(final IDBI dbi, @Named(MAIN_RO_IDBI_NAMED) final IDBI roDbi) {
+ this.dbRouter = new DBRouter<NonEntitySqlDao>(dbi, roDbi, NonEntitySqlDao.class);
this.withCachingObjectId = new WithCaching<String, Long>();
this.withCachingRecordId = new WithCaching<String, UUID>();
}
@@ -69,7 +70,7 @@ public class DefaultNonEntityDao implements NonEntityDao {
return withCachingObjectId.withCaching(new OperationRetrieval<Long>() {
@Override
public Long doRetrieve(final ObjectType objectType) {
- final NonEntitySqlDao inTransactionNonEntitySqlDao = handle == null ? roNonEntitySqlDao : SqlObjectBuilder.attach(handle, NonEntitySqlDao.class);
+ final NonEntitySqlDao inTransactionNonEntitySqlDao = handle == null ? dbRouter.onDemand(true) : SqlObjectBuilder.attach(handle, NonEntitySqlDao.class);
return inTransactionNonEntitySqlDao.getRecordIdFromObject(objectId.toString(), tableName.getTableName());
}
}, objectId.toString(), objectType, tableName, cache);
@@ -89,7 +90,7 @@ public class DefaultNonEntityDao implements NonEntityDao {
return withCachingObjectId.withCaching(new OperationRetrieval<Long>() {
@Override
public Long doRetrieve(final ObjectType objectType) {
- final NonEntitySqlDao inTransactionNonEntitySqlDao = handle == null ? roNonEntitySqlDao : SqlObjectBuilder.attach(handle, NonEntitySqlDao.class);
+ final NonEntitySqlDao inTransactionNonEntitySqlDao = handle == null ? dbRouter.onDemand(true) : SqlObjectBuilder.attach(handle, NonEntitySqlDao.class);
switch (tableName) {
case TENANT:
@@ -121,7 +122,7 @@ public class DefaultNonEntityDao implements NonEntityDao {
return withCachingObjectId.withCaching(new OperationRetrieval<Long>() {
@Override
public Long doRetrieve(final ObjectType objectType) {
- final NonEntitySqlDao inTransactionNonEntitySqlDao = handle == null ? roNonEntitySqlDao : SqlObjectBuilder.attach(handle, NonEntitySqlDao.class);
+ final NonEntitySqlDao inTransactionNonEntitySqlDao = handle == null ? dbRouter.onDemand(true) : SqlObjectBuilder.attach(handle, NonEntitySqlDao.class);
switch (tableName) {
case TENANT:
@@ -153,7 +154,7 @@ public class DefaultNonEntityDao implements NonEntityDao {
return withCachingRecordId.withCaching(new OperationRetrieval<UUID>() {
@Override
public UUID doRetrieve(final ObjectType objectType) {
- final NonEntitySqlDao inTransactionNonEntitySqlDao = handle == null ? roNonEntitySqlDao : SqlObjectBuilder.attach(handle, NonEntitySqlDao.class);
+ final NonEntitySqlDao inTransactionNonEntitySqlDao = handle == null ? dbRouter.onDemand(true) : SqlObjectBuilder.attach(handle, NonEntitySqlDao.class);
return inTransactionNonEntitySqlDao.getIdFromObject(recordId, tableName.getTableName());
}
}, String.valueOf(recordId), objectType, tableName, cache);
@@ -167,7 +168,7 @@ public class DefaultNonEntityDao implements NonEntityDao {
@Override
public Long retrieveHistoryTargetRecordId(@Nullable final Long recordId, final TableName tableName) {
- return roNonEntitySqlDao.getHistoryTargetRecordId(recordId, tableName.getTableName());
+ return dbRouter.onDemand(true).getHistoryTargetRecordId(recordId, tableName.getTableName());
}
private interface OperationRetrieval<TypeOut> {
diff --git a/util/src/main/java/org/killbill/billing/util/dao/EntityHistoryModelDao.java b/util/src/main/java/org/killbill/billing/util/dao/EntityHistoryModelDao.java
index 447f132..9fee7a9 100644
--- a/util/src/main/java/org/killbill/billing/util/dao/EntityHistoryModelDao.java
+++ b/util/src/main/java/org/killbill/billing/util/dao/EntityHistoryModelDao.java
@@ -31,16 +31,18 @@ public class EntityHistoryModelDao<M extends EntityModelDao<E>, E extends Entity
private Long targetRecordId;
private M entity;
private ChangeType changeType;
+ private Long historyRecordId;
- public EntityHistoryModelDao(final UUID id, final M src, final Long targetRecordId, final ChangeType type, final DateTime createdDate) {
+ public EntityHistoryModelDao(final UUID id, final M src, final Long targetRecordId, final ChangeType type, final Long historyRecordId, final DateTime createdDate) {
super(id, createdDate, createdDate);
this.changeType = type;
this.targetRecordId = targetRecordId;
this.entity = src;
+ this.historyRecordId = historyRecordId;
}
- public EntityHistoryModelDao(final M src, final Long targetRecordId, final ChangeType type, final DateTime createdDate) {
- this(UUIDs.randomUUID(), src, targetRecordId, type, createdDate);
+ public EntityHistoryModelDao(final M src, final Long targetRecordId, final ChangeType type, final Long historyRecordId, final DateTime createdDate) {
+ this(UUIDs.randomUUID(), src, targetRecordId, type, historyRecordId, createdDate);
}
public ChangeType getChangeType() {
@@ -66,4 +68,12 @@ public class EntityHistoryModelDao<M extends EntityModelDao<E>, E extends Entity
public void setChangeType(final ChangeType changeType) {
this.changeType = changeType;
}
+
+ public Long getHistoryRecordId() {
+ return historyRecordId;
+ }
+
+ public void setHistoryRecordId(final Long historyRecordId) {
+ this.historyRecordId = historyRecordId;
+ }
}
diff --git a/util/src/main/java/org/killbill/billing/util/dao/EntityHistoryModelDaoMapper.java b/util/src/main/java/org/killbill/billing/util/dao/EntityHistoryModelDaoMapper.java
index 5ff331d..49fd117 100644
--- a/util/src/main/java/org/killbill/billing/util/dao/EntityHistoryModelDaoMapper.java
+++ b/util/src/main/java/org/killbill/billing/util/dao/EntityHistoryModelDaoMapper.java
@@ -44,6 +44,9 @@ public class EntityHistoryModelDaoMapper<M extends EntityModelDao<E>, E extends
final String changeType = r.getString("change_type");
final DateTime createdDate = getDateTime(r, "created_date");
+ // preserve history record id, as it is needed to reference it with audit log
+ final long historyRecordId = r.getLong("history_record_id");
+
final M entityModelDao = entityMapper.map(index, r, ctx);
// Hack -- remove the id as it is the history id, not the entity id
@@ -55,6 +58,6 @@ public class EntityHistoryModelDaoMapper<M extends EntityModelDao<E>, E extends
((EntityModelDaoBase) entityModelDao).setAccountRecordId(targetRecordId);
}
- return new EntityHistoryModelDao(id, entityModelDao, targetRecordId, ChangeType.valueOf(changeType), createdDate);
+ return new EntityHistoryModelDao(id, entityModelDao, targetRecordId, ChangeType.valueOf(changeType), historyRecordId, createdDate);
}
}
diff --git a/util/src/main/java/org/killbill/billing/util/dao/HistorySqlDao.java b/util/src/main/java/org/killbill/billing/util/dao/HistorySqlDao.java
index e1f7cee..23c763e 100644
--- a/util/src/main/java/org/killbill/billing/util/dao/HistorySqlDao.java
+++ b/util/src/main/java/org/killbill/billing/util/dao/HistorySqlDao.java
@@ -21,6 +21,7 @@ package org.killbill.billing.util.dao;
import java.util.List;
import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.util.entity.Entity;
import org.killbill.billing.util.entity.dao.EntityModelDao;
import org.skife.jdbi.v2.sqlobject.Bind;
@@ -33,7 +34,7 @@ public interface HistorySqlDao<M extends EntityModelDao<E>, E extends Entity> {
@SqlQuery
public List<EntityHistoryModelDao<M, E>> getHistoryForTargetRecordId(@Bind("targetRecordId") final long targetRecordId,
- @SmartBindBean InternalCallContext context);
+ @SmartBindBean InternalTenantContext context);
@SqlUpdate
@GetGeneratedKeys
public Long addHistoryFromTransaction(@EntityHistoryBinder EntityHistoryModelDao<M, E> history,
diff --git a/util/src/main/java/org/killbill/billing/util/entity/dao/DBRouterUntyped.java b/util/src/main/java/org/killbill/billing/util/entity/dao/DBRouterUntyped.java
new file mode 100644
index 0000000..abd2b4b
--- /dev/null
+++ b/util/src/main/java/org/killbill/billing/util/entity/dao/DBRouterUntyped.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.util.entity.dao;
+
+import org.killbill.commons.profiling.Profiling.WithProfilingCallback;
+import org.skife.jdbi.v2.Handle;
+import org.skife.jdbi.v2.IDBI;
+import org.skife.jdbi.v2.TransactionCallback;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.annotations.VisibleForTesting;
+
+import static org.killbill.billing.util.entity.dao.DBRouterUntyped.THREAD_STATE.RO_ALLOWED;
+import static org.killbill.billing.util.entity.dao.DBRouterUntyped.THREAD_STATE.RW_ONLY;
+
+public class DBRouterUntyped {
+
+ private static final Logger logger = LoggerFactory.getLogger(DBRouterUntyped.class);
+
+ private static final ThreadLocal<THREAD_STATE> CURRENT_THREAD_STATE = new ThreadLocal<THREAD_STATE>() {
+ @Override
+ public THREAD_STATE initialValue() {
+ return RW_ONLY;
+ }
+ };
+
+ protected final IDBI dbi;
+ protected final IDBI roDbi;
+
+ public DBRouterUntyped(final IDBI dbi, final IDBI roDbi) {
+ this.dbi = dbi;
+ this.roDbi = roDbi;
+ }
+
+ public static Object withRODBIAllowed(final boolean allowRODBI,
+ final WithProfilingCallback<Object, Throwable> callback) throws Throwable {
+ final THREAD_STATE currentState = getCurrentState();
+ CURRENT_THREAD_STATE.set(allowRODBI ? RO_ALLOWED : RW_ONLY);
+
+ try {
+ return callback.execute();
+ } finally {
+ CURRENT_THREAD_STATE.set(currentState);
+ }
+ }
+
+ @VisibleForTesting
+ public static THREAD_STATE getCurrentState() {
+ return CURRENT_THREAD_STATE.get();
+ }
+
+ boolean shouldUseRODBI(final boolean requestedRO) {
+ if (requestedRO) {
+ if (isRODBIAllowed()) {
+ logger.debug("Using RO DBI");
+ return true;
+ } else {
+ // Redirect to the rw instance, to work-around any replication delay
+ logger.debug("RO DBI requested, but thread state is {}, using RW DBI", getCurrentState());
+ return false;
+ }
+ } else {
+ // Disable RO DBI for future calls in this thread
+ disallowRODBI();
+ logger.debug("Using RW DBI");
+ return false;
+ }
+ }
+
+ private boolean isRODBIAllowed() {
+ return getCurrentState() == RO_ALLOWED;
+ }
+
+ private void disallowRODBI() {
+ CURRENT_THREAD_STATE.set(RW_ONLY);
+ }
+
+ public Handle getHandle(final boolean requestedRO) {
+ if (shouldUseRODBI(requestedRO)) {
+ return roDbi.open();
+ } else {
+ return dbi.open();
+ }
+ }
+
+ public <T> T onDemand(final boolean requestedRO, final Class<T> sqlObjectType) {
+ if (shouldUseRODBI(requestedRO)) {
+ return roDbi.onDemand(sqlObjectType);
+ } else {
+ return dbi.onDemand(sqlObjectType);
+ }
+ }
+
+ public <T> T inTransaction(final boolean requestedRO, final TransactionCallback<T> callback) {
+ if (shouldUseRODBI(requestedRO)) {
+ return roDbi.inTransaction(callback);
+ } else {
+ return dbi.inTransaction(callback);
+ }
+ }
+
+ public enum THREAD_STATE {
+ // Advisory that RO DBI can be used
+ RO_ALLOWED,
+ // Dirty flag, calls must go to RW DBI
+ RW_ONLY
+ }
+}
diff --git a/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoTransactionalJdbiWrapper.java b/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoTransactionalJdbiWrapper.java
index a6f6287..a651330 100644
--- a/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoTransactionalJdbiWrapper.java
+++ b/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoTransactionalJdbiWrapper.java
@@ -24,7 +24,6 @@ import org.killbill.billing.util.cache.CacheControllerDispatcher;
import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.dao.NonEntityDao;
import org.killbill.billing.util.entity.Entity;
-import org.killbill.billing.util.glue.KillbillApiAopModule;
import org.killbill.clock.Clock;
import org.skife.jdbi.v2.Handle;
import org.skife.jdbi.v2.IDBI;
@@ -38,10 +37,9 @@ import org.slf4j.LoggerFactory;
*/
public class EntitySqlDaoTransactionalJdbiWrapper {
- private final Logger logger = LoggerFactory.getLogger(EntitySqlDaoTransactionalJdbiWrapper.class);
+ private static final Logger logger = LoggerFactory.getLogger(EntitySqlDaoTransactionalJdbiWrapper.class);
- private final IDBI dbi;
- private final IDBI roDbi;
+ private final DBRouterUntyped dbRouter;
private final Clock clock;
private final CacheControllerDispatcher cacheControllerDispatcher;
private final NonEntityDao nonEntityDao;
@@ -49,12 +47,11 @@ public class EntitySqlDaoTransactionalJdbiWrapper {
public EntitySqlDaoTransactionalJdbiWrapper(final IDBI dbi, final IDBI roDbi, final Clock clock, final CacheControllerDispatcher cacheControllerDispatcher,
final NonEntityDao nonEntityDao, final InternalCallContextFactory internalCallContextFactory) {
- this.dbi = dbi;
- this.roDbi = roDbi;
this.clock = clock;
this.cacheControllerDispatcher = cacheControllerDispatcher;
this.nonEntityDao = nonEntityDao;
this.internalCallContextFactory = internalCallContextFactory;
+ this.dbRouter = new DBRouterUntyped(dbi, roDbi);
}
public <M extends EntityModelDao> void populateCaches(final M refreshedEntity) {
@@ -89,39 +86,21 @@ public class EntitySqlDaoTransactionalJdbiWrapper {
*/
public <ReturnType> ReturnType execute(final boolean requestedRO, final EntitySqlDaoTransactionWrapper<ReturnType> entitySqlDaoTransactionWrapper) {
final String debugInfo = logger.isDebugEnabled() ? getDebugInfo() : null;
- final boolean ro = shouldUseRODBI(requestedRO, debugInfo);
- final String debugPrefix = ro ? "RO" : "RW";
- final Handle handle = ro ? roDbi.open() : dbi.open();
- logger.debug("[{}] DBI handle created, transaction: {}", debugPrefix, debugInfo);
+ final Handle handle = dbRouter.getHandle(requestedRO);
+ logger.debug("DBI handle created, transaction: {}", debugInfo);
try {
final EntitySqlDao<EntityModelDao<Entity>, Entity> entitySqlDao = handle.attach(InitialEntitySqlDao.class);
// The transaction isolation level is now set at the pool level: this avoids 3 roundtrips for each transaction
// Note that if the pool isn't used (tests or PostgreSQL), the transaction level will depend on the DB configuration
//return entitySqlDao.inTransaction(TransactionIsolationLevel.READ_COMMITTED, new JdbiTransaction<ReturnType, EntityModelDao<Entity>, Entity>(handle, entitySqlDaoTransactionWrapper));
- logger.debug("[{}] Starting transaction {}", debugPrefix, debugInfo);
+ logger.debug("Starting transaction {}", debugInfo);
final ReturnType returnType = entitySqlDao.inTransaction(new JdbiTransaction<ReturnType, EntityModelDao<Entity>, Entity>(handle, entitySqlDaoTransactionWrapper));
- logger.debug("[{}] Exiting transaction {}, returning {}", debugPrefix, debugInfo, returnType);
+ logger.debug("Exiting transaction {}, returning {}", debugInfo, returnType);
return returnType;
} finally {
handle.close();
- logger.debug("[{}] DBI handle closed, transaction: {}", debugPrefix, debugInfo);
- }
- }
-
- private boolean shouldUseRODBI(final boolean requestedRO, final String debugInfo) {
- if (!requestedRO) {
- KillbillApiAopModule.setDirtyDBFlag();
- logger.debug("[RW] Dirty flag set, transaction: {}", debugInfo);
- return false;
- } else {
- if (KillbillApiAopModule.getDirtyDBFlag()) {
- // Redirect to the rw instance, to work-around any replication delay
- logger.debug("[RW] RO DBI handle requested, but dirty flag set, transaction: {}", debugInfo);
- return false;
- } else {
- return true;
- }
+ logger.debug("DBI handle closed, transaction: {}", debugInfo);
}
}
@@ -130,12 +109,7 @@ public class EntitySqlDaoTransactionalJdbiWrapper {
// to send bus events, record notifications where we need to keep the Connection through the jDBI Handle.
//
public <M extends EntityModelDao<E>, E extends Entity, T extends EntitySqlDao<M, E>> T onDemandForStreamingResults(final Class<T> sqlObjectType) {
- final String debugInfo = logger.isDebugEnabled() ? getDebugInfo() : null;
- if (shouldUseRODBI(true, debugInfo)) {
- return roDbi.onDemand(sqlObjectType);
- } else {
- return dbi.onDemand(sqlObjectType);
- }
+ return dbRouter.onDemand(true, sqlObjectType);
}
/**
diff --git a/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoWrapperInvocationHandler.java b/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoWrapperInvocationHandler.java
index 907af4b..195d41d 100644
--- a/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoWrapperInvocationHandler.java
+++ b/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoWrapperInvocationHandler.java
@@ -504,7 +504,7 @@ public class EntitySqlDaoWrapperInvocationHandler<S extends EntitySqlDao<M, E>,
}
private Long insertHistory(final Long entityRecordId, final M entityModelDao, final ChangeType changeType, final InternalCallContext context) {
- final EntityHistoryModelDao<M, E> history = new EntityHistoryModelDao<M, E>(entityModelDao, entityRecordId, changeType, clock.getUTCNow());
+ final EntityHistoryModelDao<M, E> history = new EntityHistoryModelDao<M, E>(entityModelDao, entityRecordId, changeType, null, context.getCreatedDate());
final Long recordId = sqlDao.addHistoryFromTransaction(history, context);
printSQLWarnings();
return recordId;
@@ -512,7 +512,7 @@ public class EntitySqlDaoWrapperInvocationHandler<S extends EntitySqlDao<M, E>,
private void insertAudits(final TableName tableName, final M entityModelDao, final Long entityRecordId, final Long historyRecordId, final ChangeType changeType, final InternalCallContext contextMaybeWithoutAccountRecordId) {
final TableName destinationTableName = MoreObjects.firstNonNull(tableName.getHistoryTableName(), tableName);
- final EntityAudit audit = new EntityAudit(destinationTableName, historyRecordId, changeType, clock.getUTCNow());
+ final EntityAudit audit = new EntityAudit(destinationTableName, historyRecordId, changeType, contextMaybeWithoutAccountRecordId.getCreatedDate());
final InternalCallContext context;
// Populate the account record id when creating the account record
diff --git a/util/src/main/java/org/killbill/billing/util/glue/KillbillApiAopModule.java b/util/src/main/java/org/killbill/billing/util/glue/KillbillApiAopModule.java
index 80abe6d..92cc22e 100644
--- a/util/src/main/java/org/killbill/billing/util/glue/KillbillApiAopModule.java
+++ b/util/src/main/java/org/killbill/billing/util/glue/KillbillApiAopModule.java
@@ -22,11 +22,9 @@ import java.lang.reflect.Method;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.killbill.billing.KillbillApi;
-import org.killbill.billing.callcontext.DefaultTenantContext;
-import org.killbill.billing.callcontext.InternalCallContext;
-import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.osgi.api.ROTenantContext;
import org.killbill.billing.util.callcontext.CallContext;
+import org.killbill.billing.util.entity.dao.DBRouterUntyped;
import org.killbill.commons.profiling.Profiling;
import org.killbill.commons.profiling.Profiling.WithProfilingCallback;
import org.killbill.commons.profiling.ProfilingFeature.ProfilingFeatureType;
@@ -40,16 +38,26 @@ import com.google.inject.matcher.Matchers;
public class KillbillApiAopModule extends AbstractModule {
private static final Logger logger = LoggerFactory.getLogger(KillbillApiAopModule.class);
- private static final ThreadLocal<Boolean> perThreadDirtyDBFlag = new ThreadLocal<Boolean>();
- static {
- // Set an initial value
- resetDirtyDBFlag();
- }
+ private static final Matcher<Method> SYNTHETIC_METHOD_MATCHER = new Matcher<Method>() {
+ @Override
+ public boolean matches(final Method method) {
+ return method.isSynthetic();
+ }
+
+ @Override
+ public Matcher<Method> and(final Matcher<? super Method> other) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Matcher<Method> or(final Matcher<? super Method> other) {
+ throw new UnsupportedOperationException();
+ }
+ };
@Override
protected void configure() {
-
bindInterceptor(Matchers.subclassesOf(KillbillApi.class),
Matchers.not(SYNTHETIC_METHOD_MATCHER),
new ProfilingMethodInterceptor());
@@ -57,34 +65,41 @@ public class KillbillApiAopModule extends AbstractModule {
public static class ProfilingMethodInterceptor implements MethodInterceptor {
- private final Profiling prof = new Profiling<Object, Throwable>();
+ private final Profiling<Object, Throwable> prof = new Profiling<Object, Throwable>();
@Override
public Object invoke(final MethodInvocation invocation) throws Throwable {
- return prof.executeWithProfiling(ProfilingFeatureType.API, invocation.getMethod().getName(), new WithProfilingCallback() {
+ final WithProfilingCallback<Object, Throwable> callback = new WithProfilingCallback<Object, Throwable>() {
@Override
public Object execute() throws Throwable {
- final boolean useRODBIfAvailable = shouldUseRODBIfAvailable(invocation);
- if (!useRODBIfAvailable) {
- setDirtyDBFlag();
- }
-
- try {
- logger.debug("Entering API call {}, arguments: {}", invocation.getMethod(), invocation.getArguments());
- final Object proceed = invocation.proceed();
- logger.debug("Exiting API call {}, returning: {}", invocation.getMethod(), proceed);
- return proceed;
- } finally {
- resetDirtyDBFlag();
- }
+ logger.debug("Entering API call {}, arguments: {}", invocation.getMethod(), invocation.getArguments());
+ final Object proceed = invocation.proceed();
+ logger.debug("Exiting API call {}, returning: {}", invocation.getMethod(), proceed);
+ return proceed;
}
- });
+ };
+
+ if (forcedRODBI(invocation)) {
+ return prof.executeWithProfiling(ProfilingFeatureType.API,
+ invocation.getMethod().getName(),
+ new WithProfilingCallback<Object, Throwable>() {
+ @Override
+ public Object execute() throws Throwable {
+ return DBRouterUntyped.withRODBIAllowed(true, callback);
+ }
+ });
+ } else {
+ return prof.executeWithProfiling(ProfilingFeatureType.API,
+ invocation.getMethod().getName(),
+ callback);
+ }
}
- private boolean shouldUseRODBIfAvailable(final MethodInvocation invocation) {
- // Verify if the flag is already set for re-entrant calls
- if (getDirtyDBFlag()) {
- return false;
+ private boolean forcedRODBI(final MethodInvocation invocation) {
+ // Snowflakes from server filters
+ final boolean safeROOperations = "getTenantByApiKey".equals(invocation.getMethod().getName()) || "login".equals(invocation.getMethod().getName());
+ if (safeROOperations) {
+ return true;
}
final Object[] arguments = invocation.getArguments();
@@ -92,21 +107,11 @@ public class KillbillApiAopModule extends AbstractModule {
return false;
}
- // Snowflakes from server filters
- final boolean safeROOperations = "getTenantByApiKey".equals(invocation.getMethod().getName()) || "login".equals(invocation.getMethod().getName());
- if (safeROOperations) {
- return true;
- }
-
for (int i = arguments.length - 1; i >= 0; i--) {
final Object argument = arguments[i];
- // DefaultTenantContext belongs to killbill-internal-api and shouldn't be used by plugins
- final boolean fromJAXRS = argument instanceof DefaultTenantContext && !(argument instanceof CallContext);
- // Kill Bill internal re-entrant calls
- final boolean fromInternalAPIs = argument instanceof InternalTenantContext && !(argument instanceof InternalCallContext);
// RO DB explicitly requested by a plugin
final boolean pluginRequestROInstance = argument instanceof ROTenantContext && !(argument instanceof CallContext);
- if (fromJAXRS || fromInternalAPIs || pluginRequestROInstance) {
+ if (pluginRequestROInstance) {
return true;
}
}
@@ -114,33 +119,4 @@ public class KillbillApiAopModule extends AbstractModule {
return false;
}
}
-
- public static void setDirtyDBFlag() {
- perThreadDirtyDBFlag.set(true);
- }
-
- public static void resetDirtyDBFlag() {
- perThreadDirtyDBFlag.set(false);
- }
-
- public static Boolean getDirtyDBFlag() {
- return perThreadDirtyDBFlag.get() == Boolean.TRUE;
- }
-
- private static final Matcher<Method> SYNTHETIC_METHOD_MATCHER = new Matcher<Method>() {
- @Override
- public boolean matches(final Method method) {
- return method.isSynthetic();
- }
-
- @Override
- public Matcher<Method> and(final Matcher<? super Method> other) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public Matcher<Method> or(final Matcher<? super Method> other) {
- throw new UnsupportedOperationException();
- }
- };
}
diff --git a/util/src/main/java/org/killbill/billing/util/nodes/dao/DefaultNodeInfoDao.java b/util/src/main/java/org/killbill/billing/util/nodes/dao/DefaultNodeInfoDao.java
index 892c1db..82d1fd4 100644
--- a/util/src/main/java/org/killbill/billing/util/nodes/dao/DefaultNodeInfoDao.java
+++ b/util/src/main/java/org/killbill/billing/util/nodes/dao/DefaultNodeInfoDao.java
@@ -22,6 +22,7 @@ import java.util.List;
import javax.inject.Named;
+import org.killbill.billing.util.entity.dao.DBRouter;
import org.killbill.clock.Clock;
import org.skife.jdbi.v2.Handle;
import org.skife.jdbi.v2.IDBI;
@@ -34,20 +35,18 @@ import static org.killbill.billing.util.glue.IDBISetup.MAIN_RO_IDBI_NAMED;
public class DefaultNodeInfoDao implements NodeInfoDao {
- private final IDBI dbi;
- private final IDBI roDbi;
+ private final DBRouter<NodeInfoSqlDao> dbRouter;
private final Clock clock;
@Inject
public DefaultNodeInfoDao(final IDBI dbi, @Named(MAIN_RO_IDBI_NAMED) final IDBI roDbi, final Clock clock) {
- this.dbi = dbi;
- this.roDbi = roDbi;
+ this.dbRouter = new DBRouter<NodeInfoSqlDao>(dbi, roDbi, NodeInfoSqlDao.class);
this.clock = clock;
}
@Override
public void create(final NodeInfoModelDao nodeInfoModelDao) {
- dbi.inTransaction(new TransactionCallback<Void>() {
+ dbRouter.inTransaction(false, new TransactionCallback<Void>() {
@Override
public Void inTransaction(final Handle handle, final TransactionStatus status) throws Exception {
final NodeInfoSqlDao sqlDao = handle.attach(NodeInfoSqlDao.class);
@@ -62,7 +61,7 @@ public class DefaultNodeInfoDao implements NodeInfoDao {
@Override
public void updateNodeInfo(final String nodeName, final String nodeInfo) {
- dbi.inTransaction(new TransactionCallback<Void>() {
+ dbRouter.inTransaction(false, new TransactionCallback<Void>() {
@Override
public Void inTransaction(final Handle handle, final TransactionStatus status) throws Exception {
final NodeInfoSqlDao sqlDao = handle.attach(NodeInfoSqlDao.class);
@@ -75,7 +74,7 @@ public class DefaultNodeInfoDao implements NodeInfoDao {
@Override
public void delete(final String nodeName) {
- dbi.inTransaction(new TransactionCallback<Void>() {
+ dbRouter.inTransaction(false, new TransactionCallback<Void>() {
@Override
public Void inTransaction(final Handle handle, final TransactionStatus status) throws Exception {
final NodeInfoSqlDao sqlDao = handle.attach(NodeInfoSqlDao.class);
@@ -87,7 +86,7 @@ public class DefaultNodeInfoDao implements NodeInfoDao {
@Override
public List<NodeInfoModelDao> getAll() {
- return roDbi.inTransaction(new TransactionCallback<List<NodeInfoModelDao>>() {
+ return dbRouter.inTransaction(true, new TransactionCallback<List<NodeInfoModelDao>>() {
@Override
public List<NodeInfoModelDao> inTransaction(final Handle handle, final TransactionStatus status) throws Exception {
final NodeInfoSqlDao sqlDao = handle.attach(NodeInfoSqlDao.class);
@@ -98,7 +97,7 @@ public class DefaultNodeInfoDao implements NodeInfoDao {
@Override
public NodeInfoModelDao getByNodeName(final String nodeName) {
- return roDbi.inTransaction(new TransactionCallback<NodeInfoModelDao>() {
+ return dbRouter.inTransaction(true, new TransactionCallback<NodeInfoModelDao>() {
@Override
public NodeInfoModelDao inTransaction(final Handle handle, final TransactionStatus status) throws Exception {
final NodeInfoSqlDao sqlDao = handle.attach(NodeInfoSqlDao.class);
diff --git a/util/src/main/java/org/killbill/billing/util/security/shiro/dao/JDBCSessionDao.java b/util/src/main/java/org/killbill/billing/util/security/shiro/dao/JDBCSessionDao.java
index bf5c760..8bd91cc 100644
--- a/util/src/main/java/org/killbill/billing/util/security/shiro/dao/JDBCSessionDao.java
+++ b/util/src/main/java/org/killbill/billing/util/security/shiro/dao/JDBCSessionDao.java
@@ -29,6 +29,7 @@ import javax.inject.Named;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.eis.CachingSessionDAO;
import org.killbill.billing.util.UUIDs;
+import org.killbill.billing.util.entity.dao.DBRouter;
import org.skife.jdbi.v2.IDBI;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -42,27 +43,25 @@ public class JDBCSessionDao extends CachingSessionDAO {
private static final Logger log = LoggerFactory.getLogger(JDBCSessionDao.class);
- private final JDBCSessionSqlDao jdbcSessionSqlDao;
- private final JDBCSessionSqlDao roJdbcSessionSqlDao;
+ private final DBRouter<JDBCSessionSqlDao> dbRouter;
private final Cache<Serializable, Boolean> noUpdateSessionsCache = CacheBuilder.newBuilder().expireAfterWrite(5, TimeUnit.SECONDS).build();
@Inject
public JDBCSessionDao(final IDBI dbi, @Named(MAIN_RO_IDBI_NAMED) final IDBI roDbi) {
- this.jdbcSessionSqlDao = dbi.onDemand(JDBCSessionSqlDao.class);
- this.roJdbcSessionSqlDao = roDbi.onDemand(JDBCSessionSqlDao.class);
+ this.dbRouter = new DBRouter<JDBCSessionSqlDao>(dbi, roDbi, JDBCSessionSqlDao.class);
}
@Override
protected void doUpdate(final Session session) {
if (shouldUpdateSession(session)) {
- jdbcSessionSqlDao.update(new SessionModelDao(session));
+ dbRouter.onDemand(false).update(new SessionModelDao(session));
}
}
@Override
protected void doDelete(final Session session) {
- jdbcSessionSqlDao.delete(new SessionModelDao(session));
+ dbRouter.onDemand(false).delete(new SessionModelDao(session));
}
@Override
@@ -71,7 +70,7 @@ public class JDBCSessionDao extends CachingSessionDAO {
// See SessionModelDao#toSimpleSession for why we use toString()
final String sessionIdAsString = sessionId.toString();
assignSessionId(session, sessionIdAsString);
- jdbcSessionSqlDao.create(new SessionModelDao(session));
+ dbRouter.onDemand(false).create(new SessionModelDao(session));
// Make sure to return a String here as well, or Shiro will cache the Session with a UUID key
// while it is expecting String
return sessionIdAsString;
@@ -85,7 +84,7 @@ public class JDBCSessionDao extends CachingSessionDAO {
}
final String sessionIdString = sessionId.toString();
- final SessionModelDao sessionModelDao = roJdbcSessionSqlDao.read(sessionIdString);
+ final SessionModelDao sessionModelDao = dbRouter.onDemand(true).read(sessionIdString);
if (sessionModelDao == null) {
return null;
diff --git a/util/src/main/java/org/killbill/billing/util/validation/dao/DatabaseSchemaDao.java b/util/src/main/java/org/killbill/billing/util/validation/dao/DatabaseSchemaDao.java
index 2922ce2..210feba 100644
--- a/util/src/main/java/org/killbill/billing/util/validation/dao/DatabaseSchemaDao.java
+++ b/util/src/main/java/org/killbill/billing/util/validation/dao/DatabaseSchemaDao.java
@@ -24,6 +24,7 @@ import javax.annotation.Nullable;
import javax.inject.Named;
import javax.inject.Singleton;
+import org.killbill.billing.util.entity.dao.DBRouter;
import org.killbill.billing.util.validation.DefaultColumnInfo;
import org.skife.jdbi.v2.IDBI;
@@ -34,11 +35,11 @@ import static org.killbill.billing.util.glue.IDBISetup.MAIN_RO_IDBI_NAMED;
@Singleton
public class DatabaseSchemaDao {
- private final DatabaseSchemaSqlDao roDatabaseSchemaSqlDao;
+ private final DBRouter<DatabaseSchemaSqlDao> dbRouter;
@Inject
- public DatabaseSchemaDao(@Named(MAIN_RO_IDBI_NAMED) final IDBI roDbi) {
- this.roDatabaseSchemaSqlDao = roDbi.onDemand(DatabaseSchemaSqlDao.class);
+ public DatabaseSchemaDao(final IDBI dbi, @Named(MAIN_RO_IDBI_NAMED) final IDBI roDbi) {
+ this.dbRouter = new DBRouter<DatabaseSchemaSqlDao>(dbi, roDbi, DatabaseSchemaSqlDao.class);
}
public List<DefaultColumnInfo> getColumnInfoList() {
@@ -46,6 +47,6 @@ public class DatabaseSchemaDao {
}
public List<DefaultColumnInfo> getColumnInfoList(@Nullable final String schemaName) {
- return roDatabaseSchemaSqlDao.getSchemaInfo(schemaName);
+ return dbRouter.onDemand(true).getSchemaInfo(schemaName);
}
}
diff --git a/util/src/main/resources/org/killbill/billing/util/entity/dao/EntitySqlDao.sql.stg b/util/src/main/resources/org/killbill/billing/util/entity/dao/EntitySqlDao.sql.stg
index 26faf51..5b861e8 100644
--- a/util/src/main/resources/org/killbill/billing/util/entity/dao/EntitySqlDao.sql.stg
+++ b/util/src/main/resources/org/killbill/billing/util/entity/dao/EntitySqlDao.sql.stg
@@ -336,6 +336,7 @@ auditTableValues() ::= <<
getHistoryForTargetRecordId() ::= <<
select
<idField("")>
+, <prefix>record_id as history_record_id
, <historyTableFields("t.")>
<accountRecordIdFieldWithComma("t.")>
<tenantRecordIdFieldWithComma("t.")>
diff --git a/util/src/test/java/org/killbill/billing/api/AbortAfterFirstFailureListener.java b/util/src/test/java/org/killbill/billing/api/AbortAfterFirstFailureListener.java
index 69c1600..c8a8aab 100644
--- a/util/src/test/java/org/killbill/billing/api/AbortAfterFirstFailureListener.java
+++ b/util/src/test/java/org/killbill/billing/api/AbortAfterFirstFailureListener.java
@@ -50,9 +50,13 @@ public class AbortAfterFirstFailureListener implements IInvokedMethodListener {
}
if (testResult.getStatus() == ITestResult.FAILURE) {
- synchronized (this) {
- logger.warn("!!! Test failure, all other tests will be skipped: {} !!!", testResult);
- hasFailures = true;
+ // Don't skip other tests with the current test method is flaky
+ final boolean isFlakyTest = method.getTestMethod().getRetryAnalyzer() != null && method.getTestMethod().getRetryAnalyzer() instanceof FlakyRetryAnalyzer;
+ if (!isFlakyTest) {
+ synchronized (this) {
+ logger.warn("!!! Test failure, all other tests will be skipped: {} !!!", testResult);
+ hasFailures = true;
+ }
}
}
}
diff --git a/util/src/test/java/org/killbill/billing/callcontext/MutableCallContext.java b/util/src/test/java/org/killbill/billing/callcontext/MutableCallContext.java
new file mode 100644
index 0000000..725df20
--- /dev/null
+++ b/util/src/test/java/org/killbill/billing/callcontext/MutableCallContext.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.callcontext;
+
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.killbill.billing.util.callcontext.CallContext;
+import org.killbill.billing.util.callcontext.CallOrigin;
+import org.killbill.billing.util.callcontext.UserType;
+
+public class MutableCallContext implements CallContext {
+
+ private final CallContext delegate;
+ private DateTime createdDate;
+
+ public MutableCallContext(final MutableInternalCallContext internalCallContext) {
+ this.delegate = internalCallContext.toCallContext(null, null);
+ this.createdDate = delegate.getCreatedDate();
+ }
+
+ @Override
+ public UUID getUserToken() {
+ return delegate.getUserToken();
+ }
+
+ @Override
+ public String getUserName() {
+ return delegate.getUserName();
+ }
+
+ @Override
+ public CallOrigin getCallOrigin() {
+ return delegate.getCallOrigin();
+ }
+
+ @Override
+ public UserType getUserType() {
+ return delegate.getUserType();
+ }
+
+ @Override
+ public String getReasonCode() {
+ return delegate.getReasonCode();
+ }
+
+ @Override
+ public String getComments() {
+ return delegate.getComments();
+ }
+
+ @Override
+ public DateTime getCreatedDate() {
+ return createdDate;
+ }
+
+ public void setCreatedDate(final DateTime createdDate) {
+ this.createdDate = createdDate;
+ }
+
+ @Override
+ public DateTime getUpdatedDate() {
+ return delegate.getUpdatedDate();
+ }
+
+ @Override
+ public UUID getAccountId() {
+ return delegate.getAccountId();
+ }
+
+ @Override
+ public UUID getTenantId() {
+ return delegate.getTenantId();
+ }
+}
diff --git a/util/src/test/java/org/killbill/billing/GuicyKillbillTestModule.java b/util/src/test/java/org/killbill/billing/GuicyKillbillTestModule.java
index e253aac..49cb620 100644
--- a/util/src/test/java/org/killbill/billing/GuicyKillbillTestModule.java
+++ b/util/src/test/java/org/killbill/billing/GuicyKillbillTestModule.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2016 Groupon, Inc
- * Copyright 2014-2016 The Billing Project, LLC
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
*
* The Billing Project 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
@@ -22,6 +22,7 @@ import java.util.UUID;
import org.joda.time.DateTimeZone;
import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.callcontext.MutableCallContext;
import org.killbill.billing.callcontext.MutableInternalCallContext;
import org.killbill.billing.platform.api.KillbillConfigSource;
import org.killbill.billing.util.callcontext.CallContext;
@@ -52,7 +53,7 @@ public class GuicyKillbillTestModule extends KillBillModule {
GuicyKillbillTestSuite.getClock().getUTCNow(),
GuicyKillbillTestSuite.getClock().getUTCNow());
- private final CallContext callContext = internalCallContext.toCallContext(null,null);
+ private final MutableCallContext callContext = new MutableCallContext(internalCallContext);
public GuicyKillbillTestModule(final KillbillConfigSource configSource) {
super(configSource);
@@ -65,5 +66,6 @@ public class GuicyKillbillTestModule extends KillBillModule {
bind(InternalCallContext.class).toInstance(internalCallContext);
bind(MutableInternalCallContext.class).toInstance(internalCallContext);
bind(CallContext.class).toInstance(callContext);
+ bind(MutableCallContext.class).toInstance(callContext);
}
}
diff --git a/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuite.java b/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuite.java
index 794b71b..135f7aa 100644
--- a/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuite.java
+++ b/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuite.java
@@ -23,20 +23,26 @@ import java.util.UUID;
import javax.inject.Inject;
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
import org.killbill.billing.api.AbortAfterFirstFailureListener;
import org.killbill.billing.api.FlakyInvokedMethodListener;
import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.callcontext.MutableCallContext;
import org.killbill.billing.callcontext.MutableInternalCallContext;
import org.killbill.billing.platform.api.KillbillConfigSource;
import org.killbill.billing.platform.test.config.TestKillbillConfigSource;
-import org.killbill.billing.util.callcontext.CallContext;
import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.callcontext.TenantContext;
import org.killbill.clock.Clock;
import org.killbill.clock.ClockMock;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
import org.skife.config.ConfigSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.testng.Assert;
import org.testng.IHookCallBack;
import org.testng.IHookable;
import org.testng.ITestResult;
@@ -59,7 +65,10 @@ public class GuicyKillbillTestSuite implements IHookable {
// Use the simple name here to save screen real estate
protected static final Logger log = LoggerFactory.getLogger(KillbillTestSuite.class.getSimpleName());
- private boolean hasFailed = false;
+ private static final ClockMock theStaticClock = new ClockMock();
+
+ protected final KillbillConfigSource configSource;
+ protected final ConfigSource skifeConfigSource;
@Inject
protected InternalCallContextFactory internalCallContextFactory;
@@ -68,15 +77,15 @@ public class GuicyKillbillTestSuite implements IHookable {
protected MutableInternalCallContext internalCallContext;
@Inject
- protected CallContext callContext;
+ protected MutableCallContext callContext;
@Inject
- protected ClockMock clock;
+ private ClockMock theRealClock;
- private static final ClockMock theStaticClock = new ClockMock();
+ // Initialized to avoid NPE when skipping tests, but see below
+ protected ClockMock clock = new ClockMock();
- protected final KillbillConfigSource configSource;
- protected final ConfigSource skifeConfigSource;
+ private boolean hasFailed = false;
public GuicyKillbillTestSuite() {
this.configSource = getConfigSource();
@@ -88,6 +97,23 @@ public class GuicyKillbillTestSuite implements IHookable {
};
}
+ public static ClockMock getClock() {
+ return theStaticClock;
+ }
+
+ public static void refreshCallContext(final UUID accountId,
+ final Clock clock,
+ final InternalCallContextFactory internalCallContextFactory,
+ final TenantContext callContext,
+ final MutableInternalCallContext internalCallContext) {
+ final InternalTenantContext tmp = internalCallContextFactory.createInternalTenantContext(accountId, callContext);
+ internalCallContext.setAccountRecordId(tmp.getAccountRecordId());
+ internalCallContext.setFixedOffsetTimeZone(tmp.getFixedOffsetTimeZone());
+ internalCallContext.setReferenceTime(tmp.getReferenceLocalTime());
+ internalCallContext.setCreatedDate(clock.getUTCNow());
+ internalCallContext.setUpdatedDate(clock.getUTCNow());
+ }
+
protected KillbillConfigSource getConfigSource() {
try {
return new TestKillbillConfigSource(DBTestingHelper.class);
@@ -112,27 +138,15 @@ public class GuicyKillbillTestSuite implements IHookable {
}
}
- public static ClockMock getClock() {
- return theStaticClock;
- }
-
- public static void refreshCallContext(final UUID accountId,
- final Clock clock,
- final InternalCallContextFactory internalCallContextFactory,
- final TenantContext callContext,
- final MutableInternalCallContext internalCallContext) {
- final InternalTenantContext tmp = internalCallContextFactory.createInternalTenantContext(accountId, callContext);
- internalCallContext.setAccountRecordId(tmp.getAccountRecordId());
- internalCallContext.setFixedOffsetTimeZone(tmp.getFixedOffsetTimeZone());
- internalCallContext.setReferenceTime(tmp.getReferenceLocalTime());
- internalCallContext.setCreatedDate(clock.getUTCNow());
- internalCallContext.setUpdatedDate(clock.getUTCNow());
- }
-
protected void refreshCallContext(final UUID accountId) {
refreshCallContext(accountId, clock, internalCallContextFactory, callContext, internalCallContext);
}
+ // Refresh the createdDate
+ protected void refreshCallContext() {
+ refreshCallContext(callContext.getAccountId(), clock, internalCallContextFactory, callContext, internalCallContext);
+ }
+
@BeforeMethod(alwaysRun = true)
public void beforeMethodAlwaysRun(final Method method) throws Exception {
if (AbortAfterFirstFailureListener.hasFailures()) {
@@ -146,6 +160,40 @@ public class GuicyKillbillTestSuite implements IHookable {
if (internalCallContext != null) {
internalCallContext.reset();
}
+
+ if (theRealClock != null) {
+ clock = Mockito.spy(theRealClock);
+ final Answer answer = new Answer() {
+ @Override
+ public Object answer(final InvocationOnMock invocation) throws Throwable {
+ // Sync clock and theRealClock
+ final Object realAnswer = invocation.callRealMethod();
+ invocation.getMethod().invoke(theRealClock, invocation.getArguments());
+
+ // Update the contexts createdDate each time we move the clock
+ final DateTime utcNow = theRealClock.getUTCNow();
+ if (callContext != null) {
+ callContext.setCreatedDate(utcNow);
+ }
+ if (internalCallContext != null) {
+ internalCallContext.setCreatedDate(utcNow);
+ }
+
+ return realAnswer;
+ }
+ };
+ Mockito.doAnswer(answer).when(clock).getUTCNow();
+ Mockito.doAnswer(answer).when(clock).getNow(Mockito.any(DateTimeZone.class));
+ Mockito.doAnswer(answer).when(clock).getUTCToday();
+ Mockito.doAnswer(answer).when(clock).getToday(Mockito.any(DateTimeZone.class));
+ Mockito.doAnswer(answer).when(clock).addDays(Mockito.anyInt());
+ Mockito.doAnswer(answer).when(clock).addWeeks(Mockito.anyInt());
+ Mockito.doAnswer(answer).when(clock).addMonths(Mockito.anyInt());
+ Mockito.doAnswer(answer).when(clock).addYears(Mockito.anyInt());
+ Mockito.doAnswer(answer).when(clock).addDeltaFromReality(Mockito.anyLong());
+ Mockito.doAnswer(answer).when(clock).setTime(Mockito.any(DateTime.class));
+ Mockito.doAnswer(answer).when(clock).resetDeltaFromReality();
+ }
}
@AfterMethod(alwaysRun = true)
@@ -210,6 +258,6 @@ public class GuicyKillbillTestSuite implements IHookable {
}
public boolean hasFailed() {
- return hasFailed;
+ return hasFailed || AbortAfterFirstFailureListener.hasFailures();
}
}
diff --git a/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuiteWithEmbeddedDB.java b/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuiteWithEmbeddedDB.java
index 37e3f80..c4ca078 100644
--- a/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuiteWithEmbeddedDB.java
+++ b/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuiteWithEmbeddedDB.java
@@ -23,7 +23,6 @@ import javax.inject.Named;
import javax.sql.DataSource;
import org.killbill.billing.util.cache.CacheControllerDispatcher;
-import org.killbill.billing.util.glue.KillbillApiAopModule;
import org.killbill.commons.embeddeddb.EmbeddedDB;
import org.skife.jdbi.v2.IDBI;
import org.slf4j.Logger;
@@ -62,9 +61,12 @@ public class GuicyKillbillTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuite
@BeforeMethod(groups = "slow")
public void beforeMethod() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
cleanupAllTables();
controlCacheDispatcher.clearAll();
- KillbillApiAopModule.resetDirtyDBFlag();
}
protected void cleanupAllTables() {
diff --git a/util/src/test/java/org/killbill/billing/KillbillTestSuite.java b/util/src/test/java/org/killbill/billing/KillbillTestSuite.java
index ebb0d5b..7b4bdf7 100644
--- a/util/src/test/java/org/killbill/billing/KillbillTestSuite.java
+++ b/util/src/test/java/org/killbill/billing/KillbillTestSuite.java
@@ -20,8 +20,6 @@ package org.killbill.billing;
import java.lang.reflect.Method;
-import org.killbill.clock.Clock;
-import org.killbill.clock.ClockMock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.ITestResult;
@@ -35,8 +33,6 @@ public class KillbillTestSuite {
private boolean hasFailed = false;
- protected Clock clock = new ClockMock();
-
@BeforeMethod(alwaysRun = true)
public void startTestSuite(final Method method) throws Exception {
log.info("***************************************************************************************************");
@@ -48,8 +44,8 @@ public class KillbillTestSuite {
public void endTestSuite(final Method method, final ITestResult result) throws Exception {
log.info("***************************************************************************************************");
log.info("*** Ending test {}:{} {} ({} s.)", new Object[]{method.getDeclaringClass().getName(), method.getName(),
- result.isSuccess() ? "SUCCESS" : "!!! FAILURE !!!",
- (result.getEndMillis() - result.getStartMillis()) / 1000});
+ result.isSuccess() ? "SUCCESS" : "!!! FAILURE !!!",
+ (result.getEndMillis() - result.getStartMillis()) / 1000});
log.info("***************************************************************************************************");
if (!hasFailed && !result.isSuccess()) {
hasFailed = true;
diff --git a/util/src/test/java/org/killbill/billing/mock/api/MockAccountUserApi.java b/util/src/test/java/org/killbill/billing/mock/api/MockAccountUserApi.java
index d6798fc..cbbf33a 100644
--- a/util/src/test/java/org/killbill/billing/mock/api/MockAccountUserApi.java
+++ b/util/src/test/java/org/killbill/billing/mock/api/MockAccountUserApi.java
@@ -30,6 +30,8 @@ import org.killbill.billing.account.api.AccountEmail;
import org.killbill.billing.account.api.AccountUserApi;
import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.mock.MockAccountBuilder;
+import org.killbill.billing.util.api.AuditLevel;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
import org.killbill.billing.util.callcontext.CallContext;
import org.killbill.billing.util.callcontext.TenantContext;
import org.killbill.billing.util.entity.DefaultPagination;
@@ -187,4 +189,9 @@ public class MockAccountUserApi implements AccountUserApi {
public List<Account> getChildrenAccounts(final UUID uuid, final TenantContext tenantContext) throws AccountApiException {
throw new UnsupportedOperationException();
}
+
+ @Override
+ public List<AuditLogWithHistory> getAuditLogsWithHistoryForId(final UUID uuid, final AuditLevel auditLevel, final TenantContext tenantContext) throws AccountApiException {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/util/src/test/java/org/killbill/billing/util/audit/AuditLogsTestBase.java b/util/src/test/java/org/killbill/billing/util/audit/AuditLogsTestBase.java
index 1025bac..98517c2 100644
--- a/util/src/test/java/org/killbill/billing/util/audit/AuditLogsTestBase.java
+++ b/util/src/test/java/org/killbill/billing/util/audit/AuditLogsTestBase.java
@@ -19,9 +19,9 @@ package org.killbill.billing.util.audit;
import java.util.List;
import java.util.UUID;
-import org.mockito.Mockito;
-
+import org.joda.time.DateTime;
import org.killbill.billing.util.UtilTestSuiteNoDB;
+import org.mockito.Mockito;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -39,7 +39,8 @@ public abstract class AuditLogsTestBase extends UtilTestSuiteNoDB {
protected AuditLog createAuditLog() {
final AuditLog auditLog = Mockito.mock(AuditLog.class);
- Mockito.when(auditLog.getCreatedDate()).thenReturn(clock.getUTCNow());
+ final DateTime utcNow = clock.getUTCNow();
+ Mockito.when(auditLog.getCreatedDate()).thenReturn(utcNow);
Mockito.when(auditLog.getReasonCode()).thenReturn(UUID.randomUUID().toString());
Mockito.when(auditLog.getUserName()).thenReturn(UUID.randomUUID().toString());
Mockito.when(auditLog.getUserToken()).thenReturn(UUID.randomUUID().toString());
diff --git a/util/src/test/java/org/killbill/billing/util/audit/dao/MockAuditDao.java b/util/src/test/java/org/killbill/billing/util/audit/dao/MockAuditDao.java
index 1aa816d..dc84aeb 100644
--- a/util/src/test/java/org/killbill/billing/util/audit/dao/MockAuditDao.java
+++ b/util/src/test/java/org/killbill/billing/util/audit/dao/MockAuditDao.java
@@ -25,8 +25,10 @@ import java.util.UUID;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.util.api.AuditLevel;
import org.killbill.billing.util.audit.AuditLog;
+import org.killbill.billing.util.audit.AuditLogWithHistory;
import org.killbill.billing.util.audit.DefaultAccountAuditLogs;
import org.killbill.billing.util.audit.DefaultAccountAuditLogsForObjectType;
+import org.killbill.billing.util.dao.HistorySqlDao;
import org.killbill.billing.util.dao.TableName;
import com.google.common.base.MoreObjects;
@@ -81,4 +83,9 @@ public class MockAuditDao implements AuditDao {
return allAuditLogs;
}
}
+
+ @Override
+ public List<AuditLogWithHistory> getAuditLogsWithHistoryForId(final HistorySqlDao sqlDao, final TableName tableName, final UUID objectId, final AuditLevel auditLevel, final InternalTenantContext context) {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/util/src/test/java/org/killbill/billing/util/UtilTestSuiteNoDB.java b/util/src/test/java/org/killbill/billing/util/UtilTestSuiteNoDB.java
index 920e5cb..cda04b3 100644
--- a/util/src/test/java/org/killbill/billing/util/UtilTestSuiteNoDB.java
+++ b/util/src/test/java/org/killbill/billing/util/UtilTestSuiteNoDB.java
@@ -75,17 +75,29 @@ public class UtilTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
@BeforeClass(groups = "fast")
public void beforeClass() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
final Injector g = Guice.createInjector(Stage.PRODUCTION, new TestUtilModuleNoDB(configSource));
g.injectMembers(this);
}
@BeforeMethod(groups = "fast")
public void beforeMethod() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
eventBus.start();
}
@AfterMethod(groups = "fast")
public void afterMethod() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
eventBus.stop();
// Reset the security manager
ThreadContext.unbindSecurityManager();
diff --git a/util/src/test/java/org/killbill/billing/util/UtilTestSuiteWithEmbeddedDB.java b/util/src/test/java/org/killbill/billing/util/UtilTestSuiteWithEmbeddedDB.java
index f734d68..3868612 100644
--- a/util/src/test/java/org/killbill/billing/util/UtilTestSuiteWithEmbeddedDB.java
+++ b/util/src/test/java/org/killbill/billing/util/UtilTestSuiteWithEmbeddedDB.java
@@ -98,6 +98,10 @@ public abstract class UtilTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuite
@BeforeClass(groups = "slow")
public void beforeClass() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
final Injector g = Guice.createInjector(Stage.PRODUCTION, new TestUtilModuleWithEmbeddedDB(configSource));
g.injectMembers(this);
@@ -114,6 +118,10 @@ public abstract class UtilTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuite
@Override
@BeforeMethod(groups = "slow")
public void beforeMethod() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
super.beforeMethod();
eventsListener.reset();
@@ -124,6 +132,10 @@ public abstract class UtilTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuite
@AfterMethod(groups = "slow")
public void afterMethod() throws Exception {
+ if (hasFailed()) {
+ return;
+ }
+
eventBus.unregister(eventsListener);
eventBus.stop();
}
diff --git a/util/src/test/java/org/killbill/billing/util/validation/TestValidationManager.java b/util/src/test/java/org/killbill/billing/util/validation/TestValidationManager.java
index 68f8773..1a9761a 100644
--- a/util/src/test/java/org/killbill/billing/util/validation/TestValidationManager.java
+++ b/util/src/test/java/org/killbill/billing/util/validation/TestValidationManager.java
@@ -41,7 +41,7 @@ public class TestValidationManager extends UtilTestSuiteWithEmbeddedDB {
@BeforeClass(groups = "slow")
public void beforeClass() throws Exception {
super.beforeClass();
- final DatabaseSchemaDao dao = new DatabaseSchemaDao(dbi);
+ final DatabaseSchemaDao dao = new DatabaseSchemaDao(dbi, roDbi);
vm = new ValidationManager(dao);
vm.loadSchemaInformation(helper.getDatabaseName());
}