killbill-uncached

Details

diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlement.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlement.java
index 0507ae1..44cfbc1 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlement.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlement.java
@@ -327,7 +327,7 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
         refresh(callContext);
 
         if (eventsStream.isSubscriptionCancelled()) {
-            throw new EntitlementApiException(ErrorCode.SUB_CANCEL_BAD_STATE, getId(), EntitlementState.CANCELLED);
+            throw new EntitlementApiException(ErrorCode.SUB_UNCANCEL_BAD_STATE, getId());
         }
 
         final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(getAccountId(), callContext);
@@ -347,7 +347,7 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
             }
         } else {
             // Entitlement is NOT cancelled (or future cancelled), there is nothing to do
-            throw new EntitlementApiException(ErrorCode.SUB_CANCEL_BAD_STATE, getId(), EntitlementState.CANCELLED);
+            throw new EntitlementApiException(ErrorCode.SUB_UNCANCEL_BAD_STATE, getId());
         }
 
         // If billing was previously cancelled, reactivate
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlementApi.java b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlementApi.java
index 45851eb..a60129b 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlementApi.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlementApi.java
@@ -70,7 +70,7 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
             entitlement.uncancelEntitlement(ImmutableList.<PluginProperty>of(), callContext);
             Assert.fail("Entitlement hasn't been cancelled yet");
         } catch (final EntitlementApiException e) {
-            Assert.assertEquals(e.getCode(), ErrorCode.SUB_CANCEL_BAD_STATE.getCode());
+            Assert.assertEquals(e.getCode(), ErrorCode.SUB_UNCANCEL_BAD_STATE.getCode());
         }
 
         clock.addDays(3);
@@ -98,18 +98,17 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
             entitlement.uncancelEntitlement(ImmutableList.<PluginProperty>of(), callContext);
             Assert.fail("Entitlement is already cancelled");
         } catch (final EntitlementApiException e) {
-            Assert.assertEquals(e.getCode(), ErrorCode.SUB_CANCEL_BAD_STATE.getCode());
+            Assert.assertEquals(e.getCode(), ErrorCode.SUB_UNCANCEL_BAD_STATE.getCode());
         }
 
         try {
             addOnEntitlement.uncancelEntitlement(ImmutableList.<PluginProperty>of(), callContext);
             Assert.fail("Add-On Entitlement is already cancelled");
         } catch (final EntitlementApiException e) {
-            Assert.assertEquals(e.getCode(), ErrorCode.SUB_CANCEL_BAD_STATE.getCode());
+            Assert.assertEquals(e.getCode(), ErrorCode.SUB_UNCANCEL_BAD_STATE.getCode());
         }
     }
 
-
     @Test(groups = "slow")
     public void testUncancelEffectiveCancelledEntitlement() throws AccountApiException, EntitlementApiException, SubscriptionBaseApiException {
         final LocalDate initialDate = new LocalDate(2013, 8, 7);
@@ -439,4 +438,51 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
         assertEquals(newBaseEntitlement.getEffectiveStartDate(), effectiveDate);
         assertEquals(newBaseEntitlement.getEffectiveEndDate(), null);
     }
