killbill-memoizeit

Details

diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoicePaymentResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoicePaymentResource.java
index 1fb9dda..8c14531 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoicePaymentResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoicePaymentResource.java
@@ -111,6 +111,7 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
                            @ApiResponse(code = 404, message = "Payment not found")})
     public Response getInvoicePayment(@PathParam("paymentId") final String paymentIdStr,
                                       @QueryParam(QUERY_WITH_PLUGIN_INFO) @DefaultValue("false") final Boolean withPluginInfo,
+                                      @QueryParam(QUERY_WITH_ATTEMPTS) @DefaultValue("false") final Boolean withAttempts,
                                       @QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
                                       @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
                                       @javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException {
@@ -118,7 +119,7 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
         final Iterable<PluginProperty> pluginProperties = extractPluginProperties(pluginPropertiesString);
         final UUID paymentIdId = UUID.fromString(paymentIdStr);
         final TenantContext tenantContext = context.createContext(request);
-        final Payment payment = paymentApi.getPayment(paymentIdId, withPluginInfo, false, pluginProperties, tenantContext);
+        final Payment payment = paymentApi.getPayment(paymentIdId, withPluginInfo, withAttempts, pluginProperties, tenantContext);
         final AccountAuditLogs accountAuditLogs = auditUserApi.getAccountAuditLogs(payment.getAccountId(), auditMode.getLevel(), tenantContext);
 
         final List<InvoicePayment> invoicePayments = invoicePaymentApi.getInvoicePayments(paymentIdId, tenantContext);
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 a2ef19c..c57de1c 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
@@ -713,7 +713,7 @@ public class PaymentProcessor extends ProcessorBase {
 
     private PaymentAttemptModelDao getLastPaymentAttempt(final List<PaymentAttemptModelDao> pastPaymentAttempts, final UUID attemptId) {
         if (!pastPaymentAttempts.isEmpty()) {
-            for (int i = pastPaymentAttempts.size() - 1; i == 0; i--) {
+            for (int i = pastPaymentAttempts.size() - 1; i >= 0; i--) {
                 if (pastPaymentAttempts.get(i).getId().equals(attemptId)) {
                     return pastPaymentAttempts.get(i);
                 }
diff --git a/payment/src/main/java/org/killbill/billing/payment/provider/ExternalPaymentProviderPlugin.java b/payment/src/main/java/org/killbill/billing/payment/provider/ExternalPaymentProviderPlugin.java
index 33f1e30..db8a69e 100644
--- a/payment/src/main/java/org/killbill/billing/payment/provider/ExternalPaymentProviderPlugin.java
+++ b/payment/src/main/java/org/killbill/billing/payment/provider/ExternalPaymentProviderPlugin.java
@@ -36,12 +36,15 @@ import org.killbill.billing.payment.plugin.api.PaymentPluginStatus;
 import org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.TenantContext;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.billing.util.entity.DefaultPagination;
 import org.killbill.billing.util.entity.Pagination;
 import org.killbill.clock.Clock;
 
+import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
 import com.google.inject.Inject;
 
 /**
@@ -52,35 +55,37 @@ public class ExternalPaymentProviderPlugin implements PaymentPluginApi {
     public static final String PLUGIN_NAME = "__EXTERNAL_PAYMENT__";
 
     private final Clock clock;
+    private final PaymentConfig paymentConfig;
 
     @Inject
-    public ExternalPaymentProviderPlugin(final Clock clock) {
+    public ExternalPaymentProviderPlugin(final Clock clock, final PaymentConfig paymentConfig) {
         this.clock = clock;
+        this.paymentConfig = paymentConfig;
     }
 
     @Override
     public PaymentTransactionInfoPlugin authorizePayment(final UUID kbAccountId, final UUID kbPaymentId, final UUID kbTransactionId, final UUID kbPaymentMethodId, final BigDecimal amount, final Currency currency, final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentPluginApiException {
-        return new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, TransactionType.AUTHORIZE, amount, currency, clock.getUTCNow(), clock.getUTCNow(), PaymentPluginStatus.PROCESSED, null, null);
+        return new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, TransactionType.AUTHORIZE, amount, currency, clock.getUTCNow(), clock.getUTCNow(), getPaymentPluginStatus(properties), null, null);
     }
 
     @Override
     public PaymentTransactionInfoPlugin capturePayment(final UUID kbAccountId, final UUID kbPaymentId, final UUID kbTransactionId, final UUID kbPaymentMethodId, final BigDecimal amount, final Currency currency, final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentPluginApiException {
-        return new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, TransactionType.CAPTURE, amount, currency, clock.getUTCNow(), clock.getUTCNow(), PaymentPluginStatus.PROCESSED, null, null);
+        return new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, TransactionType.CAPTURE, amount, currency, clock.getUTCNow(), clock.getUTCNow(), getPaymentPluginStatus(properties), null, null);
     }
 
     @Override
     public PaymentTransactionInfoPlugin purchasePayment(final UUID kbAccountId, final UUID kbPaymentId, final UUID kbTransactionId, final UUID kbPaymentMethodId, final BigDecimal amount, final Currency currency, final Iterable<PluginProperty> properties, final CallContext context) throws PaymentPluginApiException {
-        return new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, TransactionType.PURCHASE, amount, currency, clock.getUTCNow(), clock.getUTCNow(), PaymentPluginStatus.PROCESSED, null, null);
+        return new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, TransactionType.PURCHASE, amount, currency, clock.getUTCNow(), clock.getUTCNow(), getPaymentPluginStatus(properties), null, null);
     }
 
     @Override
     public PaymentTransactionInfoPlugin voidPayment(final UUID kbAccountId, final UUID kbPaymentId, final UUID kbTransactionId, final UUID kbPaymentMethodId, final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentPluginApiException {
-        return new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, TransactionType.VOID, BigDecimal.ZERO, null, clock.getUTCNow(), clock.getUTCNow(), PaymentPluginStatus.PROCESSED, null, null);
+        return new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, TransactionType.VOID, BigDecimal.ZERO, null, clock.getUTCNow(), clock.getUTCNow(), getPaymentPluginStatus(properties), null, null);
     }
 
     @Override
     public PaymentTransactionInfoPlugin creditPayment(final UUID kbAccountId, final UUID kbPaymentId, final UUID kbTransactionId, final UUID kbPaymentMethodId, final BigDecimal amount, final Currency currency, final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentPluginApiException {
-        return new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, TransactionType.CREDIT, amount, currency, clock.getUTCNow(), clock.getUTCNow(), PaymentPluginStatus.PROCESSED, null, null);
+        return new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, TransactionType.CREDIT, amount, currency, clock.getUTCNow(), clock.getUTCNow(), getPaymentPluginStatus(properties), null, null);
     }
 
     @Override
@@ -95,7 +100,7 @@ public class ExternalPaymentProviderPlugin implements PaymentPluginApi {
 
     @Override
     public PaymentTransactionInfoPlugin refundPayment(final UUID kbAccountId, final UUID kbPaymentId, final UUID kbTransactionId, final UUID kbPaymentMethodId, final BigDecimal refundAmount, final Currency currency, final Iterable<PluginProperty> properties, final CallContext context) throws PaymentPluginApiException {
-        return new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, TransactionType.REFUND, refundAmount, currency, clock.getUTCNow(), clock.getUTCNow(), PaymentPluginStatus.PROCESSED, null, null);
+        return new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, TransactionType.REFUND, refundAmount, currency, clock.getUTCNow(), clock.getUTCNow(), getPaymentPluginStatus(properties), null, null);
     }
 
     @Override
@@ -138,4 +143,46 @@ public class ExternalPaymentProviderPlugin implements PaymentPluginApi {
     public GatewayNotification processNotification(final String notification, final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentPluginApiException {
         return new DefaultNoOpGatewayNotification();
     }
+
+    private PaymentPluginStatus getPaymentPluginStatus(final Iterable<PluginProperty> properties) throws PaymentPluginApiException {
+        if (shouldPaymentFailWithError(properties)) {
+            return PaymentPluginStatus.ERROR;
+        } else if (shouldPaymentFailWithException(properties)) {
+            throw new PaymentPluginApiException("Failed to process payment", "Triggered from killbill.external.payment.fail.cancellation");
+        } else if (shouldPaymentFailWithCancellation(properties)) {
+            return PaymentPluginStatus.CANCELED;
+        } else if (shouldPaymentTimeout(properties)) {
+            try {
+                Thread.sleep(paymentConfig.getPaymentPluginTimeout().getMillis() + 1000);
+            } catch (final InterruptedException ignored) {
+            }
+        }
+        return PaymentPluginStatus.PROCESSED;
+    }
+
+    private boolean shouldPaymentFailWithError(final Iterable<PluginProperty> properties) {
+        return isPropertySet(properties, "killbill.external.payment.fail.error");
+    }
+
+    private boolean shouldPaymentFailWithException(final Iterable<PluginProperty> properties) {
+        return isPropertySet(properties, "killbill.external.payment.fail.exception");
+    }
+
+    private boolean shouldPaymentFailWithCancellation(final Iterable<PluginProperty> properties) {
+        return isPropertySet(properties, "killbill.external.payment.fail.cancellation");
+    }
+
+    private boolean shouldPaymentTimeout(final Iterable<PluginProperty> properties) {
+        return isPropertySet(properties, "killbill.external.payment.fail.timeout");
+    }
+
+    private boolean isPropertySet(final Iterable<PluginProperty> properties, final String targetProperty) {
+        return Iterables.any(properties, new Predicate<PluginProperty>() {
+            @Override
+            public boolean apply(final PluginProperty input) {
+                return input.getKey().equals(targetProperty) && input.getValue().equals("true");
+            }
+        });
+    }
+
 }
diff --git a/payment/src/test/java/org/killbill/billing/payment/provider/TestExternalPaymentProviderPlugin.java b/payment/src/test/java/org/killbill/billing/payment/provider/TestExternalPaymentProviderPlugin.java
index 3241ea5..5bd6128 100644
--- a/payment/src/test/java/org/killbill/billing/payment/provider/TestExternalPaymentProviderPlugin.java
+++ b/payment/src/test/java/org/killbill/billing/payment/provider/TestExternalPaymentProviderPlugin.java
@@ -45,7 +45,7 @@ public class TestExternalPaymentProviderPlugin extends PaymentTestSuiteNoDB {
     @BeforeMethod(groups = "fast")
     public void beforeMethod() throws Exception {
         super.beforeMethod();
-        plugin = new ExternalPaymentProviderPlugin(clock);
+        plugin = new ExternalPaymentProviderPlugin(clock, paymentConfig);
     }
 
     @Test(groups = "fast")
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCatalog.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCatalog.java
index ae03c75..4acd0cd 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCatalog.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCatalog.java
@@ -26,6 +26,8 @@ import java.util.Set;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
+import org.killbill.billing.catalog.StandaloneCatalog;
+import org.killbill.billing.catalog.VersionedCatalog;
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.catalog.api.ProductCategory;
@@ -33,13 +35,13 @@ import org.killbill.billing.catalog.api.TimeUnit;
 import org.killbill.billing.client.KillBillClientException;
 import org.killbill.billing.client.RequestOptions;
 import org.killbill.billing.client.model.Catalog;
-import org.killbill.billing.client.model.Catalogs;
 import org.killbill.billing.client.model.Plan;
 import org.killbill.billing.client.model.PlanDetail;
 import org.killbill.billing.client.model.Product;
 import org.killbill.billing.client.model.SimplePlan;
 import org.killbill.billing.client.model.Tenant;
 import org.killbill.billing.client.model.Usage;
+import org.killbill.xmlloader.XMLLoader;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
@@ -48,15 +50,23 @@ import com.google.common.io.Resources;
 
 public class TestCatalog extends TestJaxrsBase {
 
-
-
-        @Test(groups = "slow", description = "Upload and retrieve a per tenant catalog")
+    @Test(groups = "slow", description = "Upload and retrieve a per tenant catalog")
     public void testMultiTenantCatalog() throws Exception {
-        final String catalogPath = Resources.getResource("SpyCarBasic.xml").getPath();
-        killBillClient.uploadXMLCatalog(catalogPath, createdBy, reason, comment);
+        final String versionPath1 = Resources.getResource("versionedCatalog/WeaponsHireSmall-1.xml").getPath();
+        killBillClient.uploadXMLCatalog(versionPath1, createdBy, reason, comment);
+        String catalog = killBillClient.getXMLCatalog();
+        Assert.assertNotNull(catalog);
 
-        final String catalog = killBillClient.getXMLCatalog();
+
+        final String versionPath2 = Resources.getResource("versionedCatalog/WeaponsHireSmall-2.xml").getPath();
+        killBillClient.uploadXMLCatalog(versionPath2, createdBy, reason, comment);
+        catalog = killBillClient.getXMLCatalog();
         Assert.assertNotNull(catalog);
+
+        //
+        // We can't deserialize the VersionedCatalog using our JAXB models because it contains several
+        // Standalone catalog and ids (JAXB name) are not unique across the various catalogs so deserialization would fail
+        //
     }
 
     @Test(groups = "slow", description = "Can retrieve a json version of the catalog")
diff --git a/util/src/main/resources/ehcache.xml b/util/src/main/resources/ehcache.xml
index cd6b114..61d098d 100644
--- a/util/src/main/resources/ehcache.xml
+++ b/util/src/main/resources/ehcache.xml
@@ -22,12 +22,12 @@
          xsi:noNamespaceSchemaLocation="ehcache.xsd">
 
     <defaultCache
-            maxElementsInMemory="0"
+            maxElementsInMemory="100000"
             maxElementsOnDisk="0"
-            eternal="false"
-            timeToIdleSeconds="0"
-            timeToLiveSeconds="0"
+            eternal="true"
             overflowToDisk="false"
+            diskPersistent="false"
+            memoryStoreEvictionPolicy="LFU"
             statistics="true"
             />