killbill-memoizeit

Details

diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/billing/EntitlementBillingApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/billing/EntitlementBillingApi.java
index d3e3c6e..c37c79d 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/billing/EntitlementBillingApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/billing/EntitlementBillingApi.java
@@ -27,7 +27,7 @@ import com.ning.billing.account.api.IAccount;
 import com.ning.billing.catalog.api.ICatalog;
 import com.ning.billing.entitlement.api.user.ISubscription;
 import com.ning.billing.entitlement.api.user.Subscription;
-import com.ning.billing.entitlement.api.user.Subscription.SubscriptionBuilder;
+import com.ning.billing.entitlement.api.user.SubscriptionBuilder;
 import com.ning.billing.entitlement.engine.core.Engine;
 import com.ning.billing.entitlement.engine.dao.IEntitlementDao;
 import com.ning.billing.util.clock.IClock;
@@ -62,18 +62,10 @@ public class EntitlementBillingApi implements IEntitlementBillingApi {
             new EntitlementBillingApiException(String.format("Unknwon subscription %s", subscriptionId));
         }
 
-        Subscription updatedSubscription = new SubscriptionBuilder()
-            .setId(subscription.getId())
-            .setBundleId(subscription.getBundleId())
-            .setStartDate(subscription.getStartDate())
-            .setBundleStartDate(subscription.getBundleStartDate())
+        SubscriptionBuilder builder = new SubscriptionBuilder(subscription)
             .setChargedThroughDate(ctd)
-            .setPaidThroughDate(subscription.getPaidThroughDate())
-            .setActiveVersion(subscription.getActiveVersion())
-            .setCategory(subscription.getCategory())
-            .build();
-
-        dao.updateSubscription(updatedSubscription);
+            .setPaidThroughDate(subscription.getPaidThroughDate());
+        dao.updateSubscription(new Subscription(builder, false));
     }
 
 }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApi.java
index decc852..df896e8 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApi.java
@@ -30,6 +30,7 @@ import com.ning.billing.catalog.api.ICatalogService;
 import com.ning.billing.catalog.api.IPlan;
 import com.ning.billing.catalog.api.IPlanPhase;
 import com.ning.billing.catalog.api.IPriceListSet;
+import com.ning.billing.catalog.api.PlanAlignmentChange;
 import com.ning.billing.entitlement.alignment.IPlanAligner;
 import com.ning.billing.entitlement.alignment.IPlanAligner.TimedPhase;
 import com.ning.billing.entitlement.api.user.ISubscription;