+
+    @Test(groups = "slow")
+    public void testCreateEntitlementInThePast() throws AccountApiException, EntitlementApiException, SubscriptionBaseApiException {
+        final LocalDate initialDate = new LocalDate(2013, 8, 7);
+        final LocalDate clockDate = new LocalDate(2013, 10, 7);
+        clock.setDay(clockDate);
+
+        final Account account = accountApi.createAccount(getAccountData(7), callContext);
+
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+
+        // Keep the same object for the whole test, to make sure we refresh its state before r/w calls
+        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.PHASE);
+        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
+
+        assertEquals(entitlement.getAccountId(), account.getId());
+        assertEquals(entitlement.getExternalKey(), account.getExternalKey());
+
+        assertEquals(entitlement.getEffectiveStartDate(), initialDate);
+        assertNull(entitlement.getEffectiveEndDate());
+
+        assertEquals(entitlement.getLastActivePriceList().getName(), PriceListSet.DEFAULT_PRICELIST_NAME);
+        assertEquals(entitlement.getLastActiveProduct().getName(), "Shotgun");
+        assertEquals(entitlement.getLastActivePhase().getName(), "shotgun-monthly-evergreen");
+        assertEquals(entitlement.getLastActivePlan().getName(), "shotgun-monthly");
+        assertEquals(entitlement.getLastActiveProductCategory(), ProductCategory.BASE);
+
+        assertEquals(entitlement.getState(), EntitlementState.ACTIVE);
+        assertEquals(entitlement.getSourceType(), EntitlementSourceType.NATIVE);
+
+        assertEquals(entitlement.getLastActivePlan().getName(), "shotgun-monthly");
+        assertEquals(entitlement.getLastActivePriceList().getName(), PriceListSet.DEFAULT_PRICELIST_NAME);
+        assertEquals(entitlement.getLastActiveProduct().getName(), "Shotgun");
+        assertEquals(entitlement.getLastActiveProductCategory(), ProductCategory.BASE);
+
+        assertEquals(entitlement.getState(), EntitlementState.ACTIVE);
+        assertEquals(entitlement.getSourceType(), EntitlementSourceType.NATIVE);
+
+        List<Entitlement> bundleEntitlements = entitlementApi.getAllEntitlementsForBundle(entitlement.getBundleId(), callContext);
+        assertEquals(bundleEntitlements.size(), 1);
+
+        bundleEntitlements = entitlementApi.getAllEntitlementsForAccountIdAndExternalKey(account.getId(), account.getExternalKey(), callContext);
+        assertEquals(bundleEntitlements.size(), 1);
+
+    }
+
 }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java b/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
index d5d3bd6..c81c6e8 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
@@ -45,6 +45,7 @@ import org.killbill.billing.invoice.api.Invoice;
 import org.killbill.billing.invoice.api.InvoiceApiException;
 import org.killbill.billing.invoice.api.InvoiceApiHelper;
 import org.killbill.billing.invoice.api.InvoiceItem;
+import org.killbill.billing.invoice.api.InvoiceItemType;
 import org.killbill.billing.invoice.api.InvoiceUserApi;
 import org.killbill.billing.invoice.api.WithAccountLock;
 import org.killbill.billing.invoice.dao.InvoiceDao;
@@ -72,6 +73,7 @@ import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Function;
 import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
 import com.google.common.collect.Collections2;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
@@ -365,10 +367,16 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
             }
         };
 
-        final List<InvoiceItem> creditInvoiceItems = invoiceApiHelper.dispatchToInvoicePluginsAndInsertItems(accountId, false, withAccountLock, context);
+        final Collection<InvoiceItem> creditInvoiceItems = Collections2.<InvoiceItem>filter(invoiceApiHelper.dispatchToInvoicePluginsAndInsertItems(accountId, false, withAccountLock, context),
+                                                                                            new Predicate<InvoiceItem>() {
+                                                                                                @Override
+                                                                                                public boolean apply(final InvoiceItem invoiceItem) {
+                                                                                                    return InvoiceItemType.CREDIT_ADJ.equals(invoiceItem.getInvoiceItemType());
+                                                                                                }
+                                                                                            });
         Preconditions.checkState(creditInvoiceItems.size() == 1, "Should have created a single credit invoice item: " + creditInvoiceItems);
 
-        return creditInvoiceItems.get(0);
+        return creditInvoiceItems.iterator().next();
     }
 
     @Override
@@ -401,10 +409,16 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
             }
         };
 
-        final List<InvoiceItem> adjustmentInvoiceItems = invoiceApiHelper.dispatchToInvoicePluginsAndInsertItems(accountId, false, withAccountLock, context);
+        final Collection<InvoiceItem> adjustmentInvoiceItems = Collections2.<InvoiceItem>filter(invoiceApiHelper.dispatchToInvoicePluginsAndInsertItems(accountId, false, withAccountLock, context),
+                                                                                                new Predicate<InvoiceItem>() {
+                                                                                                    @Override
+                                                                                                    public boolean apply(final InvoiceItem invoiceItem) {
+                                                                                                        return InvoiceItemType.ITEM_ADJ.equals(invoiceItem.getInvoiceItemType());
+                                                                                                    }
+                                                                                                });
         Preconditions.checkState(adjustmentInvoiceItems.size() == 1, "Should have created a single adjustment item: " + adjustmentInvoiceItems);
 
