killbill-uncached
Changes
account/pom.xml 2(+1 -1)
api/pom.xml 2(+1 -1)
beatrix/pom.xml 2(+1 -1)
bin/db-helper 15(+9 -6)
catalog/pom.xml 2(+1 -1)
currency/pom.xml 2(+1 -1)
entitlement/pom.xml 2(+1 -1)
invoice/pom.xml 2(+1 -1)
jaxrs/pom.xml 2(+1 -1)
junction/pom.xml 2(+1 -1)
NEWS 3(+3 -0)
overdue/pom.xml 2(+1 -1)
payment/pom.xml 2(+1 -1)
payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentAttemptTask.java 13(+9 -4)
payment/src/main/java/org/killbill/billing/payment/core/sm/control/ControlPluginRunner.java 2(+1 -1)
payment/src/main/java/org/killbill/billing/payment/core/sm/payments/PaymentLeavingStateCallback.java 26(+13 -13)
payment/src/main/java/org/killbill/billing/payment/core/sm/PluginControlPaymentAutomatonRunner.java 13(+5 -8)
payment/src/main/java/org/killbill/billing/payment/dispatcher/CallableWithRequestData.java 44(+44 -0)
payment/src/test/java/org/killbill/billing/payment/core/sm/control/TestControlPluginRunner.java 74(+74 -0)
payment/src/test/java/org/killbill/billing/payment/core/sm/MockRetryablePaymentAutomatonRunner.java 13(+5 -8)
pom.xml 4(+2 -2)
profiles/killbill/pom.xml 2(+1 -1)
profiles/killbill/src/main/java/org/killbill/billing/server/filters/RequestDataFilter.java 51(+51 -0)
profiles/killbill/src/main/java/org/killbill/billing/server/listeners/KillbillGuiceListener.java 2(+2 -0)
profiles/killpay/pom.xml 2(+1 -1)
profiles/pom.xml 2(+1 -1)
subscription/pom.xml 2(+1 -1)
tenant/pom.xml 2(+1 -1)
usage/pom.xml 2(+1 -1)
util/pom.xml 2(+1 -1)
Details
account/pom.xml 2(+1 -1)
diff --git a/account/pom.xml b/account/pom.xml
index e331e15..44b911a 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.15.3-SNAPSHOT</version>
+ <version>0.15.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-account</artifactId>
api/pom.xml 2(+1 -1)
diff --git a/api/pom.xml b/api/pom.xml
index f26b6fc..5b8aad5 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.15.3-SNAPSHOT</version>
+ <version>0.15.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-internal-api</artifactId>
beatrix/pom.xml 2(+1 -1)
diff --git a/beatrix/pom.xml b/beatrix/pom.xml
index 8613028..c6bdbda 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.15.3-SNAPSHOT</version>
+ <version>0.15.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-beatrix</artifactId>
bin/db-helper 15(+9 -6)
diff --git a/bin/db-helper b/bin/db-helper
index 929c59a..95761e6 100755
--- a/bin/db-helper
+++ b/bin/db-helper
@@ -19,7 +19,7 @@
# #
###################################################################################
-#set -x
+# set -x
HERE=`cd \`dirname $0\`; pwd`
TOP=$HERE/..
@@ -27,6 +27,7 @@ TOP=$HERE/..
POM="$TOP/pom.xml"
ACTION=
+HOST="localhost"
DATABASE="killbill"
USER="root"
PWD="root"
@@ -42,6 +43,7 @@ SKIP="(server)"
function usage() {
echo -n "./db_helper "
echo -n " -a <create|clean|dump>"
+ echo -n " -H MySQL host (default = localhost)"
echo -n " -d database_name (default = killbill)"
echo -n " -u user_name (default = root)"
echo -n " -p password (default = root)"
@@ -106,7 +108,7 @@ function create_ddl_file() {
local tmp="/tmp/ddl-$DATABASE.$$"
touch $tmp
- echo "/*! use $DATABASE; */" >> $tmp
+ echo "use $DATABASE;" >> $tmp
echo "" >> $tmp
for d in $ddls; do
cat $d >> $tmp
@@ -120,10 +122,11 @@ function cleanup() {
}
-while getopts ":a:d:u:p:f:t" options; do
+while getopts ":a:d:H:u:p:f:t" options; do
case $options in
a ) ACTION=$OPTARG;;
d ) DATABASE=$OPTARG;;
+ H ) HOST=$OPTARG;;
u ) USER=$OPTARG;;
p ) PWD=$OPTARG;;
t ) TEST_ALSO=1;;
@@ -152,15 +155,15 @@ fi
if [ $ACTION == "create" ]; then
DDL_FILE=`create_ddl_file`
- echo "Applying new schema $tmp to database $DATABASE"
- mysql -u $USER --password=$PWD < $DDL_FILE
+ echo "Applying new schema to database $DATABASE"
+ mysql -h $HOST -u $USER --password=$PWD < $DDL_FILE
fi
if [ $ACTION == "clean" ]; then
DDL_FILE=`create_ddl_file`
CLEAN_FILE=`create_clean_file $DDL_FILE`
echo "Cleaning db tables on database $DATABASE"
- mysql -u $USER --password=$PWD < $DDL_FILE
+ mysql -h $HOST -u $USER --password=$PWD < $DDL_FILE
fi
cleanup
catalog/pom.xml 2(+1 -1)
diff --git a/catalog/pom.xml b/catalog/pom.xml
index abdc1b9..e6ad7c8 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.15.3-SNAPSHOT</version>
+ <version>0.15.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-catalog</artifactId>
currency/pom.xml 2(+1 -1)
diff --git a/currency/pom.xml b/currency/pom.xml
index 62ea8e3..55e978a 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.15.3-SNAPSHOT</version>
+ <version>0.15.4-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 ea853a2..7895360 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.15.3-SNAPSHOT</version>
+ <version>0.15.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-entitlement</artifactId>
invoice/pom.xml 2(+1 -1)
diff --git a/invoice/pom.xml b/invoice/pom.xml
index 6b48d16..b1460fe 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.15.3-SNAPSHOT</version>
+ <version>0.15.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-invoice</artifactId>
jaxrs/pom.xml 2(+1 -1)
diff --git a/jaxrs/pom.xml b/jaxrs/pom.xml
index 5a8de80..fbb674e 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.15.3-SNAPSHOT</version>
+ <version>0.15.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-jaxrs</artifactId>
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentGatewayResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentGatewayResource.java
index 7f50aa1..62b723e 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentGatewayResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentGatewayResource.java
@@ -44,6 +44,7 @@ import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
import org.killbill.billing.payment.api.PaymentApi;
import org.killbill.billing.payment.api.PaymentApiException;
import org.killbill.billing.payment.api.PaymentGatewayApi;
+import org.killbill.billing.payment.api.PaymentOptions;
import org.killbill.billing.payment.api.PluginProperty;
import org.killbill.billing.payment.plugin.api.GatewayNotification;
import org.killbill.billing.payment.plugin.api.HostedPaymentPageFormDescriptor;
@@ -94,6 +95,7 @@ public class PaymentGatewayResource extends ComboPaymentResource {
@ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid data for Account or PaymentMethod")})
public Response buildComboFormDescriptor(final ComboHostedPaymentPageJson json,
@PathParam("accountId") final String accountIdString,
+ @QueryParam(QUERY_PAYMENT_CONTROL_PLUGIN_NAME) final List<String> paymentControlPluginNames,
@QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
@HeaderParam(HDR_CREATED_BY) final String createdBy,
@HeaderParam(HDR_REASON) final String reason,
@@ -103,6 +105,7 @@ public class PaymentGatewayResource extends ComboPaymentResource {
verifyNonNullOrEmpty(json, "ComboHostedPaymentPageJson body should be specified");
final Iterable<PluginProperty> pluginProperties = extractPluginProperties(pluginPropertiesString);
+ final PaymentOptions paymentOptions = createControlPluginApiPaymentOptions(paymentControlPluginNames);
final CallContext callContext = context.createContext(createdBy, reason, comment, request);
final Account account = getOrCreateAccount(json.getAccount(), callContext);
@@ -113,7 +116,7 @@ public class PaymentGatewayResource extends ComboPaymentResource {
final HostedPaymentPageFieldsJson hostedPaymentPageFields = json.getHostedPaymentPageFieldsJson();
final Iterable<PluginProperty> customFields = extractPluginProperties(hostedPaymentPageFields != null ? hostedPaymentPageFields.getCustomFields() : null);
- final HostedPaymentPageFormDescriptor descriptor = paymentGatewayApi.buildFormDescriptor(account, paymentMethodId, customFields, pluginProperties, callContext);
+ final HostedPaymentPageFormDescriptor descriptor = paymentGatewayApi.buildFormDescriptorWithPaymentControl(account, paymentMethodId, customFields, pluginProperties, paymentOptions, callContext);
final HostedPaymentPageFormDescriptorJson result = new HostedPaymentPageFormDescriptorJson(descriptor);
return Response.status(Response.Status.OK).entity(result).build();
@@ -130,6 +133,7 @@ public class PaymentGatewayResource extends ComboPaymentResource {
public Response buildFormDescriptor(final HostedPaymentPageFieldsJson json,
@PathParam("accountId") final String accountIdString,
@QueryParam(QUERY_PAYMENT_METHOD_ID) final String paymentMethodIdStr,
+ @QueryParam(QUERY_PAYMENT_CONTROL_PLUGIN_NAME) final List<String> paymentControlPluginNames,
@QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
@HeaderParam(HDR_CREATED_BY) final String createdBy,
@HeaderParam(HDR_REASON) final String reason,
@@ -137,6 +141,7 @@ public class PaymentGatewayResource extends ComboPaymentResource {
@javax.ws.rs.core.Context final UriInfo uriInfo,
@javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException, AccountApiException {
final Iterable<PluginProperty> pluginProperties = extractPluginProperties(pluginPropertiesString);
+ final PaymentOptions paymentOptions = createControlPluginApiPaymentOptions(paymentControlPluginNames);
final CallContext callContext = context.createContext(createdBy, reason, comment, request);
final UUID accountId = UUID.fromString(accountIdString);
final Account account = accountUserApi.getAccountById(accountId, callContext);
@@ -146,7 +151,7 @@ public class PaymentGatewayResource extends ComboPaymentResource {
final Iterable<PluginProperty> customFields = extractPluginProperties(json.getCustomFields());
- final HostedPaymentPageFormDescriptor descriptor = paymentGatewayApi.buildFormDescriptor(account, paymentMethodId, customFields, pluginProperties, callContext);
+ final HostedPaymentPageFormDescriptor descriptor = paymentGatewayApi.buildFormDescriptorWithPaymentControl(account, paymentMethodId, customFields, pluginProperties, paymentOptions, callContext);
final HostedPaymentPageFormDescriptorJson result = new HostedPaymentPageFormDescriptorJson(descriptor);
return Response.status(Response.Status.OK).entity(result).build();
@@ -161,6 +166,7 @@ public class PaymentGatewayResource extends ComboPaymentResource {
@ApiResponses(value = {})
public Response processNotification(final String body,
@PathParam(QUERY_PAYMENT_PLUGIN_NAME) final String pluginName,
+ @QueryParam(QUERY_PAYMENT_CONTROL_PLUGIN_NAME) final List<String> paymentControlPluginNames,
@QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
@HeaderParam(HDR_CREATED_BY) final String createdBy,
@HeaderParam(HDR_REASON) final String reason,
@@ -168,6 +174,7 @@ public class PaymentGatewayResource extends ComboPaymentResource {
@javax.ws.rs.core.Context final UriInfo uriInfo,
@javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException {
final Iterable<PluginProperty> pluginProperties = extractPluginProperties(pluginPropertiesString);
+ final PaymentOptions paymentOptions = createControlPluginApiPaymentOptions(paymentControlPluginNames);
final CallContext callContext = context.createContext(createdBy, reason, comment, request);
final String notificationPayload;
@@ -178,7 +185,7 @@ public class PaymentGatewayResource extends ComboPaymentResource {
}
// Note: the body is opaque here, as it comes from the gateway. The associated payment plugin will know how to deserialize it though
- final GatewayNotification notification = paymentGatewayApi.processNotification(notificationPayload, pluginName, pluginProperties, callContext);
+ final GatewayNotification notification = paymentGatewayApi.processNotificationWithPaymentControl(notificationPayload, pluginName, pluginProperties, paymentOptions, callContext);
final GatewayNotificationJson result = new GatewayNotificationJson(notification);
// The plugin told us how to build the response
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentResource.java
index e69e85c..804063d 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentResource.java
@@ -250,7 +250,6 @@ public class PaymentResource extends ComboPaymentResource {
@Timed
@PUT
- @Path("/")
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
@ApiOperation(value = "Complete an existing transaction")
junction/pom.xml 2(+1 -1)
diff --git a/junction/pom.xml b/junction/pom.xml
index 67b286a..2f403da 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.15.3-SNAPSHOT</version>
+ <version>0.15.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-junction</artifactId>
NEWS 3(+3 -0)
diff --git a/NEWS b/NEWS
index c5b5ee6..4118402 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,6 @@
+0.15.3
+ Release for milestone 0.15.3: https://github.com/killbill/killbill/issues?q=is%3Aissue+milestone%3ARelease-0.15.3+is%3Aclosed
+
0.15.2
See https://github.com/killbill/killbill/issues?q=milestone%3ARelease-0.15.2+is%3Aclosed
overdue/pom.xml 2(+1 -1)
diff --git a/overdue/pom.xml b/overdue/pom.xml
index 8c77086..03b322c 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.15.3-SNAPSHOT</version>
+ <version>0.15.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-overdue</artifactId>
payment/pom.xml 2(+1 -1)
diff --git a/payment/pom.xml b/payment/pom.xml
index 83e453a..ef45525 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.15.3-SNAPSHOT</version>
+ <version>0.15.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-payment</artifactId>
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentAttemptTask.java b/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentAttemptTask.java
index e7f2318..9851afb 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentAttemptTask.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentAttemptTask.java
@@ -70,11 +70,16 @@ public class IncompletePaymentAttemptTask extends CompletionTaskBase<PaymentAtte
private final PluginControlPaymentAutomatonRunner pluginControlledPaymentAutomatonRunner;
@Inject
- public IncompletePaymentAttemptTask(final InternalCallContextFactory internalCallContextFactory, final PaymentConfig paymentConfig,
- final PaymentDao paymentDao, final Clock clock, final PaymentStateMachineHelper paymentStateMachineHelper,
- final PaymentControlStateMachineHelper retrySMHelper, final AccountInternalApi accountInternalApi,
+ public IncompletePaymentAttemptTask(final InternalCallContextFactory internalCallContextFactory,
+ final PaymentConfig paymentConfig,
+ final PaymentDao paymentDao,
+ final Clock clock,
+ final PaymentStateMachineHelper paymentStateMachineHelper,
+ final PaymentControlStateMachineHelper retrySMHelper,
+ final AccountInternalApi accountInternalApi,
final PluginControlPaymentAutomatonRunner pluginControlledPaymentAutomatonRunner,
- final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry, final GlobalLocker locker) {
+ final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry,
+ final GlobalLocker locker) {
super(internalCallContextFactory, paymentConfig, paymentDao, clock, paymentStateMachineHelper, retrySMHelper, accountInternalApi, pluginRegistry, locker);
this.pluginControlledPaymentAutomatonRunner = pluginControlledPaymentAutomatonRunner;
}
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/janitor/Janitor.java b/payment/src/main/java/org/killbill/billing/payment/core/janitor/Janitor.java
index 3b06de1..fbac8e0 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/janitor/Janitor.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/janitor/Janitor.java
@@ -22,13 +22,22 @@ import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
-import javax.inject.Named;
import org.joda.time.DateTime;
+import org.killbill.billing.account.api.AccountInternalApi;
import org.killbill.billing.events.PaymentInternalEvent;
+import org.killbill.billing.osgi.api.OSGIServiceRegistration;
+import org.killbill.billing.payment.core.PaymentExecutors;
+import org.killbill.billing.payment.core.sm.PaymentControlStateMachineHelper;
+import org.killbill.billing.payment.core.sm.PaymentStateMachineHelper;
+import org.killbill.billing.payment.core.sm.PluginControlPaymentAutomatonRunner;
+import org.killbill.billing.payment.dao.PaymentDao;
import org.killbill.billing.payment.glue.DefaultPaymentService;
-import org.killbill.billing.payment.glue.PaymentModule;
+import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.clock.Clock;
+import org.killbill.commons.locker.GlobalLocker;
import org.killbill.notificationq.api.NotificationEvent;
import org.killbill.notificationq.api.NotificationQueue;
import org.killbill.notificationq.api.NotificationQueueService;
@@ -49,29 +58,76 @@ public class Janitor {
public static final String QUEUE_NAME = "janitor";
private final NotificationQueueService notificationQueueService;
- private final ScheduledExecutorService janitorExecutor;
private final PaymentConfig paymentConfig;
- private final IncompletePaymentAttemptTask incompletePaymentAttemptTask;
- private final IncompletePaymentTransactionTask incompletePaymentTransactionTask;
+ private final PaymentExecutors paymentExecutors;
+ private final Clock clock;
+ private final PaymentDao paymentDao;
+ private final InternalCallContextFactory internalCallContextFactory;
+ private final PaymentStateMachineHelper paymentStateMachineHelper;
+ private final PaymentControlStateMachineHelper retrySMHelper;
+ private final AccountInternalApi accountInternalApi;
+ private final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry;
+ private final GlobalLocker locker;
+ private final PluginControlPaymentAutomatonRunner pluginControlledPaymentAutomatonRunner;
+
+
+
+ private IncompletePaymentAttemptTask incompletePaymentAttemptTask;
+ private IncompletePaymentTransactionTask incompletePaymentTransactionTask;
private NotificationQueue janitorQueue;
+ private ScheduledExecutorService janitorExecutor;
private volatile boolean isStopped;
@Inject
- public Janitor(final PaymentConfig paymentConfig,
+ public Janitor(final InternalCallContextFactory internalCallContextFactory,
+ final PaymentDao paymentDao,
+ final Clock clock,
+ final PaymentStateMachineHelper paymentStateMachineHelper,
+ final PaymentControlStateMachineHelper retrySMHelper,
+ final AccountInternalApi accountInternalApi,
+ final PluginControlPaymentAutomatonRunner pluginControlledPaymentAutomatonRunner,
+ final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry,
+ final GlobalLocker locker,
+ final PaymentConfig paymentConfig,
final NotificationQueueService notificationQueueService,
- @Named(PaymentModule.JANITOR_EXECUTOR_NAMED) final ScheduledExecutorService janitorExecutor,
- final IncompletePaymentAttemptTask incompletePaymentAttemptTask,
- final IncompletePaymentTransactionTask incompletePaymentTransactionTask) {
+ final PaymentExecutors paymentExecutors) {
this.notificationQueueService = notificationQueueService;
- this.janitorExecutor = janitorExecutor;
+ this.paymentExecutors = paymentExecutors;
this.paymentConfig = paymentConfig;
- this.incompletePaymentAttemptTask = incompletePaymentAttemptTask;
- this.incompletePaymentTransactionTask = incompletePaymentTransactionTask;
- this.isStopped = false;
+ this.internalCallContextFactory = internalCallContextFactory;
+ this.paymentDao = paymentDao;
+ this.clock = clock;
+ this.pluginControlledPaymentAutomatonRunner = pluginControlledPaymentAutomatonRunner;
+ this.paymentStateMachineHelper = paymentStateMachineHelper;
+ this.retrySMHelper = retrySMHelper;
+ this.accountInternalApi = accountInternalApi;
+ this.pluginRegistry = pluginRegistry;
+ this.locker = locker;
+
}
+ /*
+ public IncompletePaymentAttemptTask(final InternalCallContextFactory internalCallContextFactory,
+ final PaymentConfig paymentConfig,
+ final PaymentDao paymentDao,
+ final Clock clock,
+ final PaymentStateMachineHelper paymentStateMachineHelper,
+ final PaymentControlStateMachineHelper retrySMHelper,
+ final AccountInternalApi accountInternalApi,
+ final PluginControlPaymentAutomatonRunner pluginControlledPaymentAutomatonRunner,
+ final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry,
+ final GlobalLocker locker) {
+
+
+ public IncompletePaymentTransactionTask(final InternalCallContextFactory internalCallContextFactory, final PaymentConfig paymentConfig,
+ final PaymentDao paymentDao, final Clock clock,
+ final PaymentStateMachineHelper paymentStateMachineHelper, final PaymentControlStateMachineHelper retrySMHelper, final AccountInternalApi accountInternalApi,
+ final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry, final GlobalLocker locker) {
+
+ */
+
public void initialize() throws NotificationQueueAlreadyExists {
janitorQueue = notificationQueueService.createNotificationQueue(DefaultPaymentService.SERVICE_NAME,
QUEUE_NAME,
@@ -90,15 +146,38 @@ public class Janitor {
}
}
);
+
+ this.incompletePaymentAttemptTask = new IncompletePaymentAttemptTask(internalCallContextFactory,
+ paymentConfig,
+ paymentDao,
+ clock,
+ paymentStateMachineHelper,
+ retrySMHelper,
+ accountInternalApi,
+ pluginControlledPaymentAutomatonRunner,
+ pluginRegistry,
+ locker);
+
+ this.incompletePaymentTransactionTask = new IncompletePaymentTransactionTask(internalCallContextFactory,
+ paymentConfig,
+ paymentDao,
+ clock,
+ paymentStateMachineHelper,
+ retrySMHelper,
+ accountInternalApi,
+ pluginRegistry,
+ locker);
+
+
incompletePaymentTransactionTask.attachJanitorQueue(janitorQueue);
incompletePaymentAttemptTask.attachJanitorQueue(janitorQueue);
}
public void start() {
- if (isStopped) {
- log.warn("Janitor is not a restartable service, and was already started, aborting");
- return;
- }
+
+ this.isStopped = false;
+
+ janitorExecutor = paymentExecutors.getJanitorExecutorService();
janitorQueue.startQueue();
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/PaymentExecutors.java b/payment/src/main/java/org/killbill/billing/payment/core/PaymentExecutors.java
new file mode 100644
index 0000000..bd4ca08
--- /dev/null
+++ b/payment/src/main/java/org/killbill/billing/payment/core/PaymentExecutors.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 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.payment.core;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+
+import javax.inject.Inject;
+
+import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.commons.concurrent.Executors;
+import org.killbill.commons.concurrent.WithProfilingThreadPoolExecutor;
+
+public class PaymentExecutors {
+
+ private static final long TIMEOUT_EXECUTOR_SEC = 3L;
+
+ private static final String PLUGIN_THREAD_PREFIX = "Plugin-th-";
+ private static final String PAYMENT_PLUGIN_TH_GROUP_NAME = "pay-plugin-grp";
+
+ public static final String JANITOR_EXECUTOR_NAMED = "JanitorExecutor";
+ public static final String PLUGIN_EXECUTOR_NAMED = "PluginExecutor";
+
+ private final PaymentConfig paymentConfig;
+
+ private volatile ExecutorService pluginExecutorService;
+ private volatile ScheduledExecutorService janitorExecutorService;
+
+ @Inject
+ public PaymentExecutors(PaymentConfig paymentConfig) {
+ this.paymentConfig = paymentConfig;
+
+ }
+
+ public void initialize() {
+ this.pluginExecutorService = createPluginExecutorService();
+ this.janitorExecutorService = createJanitorExecutorService();
+ }
+
+
+ public void stop() throws InterruptedException {
+ pluginExecutorService.shutdownNow();
+ janitorExecutorService.shutdownNow();
+
+ pluginExecutorService.awaitTermination(TIMEOUT_EXECUTOR_SEC, TimeUnit.SECONDS);
+ pluginExecutorService = null;
+
+ janitorExecutorService.awaitTermination(TIMEOUT_EXECUTOR_SEC, TimeUnit.SECONDS);
+ janitorExecutorService = null;
+ }
+
+ public ExecutorService getPluginExecutorService() {
+ return pluginExecutorService;
+ }
+
+ public ScheduledExecutorService getJanitorExecutorService() {
+ return janitorExecutorService;
+ }
+
+ private ExecutorService createPluginExecutorService() {
+ return new WithProfilingThreadPoolExecutor(paymentConfig.getPaymentPluginThreadNb(),
+ paymentConfig.getPaymentPluginThreadNb(),
+ 0L,
+ TimeUnit.MILLISECONDS,
+ new LinkedBlockingQueue<Runnable>(),
+ new ThreadFactory() {
+
+ @Override
+ public Thread newThread(final Runnable r) {
+ final Thread th = new Thread(new ThreadGroup(PAYMENT_PLUGIN_TH_GROUP_NAME), r);
+ th.setName(PLUGIN_THREAD_PREFIX + th.getId());
+ return th;
+ }
+ });
+
+ }
+
+ private ScheduledExecutorService createJanitorExecutorService() {
+ return Executors.newSingleThreadScheduledExecutor("PaymentJanitor");
+ }
+}
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/PaymentGatewayProcessor.java b/payment/src/main/java/org/killbill/billing/payment/core/PaymentGatewayProcessor.java
index dbc7541..cf9e41f 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/PaymentGatewayProcessor.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/PaymentGatewayProcessor.java
@@ -19,7 +19,6 @@ package org.killbill.billing.payment.core;
import java.util.UUID;
import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
@@ -47,13 +46,8 @@ import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.config.PaymentConfig;
import org.killbill.clock.Clock;
import org.killbill.commons.locker.GlobalLocker;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import com.google.common.base.Objects;
-import com.google.inject.name.Named;
-
-import static org.killbill.billing.payment.glue.PaymentModule.PLUGIN_EXECUTOR_NAMED;
// We don't take any lock here because the call needs to be re-entrant
// from the plugin: for example, the BitPay plugin will create the payment during the
@@ -65,8 +59,6 @@ public class PaymentGatewayProcessor extends ProcessorBase {
private final PluginDispatcher<HostedPaymentPageFormDescriptor> paymentPluginFormDispatcher;
private final PluginDispatcher<GatewayNotification> paymentPluginNotificationDispatcher;
- private static final Logger log = LoggerFactory.getLogger(PaymentGatewayProcessor.class);
-
@Inject
public PaymentGatewayProcessor(final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry,
final AccountInternalApi accountUserApi,
@@ -75,13 +67,13 @@ public class PaymentGatewayProcessor extends ProcessorBase {
final PaymentDao paymentDao,
final GlobalLocker locker,
final PaymentConfig paymentConfig,
- @Named(PLUGIN_EXECUTOR_NAMED) final ExecutorService executor,
+ final PaymentExecutors executors,
final InternalCallContextFactory internalCallContextFactory,
final Clock clock) {
- super(pluginRegistry, accountUserApi, paymentDao, tagUserApi, locker, executor, internalCallContextFactory, invoiceApi, clock);
+ super(pluginRegistry, accountUserApi, paymentDao, tagUserApi, locker, internalCallContextFactory, invoiceApi, clock);
final long paymentPluginTimeoutSec = TimeUnit.SECONDS.convert(paymentConfig.getPaymentPluginTimeout().getPeriod(), paymentConfig.getPaymentPluginTimeout().getUnit());
- this.paymentPluginFormDispatcher = new PluginDispatcher<HostedPaymentPageFormDescriptor>(paymentPluginTimeoutSec, executor);
- this.paymentPluginNotificationDispatcher = new PluginDispatcher<GatewayNotification>(paymentPluginTimeoutSec, executor);
+ this.paymentPluginFormDispatcher = new PluginDispatcher<HostedPaymentPageFormDescriptor>(paymentPluginTimeoutSec, executors);
+ this.paymentPluginNotificationDispatcher = new PluginDispatcher<GatewayNotification>(paymentPluginTimeoutSec, executors);
}
public GatewayNotification processNotification(final String notification, final String pluginName, final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentApiException {
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/PaymentMethodProcessor.java b/payment/src/main/java/org/killbill/billing/payment/core/PaymentMethodProcessor.java
index 4babea1..102c3d2 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/PaymentMethodProcessor.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/PaymentMethodProcessor.java
@@ -23,7 +23,6 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
-import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
@@ -52,6 +51,7 @@ import org.killbill.billing.payment.provider.DefaultNoOpPaymentMethodPlugin;
import org.killbill.billing.payment.provider.DefaultPaymentMethodInfoPlugin;
import org.killbill.billing.payment.provider.ExternalPaymentProviderPlugin;
import org.killbill.billing.tag.TagInternalApi;
+import org.killbill.billing.util.UUIDs;
import org.killbill.billing.util.callcontext.CallContext;
import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.callcontext.TenantContext;
@@ -69,10 +69,7 @@ import com.google.common.base.Objects;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
import com.google.inject.Inject;
-import com.google.inject.name.Named;
-import static org.killbill.billing.payment.glue.PaymentModule.PLUGIN_EXECUTOR_NAMED;
-import org.killbill.billing.util.UUIDs;
import static org.killbill.billing.util.entity.dao.DefaultPaginationHelper.getEntityPagination;
import static org.killbill.billing.util.entity.dao.DefaultPaginationHelper.getEntityPaginationFromPlugins;
@@ -90,12 +87,12 @@ public class PaymentMethodProcessor extends ProcessorBase {
final TagInternalApi tagUserApi,
final GlobalLocker locker,
final PaymentConfig paymentConfig,
- @Named(PLUGIN_EXECUTOR_NAMED) final ExecutorService executor,
+ final PaymentExecutors executors,
final InternalCallContextFactory internalCallContextFactory,
final Clock clock) {
- super(pluginRegistry, accountInternalApi, paymentDao, tagUserApi, locker, executor, internalCallContextFactory, invoiceApi, clock);
+ super(pluginRegistry, accountInternalApi, paymentDao, tagUserApi, locker, internalCallContextFactory, invoiceApi, clock);
final long paymentPluginTimeoutSec = TimeUnit.SECONDS.convert(paymentConfig.getPaymentPluginTimeout().getPeriod(), paymentConfig.getPaymentPluginTimeout().getUnit());
- this.uuidPluginNotificationDispatcher = new PluginDispatcher<UUID>(paymentPluginTimeoutSec, executor);
+ this.uuidPluginNotificationDispatcher = new PluginDispatcher<UUID>(paymentPluginTimeoutSec, executors);
}
public UUID addPaymentMethod(final String paymentMethodExternalKey, final String paymentPluginServiceName, final Account account,
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/PaymentProcessor.java b/payment/src/main/java/org/killbill/billing/payment/core/PaymentProcessor.java
index 3326bd3..7b39cf1 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/PaymentProcessor.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/PaymentProcessor.java
@@ -27,7 +27,6 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
-import java.util.concurrent.ExecutorService;
import javax.annotation.Nullable;
import javax.inject.Inject;
@@ -75,9 +74,7 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Ordering;
-import com.google.inject.name.Named;
-import static org.killbill.billing.payment.glue.PaymentModule.PLUGIN_EXECUTOR_NAMED;
import static org.killbill.billing.util.entity.dao.DefaultPaginationHelper.getEntityPagination;
import static org.killbill.billing.util.entity.dao.DefaultPaginationHelper.getEntityPaginationFromPlugins;
@@ -98,11 +95,10 @@ public class PaymentProcessor extends ProcessorBase {
final PaymentDao paymentDao,
final InternalCallContextFactory internalCallContextFactory,
final GlobalLocker locker,
- @Named(PLUGIN_EXECUTOR_NAMED) final ExecutorService executor,
final PaymentAutomatonRunner paymentAutomatonRunner,
final IncompletePaymentTransactionTask incompletePaymentTransactionTask,
final Clock clock) {
- super(pluginRegistry, accountUserApi, paymentDao, tagUserApi, locker, executor, internalCallContextFactory, invoiceApi, clock);
+ super(pluginRegistry, accountUserApi, paymentDao, tagUserApi, locker, internalCallContextFactory, invoiceApi, clock);
this.paymentAutomatonRunner = paymentAutomatonRunner;
this.incompletePaymentTransactionTask = incompletePaymentTransactionTask;
}
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 fd66fe9..96a6eda 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
@@ -21,7 +21,6 @@ import java.math.BigDecimal;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
-import java.util.concurrent.ExecutorService;
import javax.annotation.Nullable;
import javax.inject.Inject;
@@ -55,9 +54,6 @@ import org.killbill.commons.locker.GlobalLocker;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
-import com.google.inject.name.Named;
-
-import static org.killbill.billing.payment.glue.PaymentModule.PLUGIN_EXECUTOR_NAMED;
public class PluginControlPaymentProcessor extends ProcessorBase {
@@ -73,12 +69,11 @@ public class PluginControlPaymentProcessor extends ProcessorBase {
final TagInternalApi tagUserApi,
final PaymentDao paymentDao,
final GlobalLocker locker,
- @Named(PLUGIN_EXECUTOR_NAMED) final ExecutorService executor,
final InternalCallContextFactory internalCallContextFactory,
final PluginControlPaymentAutomatonRunner pluginControlledPaymentAutomatonRunner,
final PaymentControlStateMachineHelper paymentControlStateMachineHelper,
final Clock clock) {
- super(pluginRegistry, accountInternalApi, paymentDao, tagUserApi, locker, executor, internalCallContextFactory, invoiceApi, clock);
+ super(pluginRegistry, accountInternalApi, paymentDao, tagUserApi, locker, internalCallContextFactory, invoiceApi, clock);
this.paymentControlStateMachineHelper = paymentControlStateMachineHelper;
this.pluginControlledPaymentAutomatonRunner = pluginControlledPaymentAutomatonRunner;
}
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/ProcessorBase.java b/payment/src/main/java/org/killbill/billing/payment/core/ProcessorBase.java
index f8c2d67..e657269 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/ProcessorBase.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/ProcessorBase.java
@@ -37,10 +37,8 @@ import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.invoice.api.InvoiceInternalApi;
import org.killbill.billing.osgi.api.OSGIServiceRegistration;
import org.killbill.billing.payment.api.PaymentApiException;
-import org.killbill.billing.payment.api.TransactionStatus;
import org.killbill.billing.payment.dao.PaymentDao;
import org.killbill.billing.payment.dao.PaymentMethodModelDao;
-import org.killbill.billing.payment.dao.PaymentTransactionModelDao;
import org.killbill.billing.payment.dispatcher.PluginDispatcher;
import org.killbill.billing.payment.dispatcher.PluginDispatcher.PluginDispatcherReturnType;
import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
@@ -61,9 +59,7 @@ import org.slf4j.LoggerFactory;
import com.google.common.base.Function;
import com.google.common.base.Objects;
-import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
-import com.google.common.collect.Iterables;
public abstract class ProcessorBase {
@@ -72,7 +68,6 @@ public abstract class ProcessorBase {
protected final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry;
protected final AccountInternalApi accountInternalApi;
protected final GlobalLocker locker;
- protected final ExecutorService executor;
protected final PaymentDao paymentDao;
protected final InternalCallContextFactory internalCallContextFactory;
protected final TagInternalApi tagInternalApi;
@@ -86,7 +81,6 @@ public abstract class ProcessorBase {
final PaymentDao paymentDao,
final TagInternalApi tagInternalApi,
final GlobalLocker locker,
- final ExecutorService executor,
final InternalCallContextFactory internalCallContextFactory,
final InvoiceInternalApi invoiceApi,
final Clock clock) {
@@ -94,7 +88,6 @@ public abstract class ProcessorBase {
this.accountInternalApi = accountInternalApi;
this.paymentDao = paymentDao;
this.locker = locker;
- this.executor = executor;
this.tagInternalApi = tagInternalApi;
this.internalCallContextFactory = internalCallContextFactory;
this.invoiceApi = invoiceApi;
@@ -166,6 +159,7 @@ public abstract class ProcessorBase {
// TODO Rename - there is no lock!
public interface WithAccountLockCallback<PluginDispatcherReturnType, ExceptionType extends Exception> {
+
public PluginDispatcherReturnType doOperation() throws ExceptionType;
}
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/ControlPluginRunner.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/ControlPluginRunner.java
index 41c37a2..9d037f9 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/ControlPluginRunner.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/ControlPluginRunner.java
@@ -73,7 +73,7 @@ public class ControlPluginRunner {
final Iterable<PluginProperty> pluginProperties,
final CallContext callContext) throws PaymentControlApiException {
// Return as soon as the first plugin aborts, or the last result for the last plugin
- PriorPaymentControlResult prevResult = null;
+ PriorPaymentControlResult prevResult = new DefaultPriorPaymentControlResult(false, amount, currency, paymentMethodId, pluginProperties);
// Those values are adjusted prior each call with the result of what previous call to plugin returned
Iterable<PluginProperty> inputPluginProperties = pluginProperties;
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 4353d9e..d0f56a0 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
@@ -19,7 +19,6 @@ package org.killbill.billing.payment.core.sm;
import java.math.BigDecimal;
import java.util.UUID;
-import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@@ -36,7 +35,6 @@ import org.killbill.automaton.State;
import org.killbill.automaton.State.EnteringStateCallback;
import org.killbill.automaton.State.LeavingStateCallback;
import org.killbill.automaton.StateMachine;
-import org.killbill.automaton.StateMachineConfig;
import org.killbill.billing.ErrorCode;
import org.killbill.billing.account.api.Account;
import org.killbill.billing.callcontext.InternalCallContext;
@@ -45,6 +43,7 @@ import org.killbill.billing.osgi.api.OSGIServiceRegistration;
import org.killbill.billing.payment.api.PaymentApiException;
import org.killbill.billing.payment.api.PluginProperty;
import org.killbill.billing.payment.api.TransactionType;
+import org.killbill.billing.payment.core.PaymentExecutors;
import org.killbill.billing.payment.core.sm.payments.AuthorizeCompleted;
import org.killbill.billing.payment.core.sm.payments.AuthorizeInitiated;
import org.killbill.billing.payment.core.sm.payments.AuthorizeOperation;
@@ -69,7 +68,6 @@ import org.killbill.billing.payment.core.sm.payments.VoidOperation;
import org.killbill.billing.payment.dao.PaymentDao;
import org.killbill.billing.payment.dao.PaymentModelDao;
import org.killbill.billing.payment.dispatcher.PluginDispatcher;
-import org.killbill.billing.payment.glue.PaymentModule;
import org.killbill.billing.payment.invoice.InvoicePaymentControlPluginApi;
import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
import org.killbill.billing.util.callcontext.CallContext;
@@ -82,9 +80,6 @@ import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
-import com.google.inject.name.Named;
-
-import static org.killbill.billing.payment.glue.PaymentModule.PLUGIN_EXECUTOR_NAMED;
public class PaymentAutomatonRunner {
@@ -97,13 +92,12 @@ public class PaymentAutomatonRunner {
private final PersistentBus eventBus;
@Inject
- public PaymentAutomatonRunner(@javax.inject.Named(PaymentModule.STATE_MACHINE_PAYMENT) final StateMachineConfig stateMachineConfig,
- final PaymentConfig paymentConfig,
+ public PaymentAutomatonRunner(final PaymentConfig paymentConfig,
final PaymentDao paymentDao,
final GlobalLocker locker,
final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry,
final Clock clock,
- @Named(PLUGIN_EXECUTOR_NAMED) final ExecutorService executor,
+ final PaymentExecutors executors,
final PersistentBus eventBus,
final PaymentStateMachineHelper paymentSMHelper) {
this.paymentSMHelper = paymentSMHelper;
@@ -114,7 +108,7 @@ public class PaymentAutomatonRunner {
this.eventBus = eventBus;
final long paymentPluginTimeoutSec = TimeUnit.SECONDS.convert(paymentConfig.getPaymentPluginTimeout().getPeriod(), paymentConfig.getPaymentPluginTimeout().getUnit());
- this.paymentPluginDispatcher = new PluginDispatcher<OperationResult>(paymentPluginTimeoutSec, executor);
+ this.paymentPluginDispatcher = new PluginDispatcher<OperationResult>(paymentPluginTimeoutSec, executors);
}
@@ -125,20 +119,23 @@ public class PaymentAutomatonRunner {
final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
final DateTime utcNow = clock.getUTCNow();
- final PaymentStateContext paymentStateContext = new PaymentStateContext(isApiPayment, paymentId, transactionId, attemptId, paymentExternalKey, paymentTransactionExternalKey, transactionType,
+ // Retrieve the payment id from the payment external key if needed
+ final UUID effectivePaymentId = paymentId != null ? paymentId : retrievePaymentId(paymentExternalKey, internalCallContext);
+
+ final PaymentStateContext paymentStateContext = new PaymentStateContext(isApiPayment, effectivePaymentId, transactionId, attemptId, paymentExternalKey, paymentTransactionExternalKey, transactionType,
account, paymentMethodId, amount, currency, shouldLockAccount, overridePluginOperationResult, properties, internalCallContext, callContext);
final PaymentAutomatonDAOHelper daoHelper = new PaymentAutomatonDAOHelper(paymentStateContext, utcNow, paymentDao, pluginRegistry, internalCallContext, eventBus, paymentSMHelper);
final UUID effectivePaymentMethodId;
final String currentStateName;
- if (paymentId != null) {
+ if (effectivePaymentId != null) {
final PaymentModelDao paymentModelDao = daoHelper.getPayment();
effectivePaymentMethodId = paymentModelDao.getPaymentMethodId();
currentStateName = paymentModelDao.getLastSuccessStateName() != null ? paymentModelDao.getLastSuccessStateName() : paymentSMHelper.getInitStateNameForTransaction();
// Check for illegal states (should never happen)
- Preconditions.checkState(currentStateName != null, "State name cannot be null for payment " + paymentId);
+ Preconditions.checkState(currentStateName != null, "State name cannot be null for payment " + effectivePaymentId);
Preconditions.checkState(paymentMethodId == null || effectivePaymentMethodId.equals(paymentMethodId), "Specified payment method id " + paymentMethodId + " doesn't match the one on the payment " + effectivePaymentMethodId);
} else {
// If the payment method is not specified, retrieve the default one on the account; it could still be null, in which case
@@ -242,4 +239,13 @@ public class PaymentAutomatonRunner {
return invoiceProperty == null || invoiceProperty.getValue() == null ? null : invoiceProperty.getValue().toString();
}
+
+ private UUID retrievePaymentId(@Nullable final String paymentExternalKey, final InternalCallContext internalCallContext) {
+ if (paymentExternalKey == null) {
+ return null;
+ }
+
+ final PaymentModelDao payment = paymentDao.getPaymentByExternalKey(paymentExternalKey, internalCallContext);
+ return payment == null ? null : payment.getId();
+ }
}
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/PaymentLeavingStateCallback.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/PaymentLeavingStateCallback.java
index 6fb17d7..edc6f70 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/PaymentLeavingStateCallback.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/PaymentLeavingStateCallback.java
@@ -71,21 +71,12 @@ public abstract class PaymentLeavingStateCallback implements LeavingStateCallbac
existingPaymentTransactions = ImmutableList.of();
}
+ // Validate the payment transactions belong to the right payment
+ validatePaymentId(existingPaymentTransactions);
+
// Validate some constraints on the unicity of that paymentTransactionExternalKey
validateUniqueTransactionExternalKey(existingPaymentTransactions);
- // Handle UNKNOWN cases, where we skip the whole state machine and let the getPayment (through Janitor) logic refresh the state.
- final PaymentTransactionModelDao unknownPaymentTransaction = getUnknownPaymentTransaction(existingPaymentTransactions);
- if (unknownPaymentTransaction != null) {
- // Reset the attemptId on the existing paymentTransaction row since it is not accurate
- unknownPaymentTransaction.setAttemptId(paymentStateContext.getAttemptId());
- // Set the current paymentTransaction in the context (needed for the state machine logic)
- paymentStateContext.setPaymentTransactionModelDao(unknownPaymentTransaction);
- // Set special flag to bypass the state machine altogether (plugin will not be called, state will not be updated, no event will be sent unless state is fixed)
- paymentStateContext.setSkipOperationForUnknownTransaction(true);
- return;
- }
-
// Handle PENDING cases, where we want to re-use the same transaction
final PaymentTransactionModelDao pendingPaymentTransaction = getPendingPaymentTransaction(existingPaymentTransactions);
if (pendingPaymentTransaction != null) {
@@ -94,7 +85,7 @@ public abstract class PaymentLeavingStateCallback implements LeavingStateCallbac
return;
}
- // At this point we are left with PAYMENT_FAILURE, PLUGIN_FAILURE or nothing, and we validated the uniquess of the paymentTransactionExternalKey so we will create a new row
+ // At this point we are left with PAYMENT_FAILURE, PLUGIN_FAILURE or nothing, and we validated the uniqueness of the paymentTransactionExternalKey so we will create a new row
daoHelper.createNewPaymentTransaction();
} catch (PaymentApiException e) {
@@ -141,4 +132,13 @@ public abstract class PaymentLeavingStateCallback implements LeavingStateCallbac
throw new PaymentApiException(ErrorCode.PAYMENT_ACTIVE_TRANSACTION_KEY_EXISTS, paymentStateContext.getPaymentTransactionExternalKey());
}
}
+
+ // At this point, the payment id should have been populated for follow-up transactions (see PaymentAutomationRunner#run)
+ protected void validatePaymentId(final List<PaymentTransactionModelDao> existingPaymentTransactions) throws PaymentApiException {
+ for (final PaymentTransactionModelDao paymentTransactionModelDao : existingPaymentTransactions) {
+ if (!paymentTransactionModelDao.getPaymentId().equals(paymentStateContext.getPaymentId())) {
+ throw new PaymentApiException(ErrorCode.PAYMENT_INVALID_PARAMETER, paymentTransactionModelDao.getId(), "does not belong to payment " + paymentStateContext.getPaymentId());
+ }
+ }
+ }
}
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/PluginControlPaymentAutomatonRunner.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/PluginControlPaymentAutomatonRunner.java
index f3ca0ca..4f58817 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/PluginControlPaymentAutomatonRunner.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/PluginControlPaymentAutomatonRunner.java
@@ -20,7 +20,6 @@ package org.killbill.billing.payment.core.sm;
import java.math.BigDecimal;
import java.util.List;
import java.util.UUID;
-import java.util.concurrent.ExecutorService;
import javax.annotation.Nullable;
import javax.inject.Inject;
@@ -32,16 +31,17 @@ import org.killbill.automaton.OperationException;
import org.killbill.automaton.State;
import org.killbill.automaton.State.EnteringStateCallback;
import org.killbill.automaton.State.LeavingStateCallback;
-import org.killbill.automaton.StateMachineConfig;
import org.killbill.billing.ErrorCode;
import org.killbill.billing.account.api.Account;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.control.plugin.api.PaymentControlPluginApi;
import org.killbill.billing.osgi.api.OSGIServiceRegistration;
import org.killbill.billing.payment.api.Payment;
import org.killbill.billing.payment.api.PaymentApiException;
import org.killbill.billing.payment.api.PluginProperty;
import org.killbill.billing.payment.api.TransactionType;
+import org.killbill.billing.payment.core.PaymentExecutors;
import org.killbill.billing.payment.core.PaymentProcessor;
import org.killbill.billing.payment.core.sm.control.AuthorizeControlOperation;
import org.killbill.billing.payment.core.sm.control.CaptureControlOperation;
@@ -57,10 +57,8 @@ import org.killbill.billing.payment.core.sm.control.PurchaseControlOperation;
import org.killbill.billing.payment.core.sm.control.RefundControlOperation;
import org.killbill.billing.payment.core.sm.control.VoidControlOperation;
import org.killbill.billing.payment.dao.PaymentDao;
-import org.killbill.billing.payment.glue.PaymentModule;
import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
import org.killbill.billing.payment.retry.BaseRetryService.RetryServiceScheduler;
-import org.killbill.billing.control.plugin.api.PaymentControlPluginApi;
import org.killbill.billing.util.callcontext.CallContext;
import org.killbill.billing.util.config.PaymentConfig;
import org.killbill.bus.api.PersistentBus;
@@ -70,7 +68,6 @@ import org.killbill.commons.locker.GlobalLocker;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Objects;
-import static org.killbill.billing.payment.glue.PaymentModule.PLUGIN_EXECUTOR_NAMED;
import static org.killbill.billing.payment.glue.PaymentModule.RETRYABLE_NAMED;
public class PluginControlPaymentAutomatonRunner extends PaymentAutomatonRunner {
@@ -82,11 +79,11 @@ public class PluginControlPaymentAutomatonRunner extends PaymentAutomatonRunner
private final ControlPluginRunner controlPluginRunner;
@Inject
- public PluginControlPaymentAutomatonRunner(@Named(PaymentModule.STATE_MACHINE_PAYMENT) final StateMachineConfig stateMachineConfig, final PaymentDao paymentDao, final GlobalLocker locker, final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry,
+ public PluginControlPaymentAutomatonRunner(final PaymentDao paymentDao, final GlobalLocker locker, final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry,
final OSGIServiceRegistration<PaymentControlPluginApi> paymentControlPluginRegistry, final Clock clock, final PaymentProcessor paymentProcessor, @Named(RETRYABLE_NAMED) final RetryServiceScheduler retryServiceScheduler,
- final PaymentConfig paymentConfig, @Named(PLUGIN_EXECUTOR_NAMED) final ExecutorService executor, final PaymentStateMachineHelper paymentSMHelper, final PaymentControlStateMachineHelper paymentControlStateMachineHelper,
+ final PaymentConfig paymentConfig, final PaymentExecutors executors, final PaymentStateMachineHelper paymentSMHelper, final PaymentControlStateMachineHelper paymentControlStateMachineHelper,
final ControlPluginRunner controlPluginRunner, final PersistentBus eventBus) {
- super(stateMachineConfig, paymentConfig, paymentDao, locker, pluginRegistry, clock, executor, eventBus, paymentSMHelper);
+ super(paymentConfig, paymentDao, locker, pluginRegistry, clock, executors, eventBus, paymentSMHelper);
this.paymentProcessor = paymentProcessor;
this.paymentControlPluginRegistry = paymentControlPluginRegistry;
this.retryServiceScheduler = retryServiceScheduler;
diff --git a/payment/src/main/java/org/killbill/billing/payment/dispatcher/CallableWithRequestData.java b/payment/src/main/java/org/killbill/billing/payment/dispatcher/CallableWithRequestData.java
new file mode 100644
index 0000000..e78d8ad
--- /dev/null
+++ b/payment/src/main/java/org/killbill/billing/payment/dispatcher/CallableWithRequestData.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 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.payment.dispatcher;
+
+import java.util.concurrent.Callable;
+
+import org.killbill.commons.request.Request;
+import org.killbill.commons.request.RequestData;
+
+public class CallableWithRequestData<T> implements Callable<T> {
+
+ private final RequestData requestData;
+ private final Callable<T> delegate;
+
+ public CallableWithRequestData(final RequestData requestData, final Callable<T> delegate) {
+ this.requestData = requestData;
+ this.delegate = delegate;
+ }
+
+ @Override
+ public T call() throws Exception {
+ try {
+ Request.setPerThreadRequestData(requestData);
+ return delegate.call();
+ } finally {
+ Request.resetPerThreadRequestData();
+ }
+ }
+}
diff --git a/payment/src/main/java/org/killbill/billing/payment/dispatcher/PluginDispatcher.java b/payment/src/main/java/org/killbill/billing/payment/dispatcher/PluginDispatcher.java
index a60eff6..bda52e0 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dispatcher/PluginDispatcher.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dispatcher/PluginDispatcher.java
@@ -20,24 +20,27 @@ package org.killbill.billing.payment.dispatcher;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import org.killbill.billing.payment.core.PaymentExecutors;
import org.killbill.commons.profiling.Profiling;
import org.killbill.commons.profiling.ProfilingData;
+import org.killbill.commons.request.Request;
public class PluginDispatcher<ReturnType> {
private final TimeUnit DEEFAULT_PLUGIN_TIMEOUT_UNIT = TimeUnit.SECONDS;
private final long timeoutSeconds;
- private final ExecutorService executor;
+ private final PaymentExecutors paymentExecutors;
- public PluginDispatcher(final long timeoutSeconds, final ExecutorService executor) {
+ public PluginDispatcher(final long timeoutSeconds, final PaymentExecutors paymentExecutors) {
this.timeoutSeconds = timeoutSeconds;
- this.executor = executor;
+ this.paymentExecutors = paymentExecutors;
}
// TODO Once we switch fully to automata, should this throw PaymentPluginApiException instead?
@@ -48,7 +51,12 @@ public class PluginDispatcher<ReturnType> {
public ReturnType dispatchWithTimeout(final Callable<PluginDispatcherReturnType<ReturnType>> task, final long timeout, final TimeUnit unit)
throws TimeoutException, ExecutionException, InterruptedException {
- final Future<PluginDispatcherReturnType<ReturnType>> future = executor.submit(task);
+ final ExecutorService pluginExecutor = paymentExecutors.getPluginExecutorService();
+
+ // Wrap existing callable to keep the original requestId
+ final Callable<PluginDispatcherReturnType<ReturnType>> callableWithRequestData = new CallableWithRequestData(Request.getPerThreadRequestData(), task);
+
+ final Future<PluginDispatcherReturnType<ReturnType>> future = pluginExecutor.submit(callableWithRequestData);
final PluginDispatcherReturnType<ReturnType> pluginDispatcherResult = future.get(timeout, unit);
if (pluginDispatcherResult instanceof WithProfilingPluginDispatcherReturnType) {
diff --git a/payment/src/main/java/org/killbill/billing/payment/glue/DefaultPaymentService.java b/payment/src/main/java/org/killbill/billing/payment/glue/DefaultPaymentService.java
index d003f74..93603b1 100644
--- a/payment/src/main/java/org/killbill/billing/payment/glue/DefaultPaymentService.java
+++ b/payment/src/main/java/org/killbill/billing/payment/glue/DefaultPaymentService.java
@@ -21,6 +21,7 @@ package org.killbill.billing.payment.glue;
import org.killbill.billing.payment.api.PaymentApi;
import org.killbill.billing.payment.api.PaymentService;
import org.killbill.billing.payment.bus.PaymentBusEventHandler;
+import org.killbill.billing.payment.core.PaymentExecutors;
import org.killbill.billing.payment.invoice.PaymentTagHandler;
import org.killbill.billing.payment.core.janitor.Janitor;
import org.killbill.billing.payment.retry.DefaultRetryService;
@@ -46,6 +47,7 @@ public class DefaultPaymentService implements PaymentService {
private final PaymentApi api;
private final DefaultRetryService retryService;
private final Janitor janitor;
+ private final PaymentExecutors paymentExecutors;
@Inject
public DefaultPaymentService(final PaymentBusEventHandler paymentBusEventHandler,
@@ -53,13 +55,15 @@ public class DefaultPaymentService implements PaymentService {
final PaymentApi api,
final DefaultRetryService retryService,
final PersistentBus eventBus,
- final Janitor janitor) {
+ final Janitor janitor,
+ final PaymentExecutors paymentExecutors) {
this.paymentBusEventHandler = paymentBusEventHandler;
this.tagHandler = tagHandler;
this.eventBus = eventBus;
this.api = api;
this.retryService = retryService;
this.janitor = janitor;
+ this.paymentExecutors = paymentExecutors;
}
@Override
@@ -75,6 +79,7 @@ public class DefaultPaymentService implements PaymentService {
} catch (final PersistentBus.EventBusException e) {
log.error("Unable to register with the EventBus!", e);
}
+ paymentExecutors.initialize();
retryService.initialize();
janitor.initialize();
}
@@ -95,6 +100,12 @@ public class DefaultPaymentService implements PaymentService {
}
retryService.stop();
janitor.stop();
+ try {
+ paymentExecutors.stop();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ log.error("PaymentService got interrupted", e);
+ }
}
@Override
diff --git a/payment/src/main/java/org/killbill/billing/payment/glue/PaymentModule.java b/payment/src/main/java/org/killbill/billing/payment/glue/PaymentModule.java
index 0c0b0a7..c9a0c74 100644
--- a/payment/src/main/java/org/killbill/billing/payment/glue/PaymentModule.java
+++ b/payment/src/main/java/org/killbill/billing/payment/glue/PaymentModule.java
@@ -18,16 +18,11 @@
package org.killbill.billing.payment.glue;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.TimeUnit;
-
import javax.inject.Provider;
import org.killbill.automaton.DefaultStateMachineConfig;
import org.killbill.automaton.StateMachineConfig;
+import org.killbill.billing.control.plugin.api.PaymentControlPluginApi;
import org.killbill.billing.osgi.api.OSGIServiceRegistration;
import org.killbill.billing.payment.api.AdminPaymentApi;
import org.killbill.billing.payment.api.DefaultAdminPaymentApi;
@@ -37,6 +32,7 @@ import org.killbill.billing.payment.api.PaymentApi;
import org.killbill.billing.payment.api.PaymentGatewayApi;
import org.killbill.billing.payment.api.PaymentService;
import org.killbill.billing.payment.bus.PaymentBusEventHandler;
+import org.killbill.billing.payment.core.PaymentExecutors;
import org.killbill.billing.payment.core.PaymentGatewayProcessor;
import org.killbill.billing.payment.core.PaymentMethodProcessor;
import org.killbill.billing.payment.core.PaymentProcessor;
@@ -58,10 +54,8 @@ import org.killbill.billing.payment.retry.DefaultRetryService;
import org.killbill.billing.payment.retry.DefaultRetryService.DefaultRetryServiceScheduler;
import org.killbill.billing.payment.retry.RetryService;
import org.killbill.billing.platform.api.KillbillConfigSource;
-import org.killbill.billing.control.plugin.api.PaymentControlPluginApi;
import org.killbill.billing.util.config.PaymentConfig;
import org.killbill.billing.util.glue.KillBillModule;
-import org.killbill.commons.concurrent.WithProfilingThreadPoolExecutor;
import org.killbill.xmlloader.XMLLoader;
import org.skife.config.ConfigurationObjectFactory;
@@ -73,10 +67,7 @@ import com.google.inject.name.Names;
public class PaymentModule extends KillBillModule {
- private static final String PLUGIN_THREAD_PREFIX = "Plugin-th-";
- public static final String JANITOR_EXECUTOR_NAMED = "JanitorExecutor";
- public static final String PLUGIN_EXECUTOR_NAMED = "PluginExecutor";
public static final String RETRYABLE_NAMED = "Retryable";
public static final String STATE_MACHINE_RETRY = "RetryStateMachine";
@@ -101,11 +92,6 @@ public class PaymentModule extends KillBillModule {
}
protected void installJanitor() {
- final ScheduledExecutorService janitorExecutor = org.killbill.commons.concurrent.Executors.newSingleThreadScheduledExecutor("PaymentJanitor");
- bind(ScheduledExecutorService.class).annotatedWith(Names.named(JANITOR_EXECUTOR_NAMED)).toInstance(janitorExecutor);
-
- bind(IncompletePaymentTransactionTask.class).asEagerSingleton();
- bind(IncompletePaymentAttemptTask.class).asEagerSingleton();
bind(Janitor.class).asEagerSingleton();
}
@@ -135,20 +121,6 @@ public class PaymentModule extends KillBillModule {
}
protected void installProcessors(final PaymentConfig paymentConfig) {
-
- final ExecutorService pluginExecutorService = new WithProfilingThreadPoolExecutor(paymentConfig.getPaymentPluginThreadNb(), paymentConfig.getPaymentPluginThreadNb(),
- 0L, TimeUnit.MILLISECONDS,
- new LinkedBlockingQueue<Runnable>(),
- new ThreadFactory() {
-
- @Override
- public Thread newThread(final Runnable r) {
- final Thread th = new Thread(r);
- th.setName(PLUGIN_THREAD_PREFIX + th.getId());
- return th;
- }
- });
- bind(ExecutorService.class).annotatedWith(Names.named(PLUGIN_EXECUTOR_NAMED)).toInstance(pluginExecutorService);
bind(PaymentProcessor.class).asEagerSingleton();
bind(PluginControlPaymentProcessor.class).asEagerSingleton();
bind(PaymentGatewayProcessor.class).asEagerSingleton();
@@ -170,6 +142,7 @@ public class PaymentModule extends KillBillModule {
bind(PaymentBusEventHandler.class).asEagerSingleton();
bind(PaymentTagHandler.class).asEagerSingleton();
bind(PaymentService.class).to(DefaultPaymentService.class).asEagerSingleton();
+ bind(PaymentExecutors.class).asEagerSingleton();
installPaymentProviderPlugins(paymentConfig);
installPaymentDao();
installProcessors(paymentConfig);
diff --git a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java
index 19c04f6..a5b2a5a 100644
--- a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java
+++ b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java
@@ -582,34 +582,74 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
}
}
- @Test(groups = "slow")
- public void testApiRetryWithUnknownPaymentTransaction() throws Exception {
+ @Test(groups = "slow", description = "https://github.com/killbill/killbill/issues/371")
+ public void testApiWithDuplicatePendingPaymentTransaction() throws Exception {
final BigDecimal requestedAmount = BigDecimal.TEN;
- final String paymentExternalKey = UUID.randomUUID().toString();
- final String paymentTransactionExternalKey = UUID.randomUUID().toString();
-
- final Payment badPayment = paymentApi.createAuthorization(account, account.getPaymentMethodId(), null, requestedAmount, account.getCurrency(),
- paymentExternalKey, paymentTransactionExternalKey, ImmutableList.<PluginProperty>of(), callContext);
-
- final String paymentStateName = paymentSMHelper.getErroredStateForTransaction(TransactionType.AUTHORIZE).toString();
- paymentDao.updatePaymentAndTransactionOnCompletion(account.getId(), badPayment.getId(), TransactionType.AUTHORIZE, paymentStateName, paymentStateName,
- badPayment.getTransactions().get(0).getId(), TransactionStatus.UNKNOWN, requestedAmount, account.getCurrency(),
- "eroor 64", "bad something happened", internalCallContext);
-
- final Payment payment = paymentApi.createAuthorization(account, account.getPaymentMethodId(), null, requestedAmount, account.getCurrency(),
- paymentExternalKey, paymentTransactionExternalKey, ImmutableList.<PluginProperty>of(), callContext);
-
- Assert.assertEquals(payment.getId(), badPayment.getId());
- Assert.assertEquals(payment.getExternalKey(), paymentExternalKey);
- Assert.assertEquals(payment.getExternalKey(), paymentExternalKey);
+ for (final TransactionType transactionType : ImmutableList.<TransactionType>of(TransactionType.AUTHORIZE, TransactionType.PURCHASE, TransactionType.CREDIT)) {
+ final String payment1ExternalKey = UUID.randomUUID().toString();
+ final String payment1TransactionExternalKey = UUID.randomUUID().toString();
+ final String payment2ExternalKey = UUID.randomUUID().toString();
+ final String payment2TransactionExternalKey = UUID.randomUUID().toString();
+ final String payment3TransactionExternalKey = UUID.randomUUID().toString();
+
+ final Payment pendingPayment1 = createPayment(transactionType, null, payment1ExternalKey, payment1TransactionExternalKey, requestedAmount, PaymentPluginStatus.PENDING);
+ Assert.assertNotNull(pendingPayment1);
+ Assert.assertEquals(pendingPayment1.getExternalKey(), payment1ExternalKey);
+ Assert.assertEquals(pendingPayment1.getTransactions().size(), 1);
+ Assert.assertEquals(pendingPayment1.getTransactions().get(0).getAmount().compareTo(requestedAmount), 0);
+ Assert.assertEquals(pendingPayment1.getTransactions().get(0).getProcessedAmount().compareTo(requestedAmount), 0);
+ Assert.assertEquals(pendingPayment1.getTransactions().get(0).getCurrency(), account.getCurrency());
+ Assert.assertEquals(pendingPayment1.getTransactions().get(0).getExternalKey(), payment1TransactionExternalKey);
+ Assert.assertEquals(pendingPayment1.getTransactions().get(0).getTransactionStatus(), TransactionStatus.PENDING);
+
+ // Attempt to create a second transaction for the same payment, but with a different transaction external key
+ final Payment pendingPayment2 = createPayment(transactionType, null, payment1ExternalKey, payment2TransactionExternalKey, requestedAmount, PaymentPluginStatus.PENDING);
+ Assert.assertNotNull(pendingPayment2);
+ Assert.assertEquals(pendingPayment2.getId(), pendingPayment1.getId());
+ Assert.assertEquals(pendingPayment2.getExternalKey(), payment1ExternalKey);
+ Assert.assertEquals(pendingPayment2.getTransactions().size(), 2);
+ Assert.assertEquals(pendingPayment2.getTransactions().get(0).getAmount().compareTo(requestedAmount), 0);
+ Assert.assertEquals(pendingPayment2.getTransactions().get(0).getProcessedAmount().compareTo(requestedAmount), 0);
+ Assert.assertEquals(pendingPayment2.getTransactions().get(0).getCurrency(), account.getCurrency());
+ Assert.assertEquals(pendingPayment2.getTransactions().get(0).getExternalKey(), payment1TransactionExternalKey);
+ Assert.assertEquals(pendingPayment2.getTransactions().get(0).getTransactionStatus(), TransactionStatus.PENDING);
+ Assert.assertEquals(pendingPayment2.getTransactions().get(1).getAmount().compareTo(requestedAmount), 0);
+ Assert.assertEquals(pendingPayment2.getTransactions().get(1).getProcessedAmount().compareTo(requestedAmount), 0);
+ Assert.assertEquals(pendingPayment2.getTransactions().get(1).getCurrency(), account.getCurrency());
+ Assert.assertEquals(pendingPayment2.getTransactions().get(1).getExternalKey(), payment2TransactionExternalKey);
+ Assert.assertEquals(pendingPayment2.getTransactions().get(1).getTransactionStatus(), TransactionStatus.PENDING);
+
+ try {
+ // Verify we cannot use the same transaction external key on a different payment if the payment id isn't specified
+ createPayment(transactionType, null, payment2ExternalKey, payment1TransactionExternalKey, requestedAmount, PaymentPluginStatus.PENDING);
+ Assert.fail();
+ } catch (final PaymentApiException e) {
+ Assert.assertEquals(e.getCode(), ErrorCode.PAYMENT_INVALID_PARAMETER.getCode());
+ }
- Assert.assertEquals(payment.getTransactions().size(), 1);
- Assert.assertEquals(payment.getTransactions().get(0).getTransactionStatus(), TransactionStatus.SUCCESS);
- Assert.assertEquals(payment.getTransactions().get(0).getExternalKey(), paymentTransactionExternalKey);
+ try {
+ // Verify we cannot use the same transaction external key on a different payment if the payment id isn't specified
+ createPayment(transactionType, null, payment2ExternalKey, payment2TransactionExternalKey, requestedAmount, PaymentPluginStatus.PENDING);
+ Assert.fail();
+ } catch (final PaymentApiException e) {
+ Assert.assertEquals(e.getCode(), ErrorCode.PAYMENT_INVALID_PARAMETER.getCode());
+ }
+
+ // Attempt to create a second transaction for a different payment
+ final Payment pendingPayment3 = createPayment(transactionType, null, payment2ExternalKey, payment3TransactionExternalKey, requestedAmount, PaymentPluginStatus.PENDING);
+ Assert.assertNotNull(pendingPayment3);
+ Assert.assertNotEquals(pendingPayment3.getId(), pendingPayment1.getId());
+ Assert.assertEquals(pendingPayment3.getExternalKey(), payment2ExternalKey);
+ Assert.assertEquals(pendingPayment3.getTransactions().size(), 1);
+ Assert.assertEquals(pendingPayment3.getTransactions().get(0).getAmount().compareTo(requestedAmount), 0);
+ Assert.assertEquals(pendingPayment3.getTransactions().get(0).getProcessedAmount().compareTo(requestedAmount), 0);
+ Assert.assertEquals(pendingPayment3.getTransactions().get(0).getCurrency(), account.getCurrency());
+ Assert.assertEquals(pendingPayment3.getTransactions().get(0).getExternalKey(), payment3TransactionExternalKey);
+ Assert.assertEquals(pendingPayment3.getTransactions().get(0).getTransactionStatus(), TransactionStatus.PENDING);
+ }
}
- // Example of a 3D secure payment for instance
@Test(groups = "slow")
public void testApiWithPendingPaymentTransaction() throws Exception {
for (final TransactionType transactionType : ImmutableList.<TransactionType>of(TransactionType.AUTHORIZE, TransactionType.PURCHASE, TransactionType.CREDIT)) {
diff --git a/payment/src/test/java/org/killbill/billing/payment/core/sm/control/TestControlPluginRunner.java b/payment/src/test/java/org/killbill/billing/payment/core/sm/control/TestControlPluginRunner.java
new file mode 100644
index 0000000..e48f3b4
--- /dev/null
+++ b/payment/src/test/java/org/killbill/billing/payment/core/sm/control/TestControlPluginRunner.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 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.payment.core.sm.control;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+
+import org.killbill.billing.account.api.Account;
+import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.control.plugin.api.PaymentApiType;
+import org.killbill.billing.control.plugin.api.PriorPaymentControlResult;
+import org.killbill.billing.payment.PaymentTestSuiteNoDB;
+import org.killbill.billing.payment.api.PluginProperty;
+import org.killbill.billing.payment.api.TransactionType;
+import org.killbill.billing.payment.provider.DefaultPaymentControlProviderPluginRegistry;
+import org.killbill.billing.util.UUIDs;
+import org.mockito.Mockito;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+
+public class TestControlPluginRunner extends PaymentTestSuiteNoDB {
+
+ @Test(groups = "fast")
+ public void testPriorCallWithUnknownPlugin() throws Exception {
+ final Account account = Mockito.mock(Account.class);
+ final UUID paymentMethodId = UUIDs.randomUUID();
+ final UUID paymentId = UUIDs.randomUUID();
+ final String paymentExternalKey = UUIDs.randomUUID().toString();
+ final String paymentTransactionExternalKey = UUIDs.randomUUID().toString();
+ final BigDecimal amount = BigDecimal.ONE;
+ final Currency currency = Currency.USD;
+ final ImmutableList<String> paymentControlPluginNames = ImmutableList.<String>of("not-registered");
+ final ImmutableList<PluginProperty> pluginProperties = ImmutableList.<PluginProperty>of();
+
+ final ControlPluginRunner controlPluginRunner = new ControlPluginRunner(new DefaultPaymentControlProviderPluginRegistry());
+ final PriorPaymentControlResult paymentControlResult = controlPluginRunner.executePluginPriorCalls(account,
+ paymentMethodId,
+ null,
+ paymentId,
+ paymentExternalKey,
+ paymentTransactionExternalKey,
+ PaymentApiType.PAYMENT_TRANSACTION,
+ TransactionType.AUTHORIZE,
+ null,
+ amount,
+ currency,
+ true,
+ paymentControlPluginNames,
+ pluginProperties,
+ callContext);
+ Assert.assertEquals(paymentControlResult.getAdjustedAmount(), amount);
+ Assert.assertEquals(paymentControlResult.getAdjustedCurrency(), currency);
+ Assert.assertEquals(paymentControlResult.getAdjustedPaymentMethodId(), paymentMethodId);
+ Assert.assertEquals(paymentControlResult.getAdjustedPluginProperties(), pluginProperties);
+ Assert.assertFalse(paymentControlResult.isAborted());
+ }
+}
diff --git a/payment/src/test/java/org/killbill/billing/payment/core/sm/MockRetryablePaymentAutomatonRunner.java b/payment/src/test/java/org/killbill/billing/payment/core/sm/MockRetryablePaymentAutomatonRunner.java
index 14f6758..07c8031 100644
--- a/payment/src/test/java/org/killbill/billing/payment/core/sm/MockRetryablePaymentAutomatonRunner.java
+++ b/payment/src/test/java/org/killbill/billing/payment/core/sm/MockRetryablePaymentAutomatonRunner.java
@@ -20,7 +20,6 @@ package org.killbill.billing.payment.core.sm;
import java.math.BigDecimal;
import java.util.List;
import java.util.UUID;
-import java.util.concurrent.ExecutorService;
import javax.annotation.Nullable;
import javax.inject.Inject;
@@ -28,23 +27,22 @@ import javax.inject.Named;
import org.killbill.automaton.Operation.OperationCallback;
import org.killbill.automaton.OperationResult;
-import org.killbill.automaton.StateMachineConfig;
import org.killbill.billing.account.api.Account;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.control.plugin.api.PaymentControlPluginApi;
import org.killbill.billing.osgi.api.OSGIServiceRegistration;
import org.killbill.billing.payment.api.PaymentApiException;
import org.killbill.billing.payment.api.PluginProperty;
import org.killbill.billing.payment.api.TransactionType;
+import org.killbill.billing.payment.core.PaymentExecutors;
import org.killbill.billing.payment.core.PaymentProcessor;
import org.killbill.billing.payment.core.sm.control.ControlPluginRunner;
import org.killbill.billing.payment.core.sm.control.PaymentStateControlContext;
import org.killbill.billing.payment.dao.PaymentDao;
import org.killbill.billing.payment.dispatcher.PluginDispatcher;
-import org.killbill.billing.payment.glue.PaymentModule;
import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
import org.killbill.billing.payment.retry.BaseRetryService.RetryServiceScheduler;
-import org.killbill.billing.control.plugin.api.PaymentControlPluginApi;
import org.killbill.billing.tag.TagInternalApi;
import org.killbill.billing.util.callcontext.CallContext;
import org.killbill.billing.util.config.PaymentConfig;
@@ -52,7 +50,6 @@ import org.killbill.bus.api.PersistentBus;
import org.killbill.clock.Clock;
import org.killbill.commons.locker.GlobalLocker;
-import static org.killbill.billing.payment.glue.PaymentModule.PLUGIN_EXECUTOR_NAMED;
import static org.killbill.billing.payment.glue.PaymentModule.RETRYABLE_NAMED;
public class MockRetryablePaymentAutomatonRunner extends PluginControlPaymentAutomatonRunner {
@@ -61,10 +58,10 @@ public class MockRetryablePaymentAutomatonRunner extends PluginControlPaymentAut
private PaymentStateControlContext context;
@Inject
- public MockRetryablePaymentAutomatonRunner(@Named(PaymentModule.STATE_MACHINE_PAYMENT) final StateMachineConfig stateMachineConfig, @Named(PaymentModule.STATE_MACHINE_RETRY) final StateMachineConfig retryStateMachine, final PaymentDao paymentDao, final GlobalLocker locker, final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry, final OSGIServiceRegistration<PaymentControlPluginApi> retryPluginRegistry, final Clock clock, final TagInternalApi tagApi, final PaymentProcessor paymentProcessor,
- @Named(RETRYABLE_NAMED) final RetryServiceScheduler retryServiceScheduler, final PaymentConfig paymentConfig, @com.google.inject.name.Named(PLUGIN_EXECUTOR_NAMED) final ExecutorService executor,
+ public MockRetryablePaymentAutomatonRunner(final PaymentDao paymentDao, final GlobalLocker locker, final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry, final OSGIServiceRegistration<PaymentControlPluginApi> retryPluginRegistry, final Clock clock, final TagInternalApi tagApi, final PaymentProcessor paymentProcessor,
+ @Named(RETRYABLE_NAMED) final RetryServiceScheduler retryServiceScheduler, final PaymentConfig paymentConfig, final PaymentExecutors executors,
final PaymentStateMachineHelper paymentSMHelper, final PaymentControlStateMachineHelper retrySMHelper, final ControlPluginRunner controlPluginRunner, final PersistentBus eventBus) {
- super(stateMachineConfig, paymentDao, locker, pluginRegistry, retryPluginRegistry, clock, paymentProcessor, retryServiceScheduler, paymentConfig, executor, paymentSMHelper, retrySMHelper, controlPluginRunner, eventBus);
+ super(paymentDao, locker, pluginRegistry, retryPluginRegistry, clock, paymentProcessor, retryServiceScheduler, paymentConfig, executors, paymentSMHelper, retrySMHelper, controlPluginRunner, eventBus);
}
@Override
diff --git a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentOperation.java b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentOperation.java
index 586e759..7e4e1b6 100644
--- a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentOperation.java
+++ b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentOperation.java
@@ -19,7 +19,6 @@ package org.killbill.billing.payment.core.sm;
import java.math.BigDecimal;
import java.util.UUID;
-import java.util.concurrent.Executors;
import javax.annotation.Nullable;
@@ -104,7 +103,7 @@ public class TestPaymentOperation extends PaymentTestSuiteNoDB {
private void setUp(final PaymentPluginStatus paymentPluginStatus) throws Exception {
final GlobalLocker locker = new MemoryGlobalLocker();
- final PluginDispatcher<OperationResult> paymentPluginDispatcher = new PluginDispatcher<OperationResult>(1, Executors.newCachedThreadPool());
+ final PluginDispatcher<OperationResult> paymentPluginDispatcher = new PluginDispatcher<OperationResult>(1, paymentExecutors);
paymentStateContext = new PaymentStateContext(true,
UUID.randomUUID(),
null, null,
diff --git a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPluginOperation.java b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPluginOperation.java
index f004f02..9c73489 100644
--- a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPluginOperation.java
+++ b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPluginOperation.java
@@ -184,7 +184,7 @@ public class TestPluginOperation extends PaymentTestSuiteNoDB {
}
private PaymentOperation getPluginOperation(final boolean shouldLockAccount, final int timeoutSeconds) throws PaymentApiException {
- final PluginDispatcher<OperationResult> paymentPluginDispatcher = new PluginDispatcher<OperationResult>(timeoutSeconds, Executors.newCachedThreadPool());
+ final PluginDispatcher<OperationResult> paymentPluginDispatcher = new PluginDispatcher<OperationResult>(timeoutSeconds, paymentExecutors);
final PaymentStateContext paymentStateContext = new PaymentStateContext(true, UUID.randomUUID(),
null, null,
diff --git a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestRetryablePayment.java b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestRetryablePayment.java
index 433806e..b796607 100644
--- a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestRetryablePayment.java
+++ b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestRetryablePayment.java
@@ -20,7 +20,6 @@ package org.killbill.billing.payment.core.sm;
import java.math.BigDecimal;
import java.util.List;
import java.util.UUID;
-import java.util.concurrent.ExecutorService;
import javax.inject.Named;
@@ -32,6 +31,7 @@ import org.killbill.billing.ErrorCode;
import org.killbill.billing.account.api.Account;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.control.plugin.api.PaymentControlPluginApi;
import org.killbill.billing.osgi.api.OSGIServiceDescriptor;
import org.killbill.billing.osgi.api.OSGIServiceRegistration;
import org.killbill.billing.payment.PaymentTestSuiteNoDB;
@@ -39,6 +39,7 @@ import org.killbill.billing.payment.api.PaymentApiException;
import org.killbill.billing.payment.api.PluginProperty;
import org.killbill.billing.payment.api.TransactionStatus;
import org.killbill.billing.payment.api.TransactionType;
+import org.killbill.billing.payment.core.PaymentExecutors;
import org.killbill.billing.payment.core.PaymentProcessor;
import org.killbill.billing.payment.core.PluginControlPaymentProcessor;
import org.killbill.billing.payment.core.sm.control.ControlPluginRunner;
@@ -53,7 +54,6 @@ import org.killbill.billing.payment.glue.PaymentModule;
import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
import org.killbill.billing.payment.provider.MockPaymentControlProviderPlugin;
import org.killbill.billing.payment.retry.BaseRetryService.RetryServiceScheduler;
-import org.killbill.billing.control.plugin.api.PaymentControlPluginApi;
import org.killbill.billing.tag.TagInternalApi;
import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.dao.NonEntityDao;
@@ -72,7 +72,6 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.inject.Inject;
-import static org.killbill.billing.payment.glue.PaymentModule.PLUGIN_EXECUTOR_NAMED;
import static org.killbill.billing.payment.glue.PaymentModule.RETRYABLE_NAMED;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
@@ -103,8 +102,7 @@ public class TestRetryablePayment extends PaymentTestSuiteNoDB {
@Named(RETRYABLE_NAMED)
private RetryServiceScheduler retryServiceScheduler;
@Inject
- @Named(PLUGIN_EXECUTOR_NAMED)
- private ExecutorService executor;
+ private PaymentExecutors executors;
@Inject
private PaymentStateMachineHelper paymentSMHelper;
@Inject
@@ -158,8 +156,6 @@ public class TestRetryablePayment extends PaymentTestSuiteNoDB {
this.utcNow = clock.getUTCNow();
runner = new MockRetryablePaymentAutomatonRunner(
- stateMachineConfig,
- retryStateMachineConfig,
paymentDao,
locker,
pluginRegistry,
@@ -169,7 +165,7 @@ public class TestRetryablePayment extends PaymentTestSuiteNoDB {
paymentProcessor,
retryServiceScheduler,
paymentConfig,
- executor,
+ paymentExecutors,
paymentSMHelper,
retrySMHelper,
controlPluginRunner,
@@ -177,18 +173,18 @@ public class TestRetryablePayment extends PaymentTestSuiteNoDB {
paymentStateContext =
new PaymentStateControlContext(ImmutableList.<String>of(MockPaymentControlProviderPlugin.PLUGIN_NAME),
- true,
- null,
- paymentExternalKey,
- paymentTransactionExternalKey,
- TransactionType.AUTHORIZE,
- account,
- paymentMethodId,
- amount,
- currency,
- emptyProperties,
- internalCallContext,
- callContext);
+ true,
+ null,
+ paymentExternalKey,
+ paymentTransactionExternalKey,
+ TransactionType.AUTHORIZE,
+ account,
+ paymentMethodId,
+ amount,
+ currency,
+ emptyProperties,
+ internalCallContext,
+ callContext);
mockRetryAuthorizeOperationCallback =
new MockRetryAuthorizeOperationCallback(locker,
@@ -205,7 +201,6 @@ public class TestRetryablePayment extends PaymentTestSuiteNoDB {
tagApi,
paymentDao,
locker,
- executor,
internalCallContextFactory,
runner,
retrySMHelper,
diff --git a/payment/src/test/java/org/killbill/billing/payment/dispatcher/TestPluginDispatcher.java b/payment/src/test/java/org/killbill/billing/payment/dispatcher/TestPluginDispatcher.java
index d226d73..835df39 100644
--- a/payment/src/test/java/org/killbill/billing/payment/dispatcher/TestPluginDispatcher.java
+++ b/payment/src/test/java/org/killbill/billing/payment/dispatcher/TestPluginDispatcher.java
@@ -26,12 +26,27 @@ import org.killbill.billing.ErrorCode;
import org.killbill.billing.payment.PaymentTestSuiteNoDB;
import org.killbill.billing.payment.api.PaymentApiException;
import org.killbill.billing.payment.dispatcher.PluginDispatcher.PluginDispatcherReturnType;
+import org.killbill.commons.profiling.Profiling;
+import org.killbill.commons.request.Request;
+import org.killbill.commons.request.RequestData;
import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
public class TestPluginDispatcher extends PaymentTestSuiteNoDB {
- private final PluginDispatcher<Void> voidPluginDispatcher = new PluginDispatcher<Void>(10, Executors.newSingleThreadExecutor());
+ private PluginDispatcher<Void> voidPluginDispatcher;
+
+ private PluginDispatcher<String> stringPluginDispatcher;
+
+ @BeforeMethod(groups = "fast")
+ public void beforeMethod() throws Exception {
+ super.beforeMethod();
+ eventBus.start();
+ voidPluginDispatcher = new PluginDispatcher<Void>(10, paymentExecutors);
+ stringPluginDispatcher = new PluginDispatcher<String>(1, paymentExecutors);
+ }
+
@Test(groups = "fast")
public void testDispatchWithTimeout() throws TimeoutException, PaymentApiException {
@@ -106,4 +121,25 @@ public class TestPluginDispatcher extends PaymentTestSuiteNoDB {
}
Assert.assertTrue(gotIt);
}
+
+
+ @Test(groups = "fast")
+ public void testDispatchWithRequestData() throws TimeoutException, PaymentApiException, ExecutionException, InterruptedException {
+
+ final String requestId = "vive la vie et les coquillettes";
+
+ final Callable<PluginDispatcherReturnType<String>> delegate = new Callable<PluginDispatcherReturnType<String>>() {
+ @Override
+ public PluginDispatcherReturnType<String> call() throws Exception {
+ return PluginDispatcher.<String>createPluginDispatcherReturnType(Request.getPerThreadRequestData().getRequestId());
+ }
+ };
+
+ final CallableWithRequestData<PluginDispatcherReturnType<String>> callable = new CallableWithRequestData<PluginDispatcherReturnType<String>>(new RequestData(requestId),
+ delegate);
+
+ final String actualRequestId = stringPluginDispatcher.dispatchWithTimeout(callable, 100, TimeUnit.MILLISECONDS);
+ Assert.assertEquals(actualRequestId, requestId);
+ }
+
}
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 9c19560..9af2ee5 100644
--- a/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteNoDB.java
+++ b/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteNoDB.java
@@ -24,6 +24,7 @@ import org.killbill.billing.invoice.api.InvoiceInternalApi;
import org.killbill.billing.osgi.api.OSGIServiceRegistration;
import org.killbill.billing.payment.api.PaymentApi;
import org.killbill.billing.payment.api.PaymentGatewayApi;
+import org.killbill.billing.payment.core.PaymentExecutors;
import org.killbill.billing.payment.core.PaymentMethodProcessor;
import org.killbill.billing.payment.core.PaymentProcessor;
import org.killbill.billing.payment.core.PluginControlPaymentProcessor;
@@ -82,6 +83,8 @@ public abstract class PaymentTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
protected DefaultRetryService retryService;
@Inject
protected CacheControllerDispatcher cacheControllerDispatcher;
+ @Inject
+ protected PaymentExecutors paymentExecutors;
@Override
protected KillbillConfigSource getConfigSource() {
@@ -100,11 +103,13 @@ public abstract class PaymentTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
@BeforeMethod(groups = "fast")
public void beforeMethod() throws Exception {
eventBus.start();
+ paymentExecutors.initialize();
Profiling.resetPerThreadProfilingData();
}
@AfterMethod(groups = "fast")
public void afterMethod() throws Exception {
+ 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 1ab3f64..14072c3 100644
--- a/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteWithEmbeddedDB.java
+++ b/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteWithEmbeddedDB.java
@@ -24,6 +24,7 @@ import org.killbill.billing.invoice.api.InvoiceInternalApi;
import org.killbill.billing.osgi.api.OSGIServiceRegistration;
import org.killbill.billing.payment.api.PaymentApi;
import org.killbill.billing.payment.api.PaymentGatewayApi;
+import org.killbill.billing.payment.core.PaymentExecutors;
import org.killbill.billing.payment.core.PaymentProcessor;
import org.killbill.billing.payment.core.PaymentMethodProcessor;
import org.killbill.billing.payment.core.sm.PaymentStateMachineHelper;
@@ -70,6 +71,8 @@ public abstract class PaymentTestSuiteWithEmbeddedDB extends GuicyKillbillTestSu
protected PaymentDao paymentDao;
@Inject
protected TestPaymentHelper testHelper;
+ @Inject
+ protected PaymentExecutors paymentExecutors;
@Override
protected KillbillConfigSource getConfigSource() {
@@ -87,6 +90,7 @@ public abstract class PaymentTestSuiteWithEmbeddedDB extends GuicyKillbillTestSu
@BeforeMethod(groups = "slow")
public void beforeMethod() throws Exception {
super.beforeMethod();
+ paymentExecutors.initialize();
eventBus.start();
Profiling.resetPerThreadProfilingData();
clock.resetDeltaFromReality();
@@ -96,5 +100,6 @@ public abstract class PaymentTestSuiteWithEmbeddedDB extends GuicyKillbillTestSu
@AfterMethod(groups = "slow")
public void afterMethod() throws Exception {
eventBus.stop();
+ paymentExecutors.stop();
}
}
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 7ed69f5..1753af8 100644
--- a/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java
+++ b/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java
@@ -114,18 +114,17 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
protected void beforeClass() throws Exception {
super.beforeClass();
mockPaymentProviderPlugin = (MockPaymentProviderPlugin) registry.getServiceForName(MockPaymentProviderPlugin.PLUGIN_NAME);
- janitor.initialize();
- janitor.start();
}
@AfterClass(groups = "slow")
protected void afterClass() throws Exception {
- janitor.stop();
}
@BeforeMethod(groups = "slow")
public void beforeMethod() throws Exception {
super.beforeMethod();
+ janitor.initialize();
+ janitor.start();
eventBus.register(handler);
testListener.reset();
eventBus.register(testListener);
@@ -135,6 +134,7 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
@AfterMethod(groups = "slow")
public void afterMethod() throws Exception {
+ janitor.stop();
eventBus.unregister(handler);
eventBus.unregister(testListener);
super.afterMethod();
pom.xml 4(+2 -2)
diff --git a/pom.xml b/pom.xml
index 6b6b458..2ca99c5 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.37</version>
+ <version>0.38</version>
</parent>
<artifactId>killbill</artifactId>
- <version>0.15.3-SNAPSHOT</version>
+ <version>0.15.4-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 47ce217..78f963d 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.15.3-SNAPSHOT</version>
+ <version>0.15.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-profiles-killbill</artifactId>
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/filters/RequestDataFilter.java b/profiles/killbill/src/main/java/org/killbill/billing/server/filters/RequestDataFilter.java
new file mode 100644
index 0000000..a32a507
--- /dev/null
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/filters/RequestDataFilter.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 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.filters;
+
+import java.util.List;
+
+import org.killbill.billing.util.UUIDs;
+import org.killbill.commons.request.Request;
+import org.killbill.commons.request.RequestData;
+
+import com.google.inject.Singleton;
+import com.sun.jersey.spi.container.ContainerRequest;
+import com.sun.jersey.spi.container.ContainerRequestFilter;
+import com.sun.jersey.spi.container.ContainerResponse;
+import com.sun.jersey.spi.container.ContainerResponseFilter;
+
+@Singleton
+public class RequestDataFilter implements ContainerRequestFilter, ContainerResponseFilter {
+
+
+ private static final String REQUEST_ID_HEADER_REQ = "X-Killbill-Request-Id-Req";
+
+ @Override
+ public ContainerRequest filter(final ContainerRequest request) {
+ final List<String> requestIdHeaderRequests = request.getRequestHeader(REQUEST_ID_HEADER_REQ);
+ final String requestId = (requestIdHeaderRequests == null || requestIdHeaderRequests.isEmpty()) ? UUIDs.randomUUID().toString() : requestIdHeaderRequests.get(0);
+ Request.setPerThreadRequestData(new RequestData(requestId));
+ return request;
+ }
+
+ @Override
+ public ContainerResponse filter(final ContainerRequest request, final ContainerResponse response) {
+ Request.resetPerThreadRequestData();
+ return response;
+ }
+}
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/listeners/KillbillGuiceListener.java b/profiles/killbill/src/main/java/org/killbill/billing/server/listeners/KillbillGuiceListener.java
index 6e506da..0b16384 100644
--- a/profiles/killbill/src/main/java/org/killbill/billing/server/listeners/KillbillGuiceListener.java
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/listeners/KillbillGuiceListener.java
@@ -28,6 +28,7 @@ import org.killbill.billing.jaxrs.util.KillbillEventHandler;
import org.killbill.billing.platform.api.KillbillConfigSource;
import org.killbill.billing.platform.config.DefaultKillbillConfigSource;
import org.killbill.billing.server.filters.ProfilingContainerResponseFilter;
+import org.killbill.billing.server.filters.RequestDataFilter;
import org.killbill.billing.server.filters.ResponseCorsFilter;
import org.killbill.billing.server.modules.KillbillServerModule;
import org.killbill.billing.server.security.TenantFilter;
@@ -73,6 +74,7 @@ public class KillbillGuiceListener extends KillbillPlatformGuiceListener {
// The logging filter is still incompatible with the GZIP filter
//builder.addJerseyFilter(GZIPContentEncodingFilter.class.getName());
builder.addJerseyFilter(ProfilingContainerResponseFilter.class.getName());
+ builder.addJerseyFilter(RequestDataFilter.class.getName());
// Broader, to support the "Try it out!" feature
//builder.addFilter("/" + SWAGGER_PATH + "*", ResponseCorsFilter.class);
profiles/killpay/pom.xml 2(+1 -1)
diff --git a/profiles/killpay/pom.xml b/profiles/killpay/pom.xml
index 4544cc3..231f343 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.15.3-SNAPSHOT</version>
+ <version>0.15.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-profiles-killpay</artifactId>
profiles/pom.xml 2(+1 -1)
diff --git a/profiles/pom.xml b/profiles/pom.xml
index 6f18124..88e8cd0 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.15.3-SNAPSHOT</version>
+ <version>0.15.4-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 efcb4d4..cd54166 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.15.3-SNAPSHOT</version>
+ <version>0.15.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-subscription</artifactId>
tenant/pom.xml 2(+1 -1)
diff --git a/tenant/pom.xml b/tenant/pom.xml
index aef172b..a8d2a80 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.15.3-SNAPSHOT</version>
+ <version>0.15.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-tenant</artifactId>
diff --git a/tenant/src/main/java/org/killbill/billing/tenant/api/TenantCacheInvalidation.java b/tenant/src/main/java/org/killbill/billing/tenant/api/TenantCacheInvalidation.java
index 776e6b1..6b3a2bb 100644
--- a/tenant/src/main/java/org/killbill/billing/tenant/api/TenantCacheInvalidation.java
+++ b/tenant/src/main/java/org/killbill/billing/tenant/api/TenantCacheInvalidation.java
@@ -42,6 +42,7 @@ import org.killbill.billing.tenant.glue.DefaultTenantModule;
import org.killbill.billing.util.config.TenantConfig;
import org.killbill.bus.api.PersistentBus;
import org.killbill.bus.api.PersistentBus.EventBusException;
+import org.killbill.commons.concurrent.Executors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -67,22 +68,21 @@ public class TenantCacheInvalidation {
private final Map<TenantKey, CacheInvalidationCallback> cache;
private final TenantBroadcastDao broadcastDao;
- private final ScheduledExecutorService tenantExecutor;
private final TenantConfig tenantConfig;
private final PersistentBus eventBus;
private final TenantDao tenantDao;
private AtomicLong latestRecordIdProcessed;
private volatile boolean isStopped;
+ private ScheduledExecutorService tenantExecutor;
+
@Inject
public TenantCacheInvalidation(@Named(DefaultTenantModule.NO_CACHING_TENANT) final TenantBroadcastDao broadcastDao,
- @Named(DefaultTenantModule.TENANT_EXECUTOR_NAMED) final ScheduledExecutorService tenantExecutor,
@Named(DefaultTenantModule.NO_CACHING_TENANT) final TenantDao tenantDao,
final PersistentBus eventBus,
final TenantConfig tenantConfig) {
this.cache = new HashMap<TenantKey, CacheInvalidationCallback>();
this.broadcastDao = broadcastDao;
- this.tenantExecutor = tenantExecutor;
this.tenantConfig = tenantConfig;
this.tenantDao = tenantDao;
this.eventBus = eventBus;
@@ -92,14 +92,11 @@ public class TenantCacheInvalidation {
public void initialize() {
final TenantBroadcastModelDao entry = broadcastDao.getLatestEntry();
this.latestRecordIdProcessed = entry != null ? new AtomicLong(entry.getRecordId()) : new AtomicLong(0L);
-
+ this.tenantExecutor = Executors.newSingleThreadScheduledExecutor("TenantExecutor");
+ this.isStopped = false;
}
public void start() {
- if (isStopped) {
- logger.warn("TenantExecutor is in a stopped state, abort start sequence");
- return;
- }
final TimeUnit pendingRateUnit = tenantConfig.getTenantBroadcastServiceRunningRate().getUnit();
final long pendingPeriod = tenantConfig.getTenantBroadcastServiceRunningRate().getPeriod();
tenantExecutor.scheduleAtFixedRate(new TenantCacheInvalidationRunnable(this, broadcastDao, tenantDao), pendingPeriod, pendingPeriod, pendingRateUnit);
diff --git a/tenant/src/main/java/org/killbill/billing/tenant/glue/DefaultTenantModule.java b/tenant/src/main/java/org/killbill/billing/tenant/glue/DefaultTenantModule.java
index 6770c67..af1c9f7 100644
--- a/tenant/src/main/java/org/killbill/billing/tenant/glue/DefaultTenantModule.java
+++ b/tenant/src/main/java/org/killbill/billing/tenant/glue/DefaultTenantModule.java
@@ -18,8 +18,6 @@
package org.killbill.billing.tenant.glue;
-import java.util.concurrent.ScheduledExecutorService;
-
import org.killbill.billing.glue.TenantModule;
import org.killbill.billing.platform.api.KillbillConfigSource;
import org.killbill.billing.tenant.api.DefaultTenantInternalApi;
@@ -46,8 +44,6 @@ public class DefaultTenantModule extends KillBillModule implements TenantModule
public static final String NO_CACHING_TENANT = "NoCachingTenant";
- public static final String TENANT_EXECUTOR_NAMED = "TenantExecutor";
-
public DefaultTenantModule(final KillbillConfigSource configSource) {
super(configSource);
}
@@ -79,11 +75,6 @@ public class DefaultTenantModule extends KillBillModule implements TenantModule
bind(TenantCacheInvalidation.class).asEagerSingleton();
}
- protected void installExecutor() {
- final ScheduledExecutorService tenantExecutor = org.killbill.commons.concurrent.Executors.newSingleThreadScheduledExecutor("TenantExecutor");
- bind(ScheduledExecutorService.class).annotatedWith(Names.named(TENANT_EXECUTOR_NAMED)).toInstance(tenantExecutor);
- }
-
@Override
protected void configure() {
installConfig();
@@ -91,6 +82,5 @@ public class DefaultTenantModule extends KillBillModule implements TenantModule
installTenantService();
installTenantUserApi();
installTenantCacheInvalidation();
- installExecutor();
}
}
usage/pom.xml 2(+1 -1)
diff --git a/usage/pom.xml b/usage/pom.xml
index 4cd57d8..16205bd 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.15.3-SNAPSHOT</version>
+ <version>0.15.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-usage</artifactId>
util/pom.xml 2(+1 -1)
diff --git a/util/pom.xml b/util/pom.xml
index 0a88a19..bfc65ce 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.15.3-SNAPSHOT</version>
+ <version>0.15.4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-util</artifactId>