@@ -100,13 +101,11 @@ public class EntitlementUserApi implements IEntitlementUserApi {
 
         String realPriceList = (priceList == null) ? IPriceListSet.DEFAULT_PRICELIST_NAME : priceList;
         DateTime now = clock.getUTCNow();
-        requestedDate = (requestedDate != null) ? Clock.truncateMs(requestedDate) : null;
+        requestedDate = (requestedDate != null) ? Clock.truncateMs(requestedDate) : now;
         if (requestedDate != null && requestedDate.isAfter(now)) {
             throw new EntitlementUserApiException(ErrorCode.ENT_INVALID_REQUESTED_DATE, requestedDate.toString());
         }
 
-        requestedDate = (requestedDate == null) ? now : requestedDate;
-
         IPlan plan = catalogService.getCatalog().getPlan(productName, term, realPriceList);
         if (plan == null) {
             throw new EntitlementUserApiException(ErrorCode.ENT_CREATE_BAD_CATALOG, productName, term, realPriceList);
@@ -146,7 +145,13 @@ public class EntitlementUserApi implements IEntitlementUserApi {
         }
 
         DateTime effectiveDate = requestedDate;
-        Subscription subscription = new Subscription(bundleId, plan.getProduct().getCategory(), bundleStartDate, effectiveDate);
+        Subscription subscription = new Subscription(new SubscriptionBuilder()
+            .setId(UUID.randomUUID())
+            .setBundleId(bundleId)
+            .setCategory(plan.getProduct().getCategory())
+            .setBundleStartDate(bundleStartDate)
+            .setStartDate(effectiveDate),
+                false);
 
         TimedPhase currentTimedPhase =  planAligner.getCurrentTimedPhaseOnCreate(subscription, plan, realPriceList, effectiveDate);
         ApiEventCreate creationEvent =
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java
index 3781e74..950af53 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java
@@ -16,7 +16,6 @@
 
 package com.ning.billing.entitlement.api.user;
 
-import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Iterator;
@@ -24,10 +23,10 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.UUID;
 
-import com.ning.billing.ErrorCode;
-import com.ning.billing.account.api.IAccount;
 import org.joda.time.DateTime;
 
+import com.ning.billing.ErrorCode;
+
 import com.ning.billing.catalog.api.ActionPolicy;
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.CatalogApiException;
@@ -60,118 +59,55 @@ import com.ning.billing.util.clock.IClock;
 
 public class Subscription extends PrivateFields  implements ISubscription {
 
+    //
+    // Singletons used to perform API changes
+    private final IClock clock;
+    private final IEntitlementDao dao;
+    private final ICatalog catalog;
+    private final IPlanAligner planAligner;
+
+    //
+    // Final subscription fields
+    //
     private final UUID id;
     private final UUID bundleId;
     private final DateTime startDate;
     private final DateTime bundleStartDate;
-    private final long activeVersion;
     private final ProductCategory category;
 
-    private final IClock clock;
-    private final IEntitlementDao dao;
-    private final ICatalog catalog;
-    private final IPlanAligner planAligner;
-
-    // STEPH interaction with billing /payment system
+    //
+    // Those can be modified through non User APIs, and a new Subscription object would be created
+    //
+    private final long activeVersion;
     private final DateTime chargedThroughDate;
     private final DateTime paidThroughDate;
 
-    // STEPH non final because of change/ cancel API at the object level
+    //
+    // User APIs (createm chnage, cancel,...) will recompute those each time,
+    // so the user holding that subscription object get the correct state when
+    // the call completes
+    //
     private List<SubscriptionTransition> transitions;
 
-
-    public static class SubscriptionBuilder {
-        private  UUID id;
-        private  UUID bundleId;
-        private  DateTime startDate;
-        private  DateTime bundleStartDate;
-        private  Long activeVersion;
-        private  ProductCategory category;
-        private  DateTime chargedThroughDate;
-        private  DateTime paidThroughDate;
-
-        public SubscriptionBuilder setId(UUID id) {
-            this.id = id;
-            return this;
-        }
-        public SubscriptionBuilder setBundleId(UUID bundleId) {
-            this.bundleId = bundleId;
-            return this;
-        }
-        public SubscriptionBuilder setStartDate(DateTime startDate) {
-            this.startDate = startDate;
-            return this;
-        }
-        public SubscriptionBuilder setBundleStartDate(DateTime bundleStartDate) {
-            this.bundleStartDate = bundleStartDate;
-            return this;
-            }
-        public SubscriptionBuilder setActiveVersion(long activeVersion) {
-            this.activeVersion = activeVersion;
-            return this;
-        }
-        public SubscriptionBuilder setChargedThroughDate(DateTime chargedThroughDate) {
-            this.chargedThroughDate = chargedThroughDate;
-            return this;
-        }
-        public SubscriptionBuilder setPaidThroughDate(DateTime paidThroughDate) {
-            this.paidThroughDate = paidThroughDate;
-            return this;
-        }
-        public SubscriptionBuilder setCategory(ProductCategory category) {
-            this.category = category;
-            return this;
-        }
-
-        private void checkAllFieldsSet() {
-            for (Field cur : SubscriptionBuilder.class.getDeclaredFields()) {
-                try {
-                    Object value = cur.get(this);
-                    if (value == null) {
-                        throw new EntitlementError(String.format("Field %s has not been set for Subscription",
-                                cur.getName()));
-                    }
-                } catch (IllegalAccessException e) {
-                    throw new EntitlementError(String.format("Failed to access value for field %s for Subscription",
-                            cur.getName()), e);
-                }
-            }
-        }
-
-        public Subscription build() {
-            //checkAllFieldsSet();
-            return new Subscription(id, bundleId, category, bundleStartDate, startDate, chargedThroughDate, paidThroughDate, activeVersion);
-        }
-
-    }
-
-    public Subscription(UUID bundleId, ProductCategory category, DateTime bundleStartDate, DateTime startDate) {
-        this(UUID.randomUUID(), bundleId, category, bundleStartDate, startDate, null, null, SubscriptionEvents.INITIAL_VERSION);
-    }
-
-
-    public Subscription(UUID id, UUID bundleId, ProductCategory category, DateTime bundleStartDate, DateTime startDate, DateTime ctd, DateTime ptd, long activeVersion) {
-
+    public Subscription(SubscriptionBuilder builder, boolean rebuildTransition) {
         super();
         this.clock = InjectorMagic.getClock();
         this.dao = InjectorMagic.getEntitlementDao();
         this.catalog = InjectorMagic.getCatlog();
         this.planAligner = InjectorMagic.getPlanAligner();
-        this.id = id;
-        this.bundleId = bundleId;
-        this.startDate = startDate;
-        this.bundleStartDate = bundleStartDate;
-        this.category = category;
-
-        this.activeVersion = activeVersion;
-
-        this.chargedThroughDate = ctd;
-        this.paidThroughDate = ptd;
-
-        rebuildTransitions();
+        this.id = builder.getId();
+        this.bundleId = builder.getBundleId();
+        this.startDate = builder.getStartDate();
+        this.bundleStartDate = builder.getBundleStartDate();
+        this.category = builder.getCategory();
+        this.activeVersion = builder.getActiveVersion();
+        this.chargedThroughDate = builder.getChargedThroughDate();
+        this.paidThroughDate = builder.getPaidThroughDate();
+        if (rebuildTransition) {
+            rebuildTransitions();
+        }
     }
 
-
     @Override
     public UUID getId() {
         return id;
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionBuilder.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionBuilder.java
new file mode 100644
index 0000000..d14b754
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionBuilder.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.entitlement.api.user;
+
+import java.lang.reflect.Field;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.entitlement.exceptions.EntitlementError;
+
+public class SubscriptionBuilder {
+
+    private  UUID id;
+    private  UUID bundleId;
+    private  DateTime startDate;
+    private  DateTime bundleStartDate;
+    private  Long activeVersion;
+    private  ProductCategory category;
+    private  DateTime chargedThroughDate;
+    private  DateTime paidThroughDate;
+
+    public SubscriptionBuilder() {
+        this.activeVersion = SubscriptionEvents.INITIAL_VERSION;
+    }
+
+    public SubscriptionBuilder(Subscription original) {
+        this.id = original.getId();
+        this.bundleId = original.getBundleId();
+        this.startDate = original.getStartDate();
+        this.bundleStartDate = original.getBundleStartDate();
+        this.category = original.getCategory();
+        this.activeVersion = original.getActiveVersion();
+        this.chargedThroughDate = original.getChargedThroughDate();
+        this.paidThroughDate = original.getPaidThroughDate();
+    }
+
+    public SubscriptionBuilder setId(UUID id) {
+        this.id = id;
+        return this;
+    }
+    public SubscriptionBuilder setBundleId(UUID bundleId) {
+        this.bundleId = bundleId;
+        return this;
+    }
+    public SubscriptionBuilder setStartDate(DateTime startDate) {
+        this.startDate = startDate;
+        return this;
+    }
+    public SubscriptionBuilder setBundleStartDate(DateTime bundleStartDate) {
+        this.bundleStartDate = bundleStartDate;
+        return this;
+        }
+    public SubscriptionBuilder setActiveVersion(long activeVersion) {
+        this.activeVersion = activeVersion;
+        return this;
+    }
+    public SubscriptionBuilder setChargedThroughDate(DateTime chargedThroughDate) {
+        this.chargedThroughDate = chargedThroughDate;
+        return this;
+    }
+    public SubscriptionBuilder setPaidThroughDate(DateTime paidThroughDate) {
+        this.paidThroughDate = paidThroughDate;
+        return this;
+    }
+    public SubscriptionBuilder setCategory(ProductCategory category) {
+        this.category = category;
+        return this;
+    }
+
+    public UUID getId() {
+        return id;
+    }
+    public UUID getBundleId() {
+        return bundleId;
+    }
+    public DateTime getStartDate() {
+        return startDate;
+    }
+    public DateTime getBundleStartDate() {
+        return bundleStartDate;
+    }
+    public Long getActiveVersion() {
+        return activeVersion;
+    }
+    public ProductCategory getCategory() {
+        return category;
+    }
+    public DateTime getChargedThroughDate() {
+        return chargedThroughDate;
+    }
+    public DateTime getPaidThroughDate() {
+        return paidThroughDate;
+    }
+    private void checkAllFieldsSet() {
+        for (Field cur : SubscriptionBuilder.class.getDeclaredFields()) {
+            try {
+                Object value = cur.get(this);
+                if (value == null) {
+                    throw new EntitlementError(String.format("Field %s has not been set for Subscription",
+                            cur.getName()));
+                }
+            } catch (IllegalAccessException e) {
+                throw new EntitlementError(String.format("Failed to access value for field %s for Subscription",
+                        cur.getName()), e);
+            }
+        }
+    }
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java
index b490fcd..943a0dc 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java
@@ -37,6 +37,7 @@ import com.ning.billing.config.IEntitlementConfig;
 import com.ning.billing.entitlement.api.user.ISubscription;
 import com.ning.billing.entitlement.api.user.ISubscriptionBundle;
 import com.ning.billing.entitlement.api.user.Subscription;
+import com.ning.billing.entitlement.api.user.SubscriptionBuilder;
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
 import com.ning.billing.entitlement.events.IEvent;
 import com.ning.billing.entitlement.events.IEvent.EventType;
@@ -225,8 +226,7 @@ public class EntitlementDao implements IEntitlementDao {
                 return null;
             }
         });
-        return new Subscription(subscription.getId(), subscription.getBundleId(),subscription.getCategory(), subscription.getBundleStartDate(),
-                subscription.getStartDate(), subscription.getChargedThroughDate(), subscription.getPaidThroughDate(), subscription.getActiveVersion());
+        return new Subscription(new SubscriptionBuilder(subscription), true);
     }
 
     @Override
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/ISubscriptionSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/ISubscriptionSqlDao.java
index e1af556..f3c5c7d 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/ISubscriptionSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/ISubscriptionSqlDao.java
@@ -41,6 +41,8 @@ import org.skife.jdbi.v2.tweak.ResultSetMapper;
 import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.entitlement.api.user.ISubscription;
 import com.ning.billing.entitlement.api.user.Subscription;
+import com.ning.billing.entitlement.api.user.SubscriptionBuilder;
+
 
 @ExternalizedSqlViaStringTemplate3()
 public interface ISubscriptionSqlDao extends Transactional<ISubscriptionSqlDao>, CloseMe, Transmogrifier {
@@ -98,10 +100,17 @@ public interface ISubscriptionSqlDao extends Transactional<ISubscriptionSqlDao>,
             DateTime ptd = getDate(r, "ptd_dt");
             long activeVersion = r.getLong("active_version");
 
-            Subscription subscription = new Subscription(id, bundleId, category, bundleStartDate, startDate, ctd, ptd, activeVersion);
+            Subscription subscription = new Subscription(new SubscriptionBuilder()
+            .setId(id)
+            .setBundleId(bundleId)
+            .setCategory(category)
+            .setBundleStartDate(bundleStartDate)
+            .setStartDate(startDate)
+            .setActiveVersion(activeVersion)
+            .setChargedThroughDate(ctd)
+            .setPaidThroughDate(ptd),
+                true);
             return subscription;
         }
     }
-
-
 }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/EntitlementDaoMemoryMock.java b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/EntitlementDaoMemoryMock.java
index c6fff2f..e4d2559 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/EntitlementDaoMemoryMock.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/EntitlementDaoMemoryMock.java
@@ -36,6 +36,7 @@ import com.ning.billing.config.IEntitlementConfig;
 import com.ning.billing.entitlement.api.user.ISubscription;
 import com.ning.billing.entitlement.api.user.ISubscriptionBundle;
 import com.ning.billing.entitlement.api.user.Subscription;
+import com.ning.billing.entitlement.api.user.SubscriptionBuilder;
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
 import com.ning.billing.entitlement.events.IEvent;
 import com.ning.billing.entitlement.events.IEvent.EventType;
@@ -227,8 +228,7 @@ public class EntitlementDaoMemoryMock implements IEntitlementDao, IEntitlementDa
     }
 
     private ISubscription buildSubscription(Subscription in) {
-        return new Subscription(in.getId(), in.getBundleId(), in.getCategory(), in.getBundleStartDate(),
-                in.getStartDate(), in.getChargedThroughDate(), in.getPaidThroughDate(), in.getActiveVersion());
+        return new Subscription(new SubscriptionBuilder(in), true);
     }
 
     @Override