-        return adjustmentInvoiceItems.get(0);
+        return adjustmentInvoiceItems.iterator().next();
     }
 
     @Override
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PluginResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PluginResource.java
index 66d1b43..10ff827 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PluginResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PluginResource.java
@@ -24,6 +24,8 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.net.URLEncoder;
 import java.nio.charset.Charset;
+import java.util.Collections;
+import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -48,6 +50,7 @@ import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
 
 import org.killbill.billing.account.api.AccountUserApi;
 import org.killbill.billing.jaxrs.util.Context;
@@ -61,10 +64,13 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.codahale.metrics.annotation.Timed;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.LinkedHashMultimap;
 import com.google.common.io.ByteStreams;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import com.google.inject.name.Named;
+import com.sun.jersey.api.representation.Form;
 import com.wordnik.swagger.annotations.Api;
 
 @Singleton
@@ -97,8 +103,9 @@ public class PluginResource extends JaxRsResourceBase {
     public Response doDELETE(@javax.ws.rs.core.Context final HttpServletRequest request,
                              @javax.ws.rs.core.Context final HttpServletResponse response,
                              @javax.ws.rs.core.Context final ServletContext servletContext,
-                             @javax.ws.rs.core.Context final ServletConfig servletConfig) throws ServletException, IOException {
-        return serviceViaOSGIPlugin(request, response, servletContext, servletConfig);
+                             @javax.ws.rs.core.Context final ServletConfig servletConfig,
+                             @javax.ws.rs.core.Context final UriInfo uriInfo) throws ServletException, IOException {
+        return serviceViaOSGIPlugin(request, response, servletContext, servletConfig, uriInfo);
     }
 
     @Timed
@@ -106,8 +113,9 @@ public class PluginResource extends JaxRsResourceBase {
     public Response doGET(@javax.ws.rs.core.Context final HttpServletRequest request,
                           @javax.ws.rs.core.Context final HttpServletResponse response,
                           @javax.ws.rs.core.Context final ServletContext servletContext,
-                          @javax.ws.rs.core.Context final ServletConfig servletConfig) throws ServletException, IOException {
-        return serviceViaOSGIPlugin(request, response, servletContext, servletConfig);
+                          @javax.ws.rs.core.Context final ServletConfig servletConfig,
+                          @javax.ws.rs.core.Context final UriInfo uriInfo) throws ServletException, IOException {
+        return serviceViaOSGIPlugin(request, response, servletContext, servletConfig, uriInfo);
     }
 
     @Timed
@@ -115,8 +123,9 @@ public class PluginResource extends JaxRsResourceBase {
     public Response doOPTIONS(@javax.ws.rs.core.Context final HttpServletRequest request,
                               @javax.ws.rs.core.Context final HttpServletResponse response,
                               @javax.ws.rs.core.Context final ServletContext servletContext,
-                              @javax.ws.rs.core.Context final ServletConfig servletConfig) throws ServletException, IOException {
-        return serviceViaOSGIPlugin(request, response, servletContext, servletConfig);
+                              @javax.ws.rs.core.Context final ServletConfig servletConfig,
+                              @javax.ws.rs.core.Context final UriInfo uriInfo) throws ServletException, IOException {
+        return serviceViaOSGIPlugin(request, response, servletContext, servletConfig, uriInfo);
     }
 
     @Timed
@@ -126,8 +135,9 @@ public class PluginResource extends JaxRsResourceBase {
                                @javax.ws.rs.core.Context final HttpServletRequest request,
                                @javax.ws.rs.core.Context final HttpServletResponse response,
                                @javax.ws.rs.core.Context final ServletContext servletContext,
-                               @javax.ws.rs.core.Context final ServletConfig servletConfig) throws ServletException, IOException {
-        return serviceViaOSGIPlugin(form, request, response, servletContext, servletConfig);
+                               @javax.ws.rs.core.Context final ServletConfig servletConfig,
+                               @javax.ws.rs.core.Context final UriInfo uriInfo) throws ServletException, IOException {
+        return serviceViaOSGIPlugin(form, request, response, servletContext, servletConfig, uriInfo);
     }
 
     @Timed
@@ -135,8 +145,9 @@ public class PluginResource extends JaxRsResourceBase {
     public Response doPOST(@javax.ws.rs.core.Context final HttpServletRequest request,
                            @javax.ws.rs.core.Context final HttpServletResponse response,
                            @javax.ws.rs.core.Context final ServletContext servletContext,
-                           @javax.ws.rs.core.Context final ServletConfig servletConfig) throws ServletException, IOException {
-        return serviceViaOSGIPlugin(request, response, servletContext, servletConfig);
+                           @javax.ws.rs.core.Context final ServletConfig servletConfig,
+                           @javax.ws.rs.core.Context final UriInfo uriInfo) throws ServletException, IOException {
+        return serviceViaOSGIPlugin(request, response, servletContext, servletConfig, uriInfo);
     }
 
     @Timed
@@ -144,8 +155,9 @@ public class PluginResource extends JaxRsResourceBase {
     public Response doPUT(@javax.ws.rs.core.Context final HttpServletRequest request,
                           @javax.ws.rs.core.Context final HttpServletResponse response,
                           @javax.ws.rs.core.Context final ServletContext servletContext,
-                          @javax.ws.rs.core.Context final ServletConfig servletConfig) throws ServletException, IOException {
-        return serviceViaOSGIPlugin(request, response, servletContext, servletConfig);
+                          @javax.ws.rs.core.Context final ServletConfig servletConfig,
+                          @javax.ws.rs.core.Context final UriInfo uriInfo) throws ServletException, IOException {
+        return serviceViaOSGIPlugin(request, response, servletContext, servletConfig, uriInfo);
     }
 
     @Timed
@@ -153,30 +165,34 @@ public class PluginResource extends JaxRsResourceBase {
     public Response doHEAD(@javax.ws.rs.core.Context final HttpServletRequest request,
                            @javax.ws.rs.core.Context final HttpServletResponse response,
                            @javax.ws.rs.core.Context final ServletContext servletContext,
-                           @javax.ws.rs.core.Context final ServletConfig servletConfig) throws ServletException, IOException {
-        serviceViaOSGIPlugin(request, response, servletContext, servletConfig);
+                           @javax.ws.rs.core.Context final ServletConfig servletConfig,
+                           @javax.ws.rs.core.Context final UriInfo uriInfo) throws ServletException, IOException {
+        serviceViaOSGIPlugin(request, response, servletContext, servletConfig, uriInfo);
 
         // Make sure to return 204
         return Response.noContent().build();
     }
 
     private Response serviceViaOSGIPlugin(final HttpServletRequest request, final HttpServletResponse response,
-                                          final ServletContext servletContext, final ServletConfig servletConfig) throws ServletException, IOException {
-        return serviceViaOSGIPlugin(request, request.getInputStream(), response, servletContext, servletConfig);
+                                          final ServletContext servletContext, final ServletConfig servletConfig,
+                                          final UriInfo uriInfo) throws ServletException, IOException {
+        return serviceViaOSGIPlugin(request, request.getInputStream(), new Form(), response, servletContext, servletConfig, uriInfo);
     }
 
     private Response serviceViaOSGIPlugin(final MultivaluedMap<String, String> form,
                                           final HttpServletRequest request, final HttpServletResponse response,
-                                          final ServletContext servletContext, final ServletConfig servletConfig) throws ServletException, IOException {
+                                          final ServletContext servletContext, final ServletConfig servletConfig,
+                                          final UriInfo uriInfo) throws ServletException, IOException {
         // form will contain form parameters, if any. Even if the request contains such parameters, it may be empty
         // if a filter (e.g. Shiro) has already consumed them (see kludge below)
-        return serviceViaOSGIPlugin(request, createInputStream(request, form), response, servletContext, servletConfig);
+        return serviceViaOSGIPlugin(request, createInputStream(request, form), form, response, servletContext, servletConfig, uriInfo);
     }
 
-    private Response serviceViaOSGIPlugin(final HttpServletRequest request, final InputStream inputStream, final HttpServletResponse response,
-                                          final ServletContext servletContext, final ServletConfig servletConfig) throws ServletException, IOException {
+    private Response serviceViaOSGIPlugin(final HttpServletRequest request, final InputStream inputStream, final MultivaluedMap<String, String> formData,
+                                          final HttpServletResponse response, final ServletContext servletContext,
+                                          final ServletConfig servletConfig, final UriInfo uriInfo) throws ServletException, IOException {
         prepareOSGIRequest(request, servletContext, servletConfig);
-        osgiServlet.service(new OSGIServletRequestWrapper(request, inputStream), new OSGIServletResponseWrapper(response));
+        osgiServlet.service(new OSGIServletRequestWrapper(request, inputStream, formData, uriInfo.getQueryParameters()), new OSGIServletResponseWrapper(response));
 
         if (response.isCommitted()) {
             if (response.getStatus() >= 400) {
@@ -233,10 +249,52 @@ public class PluginResource extends JaxRsResourceBase {
     private static final class OSGIServletRequestWrapper extends HttpServletRequestWrapper {
 
         private final InputStream inputStream;
+        private final Map<String, String[]> parameterMap;
 
-        public OSGIServletRequestWrapper(final HttpServletRequest request, final InputStream inputStream) {
+        public OSGIServletRequestWrapper(final HttpServletRequest request, final InputStream inputStream, final MultivaluedMap<String, String> formData, final MultivaluedMap<String, String> queryParameters) {
             super(request);
             this.inputStream = inputStream;
+            this.parameterMap = new HashMap<String, String[]>();
+
+            // Query string parameters and posted form data must appear in the parameters
+            final LinkedHashMultimap<String, String> tmpParameterMap = LinkedHashMultimap.<String, String>create();
+            for (final String formDataKey : formData.keySet()) {
+                tmpParameterMap.putAll(formDataKey, formData.get(formDataKey));
+            }
+            for (final String queryParameterKey : queryParameters.keySet()) {
+                tmpParameterMap.putAll(queryParameterKey, queryParameters.get(queryParameterKey));
+            }
+            for (final String parameterKey : request.getParameterMap().keySet()) {
+                tmpParameterMap.putAll(parameterKey, ImmutableList.<String>copyOf(request.getParameterMap().get(parameterKey)));
+            }
+            for (final String value : tmpParameterMap.keys()) {
+                parameterMap.put(value, tmpParameterMap.get(value).toArray(new String[0]));
+            }
+        }
+
+        @Override
+        public String getParameter(final String name) {
+            final String[] values = parameterMap.get(name);
+            if (values == null || values.length == 0) {
+                return null;
+            } else {
+                return values[0];
+            }
+        }
+
+        @Override
+        public Map<String, String[]> getParameterMap() {
+            return Collections.<String, String[]>unmodifiableMap(parameterMap);
+        }
+
+        @Override
+        public Enumeration<String> getParameterNames() {
+            return Collections.<String>enumeration(parameterMap.keySet());
+        }
+
+        @Override
+        public String[] getParameterValues(final String name) {
+            return parameterMap.get(name);
         }
 
         @Override
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TransactionResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TransactionResource.java
index 68d9060..bf002c4 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TransactionResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TransactionResource.java
@@ -57,7 +57,7 @@ import com.wordnik.swagger.annotations.ApiResponses;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 
-@Path(JaxrsResource.PAYMENT_TRANSACTIONS)
+@Path(JaxrsResource.PAYMENT_TRANSACTIONS_PATH)
 @Api(value = JaxrsResource.PAYMENT_TRANSACTIONS, description = "Operations on payment transactions")
 public class TransactionResource extends JaxRsResourceBase {