Details
diff --git a/api/src/main/java/org/killbill/billing/callcontext/InternalCallContext.java b/api/src/main/java/org/killbill/billing/callcontext/InternalCallContext.java
index 5370614..f1429a0 100644
--- a/api/src/main/java/org/killbill/billing/callcontext/InternalCallContext.java
+++ b/api/src/main/java/org/killbill/billing/callcontext/InternalCallContext.java
@@ -69,6 +69,12 @@ public class InternalCallContext extends InternalTenantContext {
context.getUpdatedDate());
}
+ public InternalCallContext(final InternalCallContext context, final Long accountRecordId, final Long tenantRecordId) {
+ this(tenantRecordId, accountRecordId, context.getUserToken(), context.getCreatedBy(), context.getCallOrigin(),
+ context.getContextUserType(), context.getReasonCode(), context.getComments(), context.getCreatedDate(),
+ context.getUpdatedDate());
+ }
+
// TODO should not be needed if all services are using internal API
// Unfortunately not true as some APIs ae hidden in object -- e.g OverdueStateApplicator is doing subscription.cancelEntitlementWithDateOverrideBillingPolicy
public CallContext toCallContext(final UUID tenantId) {
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/janitor/AttemptCompletionTask.java b/payment/src/main/java/org/killbill/billing/payment/core/janitor/AttemptCompletionTask.java
index 542a883..e3d4018 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/janitor/AttemptCompletionTask.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/janitor/AttemptCompletionTask.java
@@ -62,7 +62,7 @@ final class AttemptCompletionTask extends CompletionTaskBase<PaymentAttemptModel
@Override
public List<PaymentAttemptModelDao> getItemsForIteration() {
- final List<PaymentAttemptModelDao> incompleteAttempts = paymentDao.getPaymentAttemptsByState(retrySMHelper.getInitialState().getName(), getCreatedDateBefore(), completionTaskCallContext);
+ final List<PaymentAttemptModelDao> incompleteAttempts = paymentDao.getPaymentAttemptsByStateAcrossTenants(retrySMHelper.getInitialState().getName(), getCreatedDateBefore());
if (!incompleteAttempts.isEmpty()) {
log.info("Janitor AttemptCompletionTask start run: found {} incomplete attempts", incompleteAttempts.size());
}
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/janitor/CompletionTaskBase.java b/payment/src/main/java/org/killbill/billing/payment/core/janitor/CompletionTaskBase.java
index ecb9096..008bc8b 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/janitor/CompletionTaskBase.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/janitor/CompletionTaskBase.java
@@ -52,7 +52,6 @@ abstract class CompletionTaskBase<T> implements Runnable {
protected final PaymentConfig paymentConfig;
protected final Clock clock;
protected final PaymentDao paymentDao;
- protected final InternalCallContext completionTaskCallContext;
protected final InternalCallContextFactory internalCallContextFactory;
protected final PaymentStateMachineHelper paymentStateMachineHelper;
protected final RetryStateMachineHelper retrySMHelper;
@@ -76,7 +75,6 @@ abstract class CompletionTaskBase<T> implements Runnable {
this.pluginRegistry = pluginRegistry;
// Limit the length of the username in the context (limited to 50 characters)
this.taskName = this.getClass().getSimpleName();
- this.completionTaskCallContext = internalCallContextFactory.createInternalCallContext((Long) null, (Long) null, taskName, CallOrigin.INTERNAL, UserType.SYSTEM, UUIDs.randomUUID());
}
@Override
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/janitor/ErroredPaymentTask.java b/payment/src/main/java/org/killbill/billing/payment/core/janitor/ErroredPaymentTask.java
index 54d09f9..75ae23d 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/janitor/ErroredPaymentTask.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/janitor/ErroredPaymentTask.java
@@ -74,7 +74,7 @@ public class ErroredPaymentTask extends CompletionTaskBase<PaymentModelDao> {
// We want to avoid iterating on the same failed payments -- if for some reasons they can't fix themselves.
final DateTime createdAfterDate = clock.getUTCNow().minusDays(OLDER_PAYMENTS_IN_DAYS);
- final List<PaymentModelDao> result = paymentDao.getPaymentsByStates(paymentStateMachineHelper.getErroredStateNames(), createdBeforeDate, createdAfterDate, MAX_ITEMS_PER_LOOP, completionTaskCallContext);
+ final List<PaymentModelDao> result = paymentDao.getPaymentsByStatesAcrossTenants(paymentStateMachineHelper.getErroredStateNames(), createdBeforeDate, createdAfterDate, MAX_ITEMS_PER_LOOP);
if (!result.isEmpty()) {
log.info("Janitor ErroredPaymentTask start run: found {} errored/unknown payments", result.size());
}
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/janitor/PendingTransactionTask.java b/payment/src/main/java/org/killbill/billing/payment/core/janitor/PendingTransactionTask.java
index 97c65a4..8bc737d 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/janitor/PendingTransactionTask.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/janitor/PendingTransactionTask.java
@@ -20,6 +20,7 @@ package org.killbill.billing.payment.core.janitor;
import java.util.List;
import org.killbill.billing.account.api.AccountInternalApi;
+import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.osgi.api.OSGIServiceRegistration;
import org.killbill.billing.payment.api.TransactionStatus;
import org.killbill.billing.payment.core.sm.PaymentStateMachineHelper;
@@ -27,7 +28,10 @@ import org.killbill.billing.payment.core.sm.PluginRoutingPaymentAutomatonRunner;
import org.killbill.billing.payment.core.sm.RetryStateMachineHelper;
import org.killbill.billing.payment.dao.PaymentDao;
import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
+import org.killbill.billing.util.UUIDs;
+import org.killbill.billing.util.callcontext.CallOrigin;
import org.killbill.billing.util.callcontext.InternalCallContextFactory;
+import org.killbill.billing.util.callcontext.UserType;
import org.killbill.billing.util.config.PaymentConfig;
import org.killbill.clock.Clock;
@@ -55,9 +59,8 @@ final class PendingTransactionTask extends CompletionTaskBase<Integer> {
@Override
public void doIteration(final Integer item) {
-
- // TODO this is needs to be fixed see- #230
- int result = paymentDao.failOldPendingTransactions(TransactionStatus.PLUGIN_FAILURE, getCreatedDateBefore(), completionTaskCallContext);
+ final InternalCallContext contextTemplate = internalCallContextFactory.createInternalCallContext((Long) null, (Long) null, "PendingTransactionTask", CallOrigin.INTERNAL, UserType.SYSTEM, UUIDs.randomUUID());
+ int result = paymentDao.failOldPendingTransactions(TransactionStatus.PLUGIN_FAILURE, getCreatedDateBefore(), contextTemplate);
if (result > 0) {
log.info("Janitor PendingTransactionTask moved " + result + " PENDING payments -> PLUGIN_FAILURE");
}
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 d8f2486..75f8fe7 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
@@ -20,8 +20,11 @@ package org.killbill.billing.payment.dao;
import java.math.BigDecimal;
import java.util.Collection;
+import java.util.HashMap;
import java.util.Iterator;
+import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import java.util.UUID;
import javax.annotation.Nullable;
@@ -60,6 +63,7 @@ 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;
public class DefaultPaymentDao implements PaymentDao {
@@ -117,12 +121,12 @@ public class DefaultPaymentDao implements PaymentDao {
}
@Override
- public List<PaymentAttemptModelDao> getPaymentAttemptsByState(final String stateName, final DateTime createdBeforeDate, final InternalTenantContext context) {
+ public List<PaymentAttemptModelDao> getPaymentAttemptsByStateAcrossTenants(final String stateName, final DateTime createdBeforeDate) {
return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<PaymentAttemptModelDao>>() {
@Override
public List<PaymentAttemptModelDao> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
final PaymentAttemptSqlDao transactional = entitySqlDaoWrapperFactory.become(PaymentAttemptSqlDao.class);
- return transactional.getByStateName(stateName, createdBeforeDate.toDate(), context);
+ return transactional.getByStateNameAcrossTenants(stateName, createdBeforeDate.toDate());
}
});
}
@@ -152,22 +156,38 @@ public class DefaultPaymentDao implements PaymentDao {
}
@Override
- public int failOldPendingTransactions(final TransactionStatus newTransactionStatus, final DateTime createdBeforeDate, final InternalCallContext context) {
+ public int failOldPendingTransactions(final TransactionStatus newTransactionStatus, final DateTime createdBeforeDate, final InternalCallContext internalCallContextTemplate) {
return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Integer>() {
@Override
public Integer inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
final TransactionSqlDao transactional = entitySqlDaoWrapperFactory.become(TransactionSqlDao.class);
- final List<PaymentTransactionModelDao> oldPendingTransactions = transactional.getByTransactionStatusPriorDate(TransactionStatus.PENDING.toString(), createdBeforeDate.toDate(), context);
+ final List<PaymentTransactionModelDao> oldPendingTransactions = transactional.getByTransactionStatusPriorDateAcrossTenants(TransactionStatus.PENDING.toString(), createdBeforeDate.toDate());
if (oldPendingTransactions.size() > 0) {
- final Collection<String> oldPendingTransactionIds = Collections2.transform(oldPendingTransactions, new Function<PaymentTransactionModelDao, String>() {
- @Override
- public String apply(final PaymentTransactionModelDao input) {
- return input.getId().toString();
- }
- });
+ // Partition per tenant to compute a valid context
+ final Map<Long, List<PaymentTransactionModelDao>> perTenantPendingTransactions = new HashMap<Long, List<PaymentTransactionModelDao>>();
+ for (PaymentTransactionModelDao curPaymentTransactionModelDao : oldPendingTransactions) {
+ List<PaymentTransactionModelDao> pendingTransactions = perTenantPendingTransactions.get(curPaymentTransactionModelDao.getTenantRecordId());
+ if (pendingTransactions == null) {
+ pendingTransactions = new LinkedList<PaymentTransactionModelDao>();
+ perTenantPendingTransactions.put(curPaymentTransactionModelDao.getTenantRecordId(), pendingTransactions);
+ }
+ pendingTransactions.add(curPaymentTransactionModelDao);
+ }
- return transactional.failOldPendingTransactions(oldPendingTransactionIds, TransactionStatus.PAYMENT_FAILURE.toString(), context);
+ int result = 0;
+ for (final Long curTenantRecordId : perTenantPendingTransactions.keySet()) {
+ final InternalCallContext validContext = new InternalCallContext(internalCallContextTemplate, -1L, curTenantRecordId);
+ final Collection<String> perTenantPendingTransactionIds = Collections2.transform(perTenantPendingTransactions.get(curTenantRecordId), new Function<PaymentTransactionModelDao, String>() {
+ @Nullable
+ @Override
+ public String apply(@Nullable final PaymentTransactionModelDao input) {
+ return input.getId().toString();
+ }
+ });
+ result += transactional.failOldPendingTransactions(perTenantPendingTransactionIds, TransactionStatus.PAYMENT_FAILURE.toString(), validContext);
+ }
+ return result;
}
return 0;
}
@@ -324,11 +344,11 @@ public class DefaultPaymentDao implements PaymentDao {
}
@Override
- public List<PaymentModelDao> getPaymentsByStates(final String[] states, final DateTime createdBeforeDate, final DateTime createdAfterDate, final int limit, final InternalTenantContext context) {
+ public List<PaymentModelDao> getPaymentsByStatesAcrossTenants(final String[] states, final DateTime createdBeforeDate, final DateTime createdAfterDate, final int limit) {
return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<PaymentModelDao>>() {
@Override
public List<PaymentModelDao> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
- return entitySqlDaoWrapperFactory.become(PaymentSqlDao.class).getPaymentsByStates(ImmutableList.copyOf(states), createdBeforeDate.toDate(), createdAfterDate.toDate(), context, limit);
+ return entitySqlDaoWrapperFactory.become(PaymentSqlDao.class).getPaymentsByStatesAcrossTenants(ImmutableList.copyOf(states), createdBeforeDate.toDate(), createdAfterDate.toDate(), limit);
}
});
}
diff --git a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentAttemptSqlDao.java b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentAttemptSqlDao.java
index e3a3fe4..544e02e 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentAttemptSqlDao.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentAttemptSqlDao.java
@@ -50,8 +50,7 @@ public interface PaymentAttemptSqlDao extends EntitySqlDao<PaymentAttemptModelDa
@BindBean final InternalTenantContext context);
@SqlQuery
- List<PaymentAttemptModelDao> getByStateName(@Bind("stateName") final String stateName,
- @Bind("createdBeforeDate") final Date createdBeforeDate,
- @BindBean final InternalTenantContext context);
+ List<PaymentAttemptModelDao> getByStateNameAcrossTenants(@Bind("stateName") final String stateName,
+ @Bind("createdBeforeDate") final Date createdBeforeDate);
}
diff --git a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentDao.java b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentDao.java
index 1119b85..76ac6c8 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentDao.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentDao.java
@@ -30,13 +30,13 @@ import org.killbill.billing.util.entity.Pagination;
public interface PaymentDao {
- public int failOldPendingTransactions(TransactionStatus newTransactionStatus, DateTime createdBeforeDate, InternalCallContext context);
+ public int failOldPendingTransactions(TransactionStatus newTransactionStatus, DateTime createdBeforeDate, InternalCallContext internalCallContextTemplate);
public PaymentAttemptModelDao insertPaymentAttemptWithProperties(PaymentAttemptModelDao attempt, InternalCallContext context);
public void updatePaymentAttempt(UUID paymentAttemptId, UUID transactionId, String state, InternalCallContext context);
- public List<PaymentAttemptModelDao> getPaymentAttemptsByState(String stateName, DateTime createdBeforeDate, InternalTenantContext context);
+ public List<PaymentAttemptModelDao> getPaymentAttemptsByStateAcrossTenants(String stateName, DateTime createdBeforeDate);
public List<PaymentAttemptModelDao> getPaymentAttempts(String paymentExternalKey, InternalTenantContext context);
@@ -64,7 +64,7 @@ public interface PaymentDao {
public List<PaymentModelDao> getPaymentsForAccount(UUID accountId, InternalTenantContext context);
- public List<PaymentModelDao> getPaymentsByStates(String [] states, DateTime createdBeforeDate, DateTime createdAfterDate, int limit, InternalTenantContext context);
+ public List<PaymentModelDao> getPaymentsByStatesAcrossTenants(String[] states, DateTime createdBeforeDate, DateTime createdAfterDate, int limit);
public List<PaymentTransactionModelDao> getTransactionsForAccount(UUID accountId, InternalTenantContext context);
diff --git a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentSqlDao.java b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentSqlDao.java
index 4d8dd0c..9e9ed12 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dao/PaymentSqlDao.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dao/PaymentSqlDao.java
@@ -62,11 +62,10 @@ public interface PaymentSqlDao extends EntitySqlDao<PaymentModelDao, Payment> {
@BindBean final InternalTenantContext context);
@SqlQuery
- public List<PaymentModelDao> getPaymentsByStates(@StateCollectionBinder final Collection<String> states,
- @Bind("createdBeforeDate") final Date createdBeforeDate,
- @Bind("createdAfterDate") final Date createdAfterDate,
- @BindBean final InternalTenantContext context,
- @Bind("limit") final int limit);
+ public List<PaymentModelDao> getPaymentsByStatesAcrossTenants(@StateCollectionBinder final Collection<String> states,
+ @Bind("createdBeforeDate") final Date createdBeforeDate,
+ @Bind("createdAfterDate") final Date createdAfterDate,
+ @Bind("limit") final int limit);
@SqlQuery
@SmartFetchSize(shouldStream = true)
diff --git a/payment/src/main/java/org/killbill/billing/payment/dao/TransactionSqlDao.java b/payment/src/main/java/org/killbill/billing/payment/dao/TransactionSqlDao.java
index e9abc96..2f1853f 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dao/TransactionSqlDao.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dao/TransactionSqlDao.java
@@ -53,9 +53,8 @@ public interface TransactionSqlDao extends EntitySqlDao<PaymentTransactionModelD
@BindBean final InternalTenantContext context);
@SqlQuery
- List<PaymentTransactionModelDao> getByTransactionStatusPriorDate(@Bind("transactionStatus") final String transactionStatus,
- @Bind("beforeCreatedDate") final Date beforeCreatedDate,
- @BindBean final InternalTenantContext context);
+ List<PaymentTransactionModelDao> getByTransactionStatusPriorDateAcrossTenants(@Bind("transactionStatus") final String transactionStatus,
+ @Bind("beforeCreatedDate") final Date beforeCreatedDate);
@SqlUpdate
@Audited(ChangeType.UPDATE)
diff --git a/payment/src/main/resources/org/killbill/billing/payment/dao/PaymentAttemptSqlDao.sql.stg b/payment/src/main/resources/org/killbill/billing/payment/dao/PaymentAttemptSqlDao.sql.stg
index 1cc0604..c1f77a9 100644
--- a/payment/src/main/resources/org/killbill/billing/payment/dao/PaymentAttemptSqlDao.sql.stg
+++ b/payment/src/main/resources/org/killbill/billing/payment/dao/PaymentAttemptSqlDao.sql.stg
@@ -65,7 +65,7 @@ where payment_external_key = :paymentExternalKey
>>
/* Does not include tenant info, global */
-getByStateName() ::= <<
+getByStateNameAcrossTenants() ::= <<
select
<allTableFields("")>
from <tableName()>
diff --git a/payment/src/main/resources/org/killbill/billing/payment/dao/PaymentSqlDao.sql.stg b/payment/src/main/resources/org/killbill/billing/payment/dao/PaymentSqlDao.sql.stg
index 3a96d8f..e17fdff 100644
--- a/payment/src/main/resources/org/killbill/billing/payment/dao/PaymentSqlDao.sql.stg
+++ b/payment/src/main/resources/org/killbill/billing/payment/dao/PaymentSqlDao.sql.stg
@@ -102,7 +102,7 @@ where pm.plugin_name = :pluginName
;
>>
-getPaymentsByStates(states) ::= <<
+getPaymentsByStatesAcrossTenants(states) ::= <<
select
<allTableFields("t.")>
from <tableName()> t
diff --git a/payment/src/main/resources/org/killbill/billing/payment/dao/TransactionSqlDao.sql.stg b/payment/src/main/resources/org/killbill/billing/payment/dao/TransactionSqlDao.sql.stg
index 932a405..b14134f 100644
--- a/payment/src/main/resources/org/killbill/billing/payment/dao/TransactionSqlDao.sql.stg
+++ b/payment/src/main/resources/org/killbill/billing/payment/dao/TransactionSqlDao.sql.stg
@@ -82,7 +82,7 @@ where payment_id = :paymentId
/* Does not include AND_CHECK_TENANT() since this is a global operation */
-getByTransactionStatusPriorDate() ::= <<
+getByTransactionStatusPriorDateAcrossTenants() ::= <<
select <allTableFields()>
from <tableName()>
where transaction_status = :transactionStatus
diff --git a/payment/src/test/java/org/killbill/billing/payment/dao/MockPaymentDao.java b/payment/src/test/java/org/killbill/billing/payment/dao/MockPaymentDao.java
index 836d2d3..03e743b 100644
--- a/payment/src/test/java/org/killbill/billing/payment/dao/MockPaymentDao.java
+++ b/payment/src/test/java/org/killbill/billing/payment/dao/MockPaymentDao.java
@@ -105,7 +105,7 @@ public class MockPaymentDao implements PaymentDao {
}
@Override
- public List<PaymentAttemptModelDao> getPaymentAttemptsByState(final String stateName, final DateTime createdBeforeDate, final InternalTenantContext context) {
+ public List<PaymentAttemptModelDao> getPaymentAttemptsByStateAcrossTenants(final String stateName, final DateTime createdBeforeDate) {
return null;
}
@@ -244,7 +244,7 @@ public class MockPaymentDao implements PaymentDao {
}
@Override
- public List<PaymentModelDao> getPaymentsByStates(final String[] states, final DateTime createdBeforeDate, final DateTime createdAfterDate, final int limit, final InternalTenantContext context) {
+ public List<PaymentModelDao> getPaymentsByStatesAcrossTenants(final String[] states, final DateTime createdBeforeDate, final DateTime createdAfterDate, final int limit) {
return null;
}
diff --git a/payment/src/test/java/org/killbill/billing/payment/dao/TestPaymentDao.java b/payment/src/test/java/org/killbill/billing/payment/dao/TestPaymentDao.java
index 67679de..d0ee734 100644
--- a/payment/src/test/java/org/killbill/billing/payment/dao/TestPaymentDao.java
+++ b/payment/src/test/java/org/killbill/billing/payment/dao/TestPaymentDao.java
@@ -452,7 +452,7 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
paymentDao.insertPaymentWithFirstTransaction(paymentModelDao5, transaction5, context5);
final String[] errorStates = {"AUTH_ERRORED", "CAPTURE_ERRORED", "REFUND_ERRORED", "CREDIT_ERRORED"};
- final List<PaymentModelDao> result = paymentDao.getPaymentsByStates(errorStates, createdBeforeDate, createdAfterDate, 10, internalCallContext);
+ final List<PaymentModelDao> result = paymentDao.getPaymentsByStatesAcrossTenants(errorStates, createdBeforeDate, createdAfterDate, 10);
assertEquals(result.size(), 2);
}