killbill-memoizeit
Changes
entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionApi.java 268(+117 -151)
entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionBundleTimeline.java 754(+379 -375)
entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultAccountEntitlements.java 90(+90 -0)
entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultAccountEventsStreams.java 108(+108 -0)
entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultEntitlementInternalApi.java 101(+101 -0)
entitlement/src/main/java/com/ning/billing/entitlement/dao/OptimizedProxyBlockingStateDao.java 75(+75 -0)
entitlement/src/main/java/com/ning/billing/entitlement/engine/core/DefaultEventsStream.java 153(+125 -28)
entitlement/src/main/java/com/ning/billing/entitlement/engine/core/EventsStreamBuilder.java 217(+124 -93)
entitlement/src/test/java/com/ning/billing/entitlement/engine/core/TestEntitlementUtils.java 50(+46 -4)
entitlement/src/test/java/com/ning/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java 3(+3 -0)
Details
diff --git a/api/src/main/java/com/ning/billing/entitlement/AccountEntitlements.java b/api/src/main/java/com/ning/billing/entitlement/AccountEntitlements.java
new file mode 100644
index 0000000..3a192e9
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/entitlement/AccountEntitlements.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2010-2013 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;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.UUID;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.entitlement.api.Entitlement;
+import com.ning.billing.subscription.api.user.SubscriptionBaseBundle;
+
+// Wrapper object to save on DAO calls
+public interface AccountEntitlements {
+
+ public Account getAccount();
+
+ // Map bundle id -> bundle
+ public Map<UUID, SubscriptionBaseBundle> getBundles();
+
+ // Map bundle id -> entitlements
+ public Map<UUID, Collection<Entitlement>> getEntitlements();
+}
diff --git a/api/src/main/java/com/ning/billing/entitlement/AccountEventsStreams.java b/api/src/main/java/com/ning/billing/entitlement/AccountEventsStreams.java
new file mode 100644
index 0000000..94e7817
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/entitlement/AccountEventsStreams.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2010-2013 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;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.UUID;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.subscription.api.user.SubscriptionBaseBundle;
+
+// Wrapper object to save on DAO calls
+public interface AccountEventsStreams {
+
+ public Account getAccount();
+
+ // Map bundle id -> bundle
+ public Map<UUID, SubscriptionBaseBundle> getBundles();
+
+ // Map bundle id -> events streams
+ public Map<UUID, Collection<EventsStream>> getEventsStreams();
+}
diff --git a/api/src/main/java/com/ning/billing/entitlement/EntitlementInternalApi.java b/api/src/main/java/com/ning/billing/entitlement/EntitlementInternalApi.java
new file mode 100644
index 0000000..44953ea
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/entitlement/EntitlementInternalApi.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2010-2013 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;
+
+import java.util.UUID;
+
+import com.ning.billing.entitlement.api.EntitlementApiException;
+import com.ning.billing.util.callcontext.TenantContext;
+
+public interface EntitlementInternalApi {
+
+ public AccountEntitlements getAllEntitlementsForAccountId(UUID accountId, TenantContext context) throws EntitlementApiException;
+}
diff --git a/api/src/main/java/com/ning/billing/entitlement/EventsStream.java b/api/src/main/java/com/ning/billing/entitlement/EventsStream.java
new file mode 100644
index 0000000..fc57b74
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/entitlement/EventsStream.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2010-2013 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;
+
+import java.util.Collection;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.joda.time.LocalDate;
+
+import com.ning.billing.callcontext.InternalTenantContext;
+import com.ning.billing.entitlement.api.BlockingState;
+import com.ning.billing.entitlement.api.Entitlement.EntitlementState;
+import com.ning.billing.subscription.api.SubscriptionBase;
+
+public interface EventsStream {
+
+ UUID getAccountId();
+
+ DateTimeZone getAccountTimeZone();
+
+ UUID getBundleId();
+
+ String getBundleExternalKey();
+
+ UUID getEntitlementId();
+
+ EntitlementState getEntitlementState();
+
+ LocalDate getEntitlementEffectiveEndDate();
+
+ SubscriptionBase getSubscription();
+
+ SubscriptionBase getBaseSubscription();
+
+ boolean isEntitlementActive();
+
+ boolean isBlockChange();
+
+ boolean isEntitlementCancelled();
+
+ boolean isSubscriptionCancelled();
+
+ Collection<BlockingState> getCurrentSubscriptionEntitlementBlockingStatesForServices();
+
+ Collection<BlockingState> getPendingEntitlementCancellationEvents();
+
+ Collection<BlockingState> getSubscriptionEntitlementStates();
+
+ Collection<BlockingState> getBundleEntitlementStates();
+
+ Collection<BlockingState> getAccountEntitlementStates();
+
+ Collection<BlockingState> computeAddonsBlockingStatesForNextSubscriptionBaseEvent(DateTime effectiveDate);
+
+ Collection<BlockingState> computeAddonsBlockingStatesForFutureSubscriptionBaseEvents();
+
+ InternalTenantContext getInternalTenantContext();
+}
diff --git a/api/src/main/java/com/ning/billing/glue/EntitlementModule.java b/api/src/main/java/com/ning/billing/glue/EntitlementModule.java
index 456fb17..bd21d81 100644
--- a/api/src/main/java/com/ning/billing/glue/EntitlementModule.java
+++ b/api/src/main/java/com/ning/billing/glue/EntitlementModule.java
@@ -17,12 +17,15 @@
package com.ning.billing.glue;
public interface EntitlementModule {
+
public void installBlockingStateDao();
public void installBlockingApi();
public void installEntitlementApi();
+ public void installEntitlementInternalApi();
+
public void installSubscriptionApi();
public void installBlockingChecker();
diff --git a/api/src/main/java/com/ning/billing/glue/SubscriptionModule.java b/api/src/main/java/com/ning/billing/glue/SubscriptionModule.java
index a400b68..65185eb 100644
--- a/api/src/main/java/com/ning/billing/glue/SubscriptionModule.java
+++ b/api/src/main/java/com/ning/billing/glue/SubscriptionModule.java
@@ -27,5 +27,4 @@ public interface SubscriptionModule {
public void installSubscriptionInternalApi();
public void installSubscriptionTimelineApi();
-
}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlement.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlement.java
index 983381d..ac479f7 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlement.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlement.java
@@ -21,10 +21,10 @@ import java.util.Collection;
import java.util.UUID;
import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
import org.joda.time.LocalDate;
import com.ning.billing.ErrorCode;
-import com.ning.billing.account.api.Account;
import com.ning.billing.callcontext.InternalCallContext;
import com.ning.billing.catalog.api.BillingActionPolicy;
import com.ning.billing.catalog.api.BillingPeriod;
@@ -36,12 +36,12 @@ import com.ning.billing.catalog.api.ProductCategory;
import com.ning.billing.clock.Clock;
import com.ning.billing.entitlement.DefaultEntitlementService;
import com.ning.billing.entitlement.EntitlementService;
+import com.ning.billing.entitlement.EventsStream;
import com.ning.billing.entitlement.block.BlockingChecker;
import com.ning.billing.entitlement.dao.BlockingStateDao;
import com.ning.billing.entitlement.engine.core.EntitlementNotificationKey;
import com.ning.billing.entitlement.engine.core.EntitlementNotificationKeyAction;
import com.ning.billing.entitlement.engine.core.EntitlementUtils;
-import com.ning.billing.entitlement.engine.core.EventsStream;
import com.ning.billing.entitlement.engine.core.EventsStreamBuilder;
import com.ning.billing.entity.EntityBase;
import com.ning.billing.junction.DefaultBlockingState;
@@ -88,7 +88,7 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
final SubscriptionBaseInternalApi subscriptionInternalApi, final BlockingChecker checker,
final NotificationQueueService notificationQueueService, final EntitlementUtils entitlementUtils,
final EntitlementDateHelper dateHelper, final Clock clock, final InternalCallContextFactory internalCallContextFactory) {
- super(eventsStream.getSubscription().getId(), eventsStream.getSubscription().getCreatedDate(), eventsStream.getSubscription().getUpdatedDate());
+ super(eventsStream.getEntitlementId(), eventsStream.getSubscription().getCreatedDate(), eventsStream.getSubscription().getUpdatedDate());
this.eventsStreamBuilder = eventsStreamBuilder;
this.eventsStream = eventsStream;
this.dateHelper = dateHelper;
@@ -116,18 +116,22 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
in.getInternalCallContextFactory());
}
- public Account getAccount() {
- return eventsStream.getAccount();
- }
-
public EventsStream getEventsStream() {
return eventsStream;
}
+ public DateTimeZone getAccountTimeZone() {
+ return eventsStream.getAccountTimeZone();
+ }
+
public SubscriptionBase getSubscriptionBase() {
return eventsStream.getSubscription();
}
+ public SubscriptionBase getBaseSubscription() {
+ return eventsStream.getBaseSubscription();
+ }
+
public EventsStreamBuilder getEventsStreamBuilder() {
return eventsStreamBuilder;
}
@@ -170,22 +174,22 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
@Override
public UUID getBaseEntitlementId() {
- return eventsStream.getSubscription().getId();
+ return eventsStream.getEntitlementId();
}
@Override
public UUID getBundleId() {
- return eventsStream.getSubscription().getBundleId();
+ return eventsStream.getBundleId();
}
@Override
public UUID getAccountId() {
- return eventsStream.getAccount().getId();
+ return eventsStream.getAccountId();
}
@Override
public String getExternalKey() {
- return eventsStream.getBundle().getExternalKey();
+ return eventsStream.getBundleExternalKey();
}
@Override
@@ -200,7 +204,7 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
@Override
public LocalDate getEffectiveStartDate() {
- return new LocalDate(getSubscriptionBase().getStartDate(), eventsStream.getAccount().getTimeZone());
+ return new LocalDate(getSubscriptionBase().getStartDate(), eventsStream.getAccountTimeZone());
}
@Override
@@ -319,7 +323,7 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
// (we don't want an entitlement cancel date one second or so after the subscription cancel date or add-ons cancellations
// computations won't work).
final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(getAccountId(), callContext);
- final LocalDate effectiveLocalDate = new LocalDate(localCancelDate, eventsStream.getAccount().getTimeZone());
+ final LocalDate effectiveLocalDate = new LocalDate(localCancelDate, eventsStream.getAccountTimeZone());
final DateTime effectiveDate = dateHelper.fromLocalDateAndReferenceTime(effectiveLocalDate, getSubscriptionBase().getStartDate(), contextWithValidAccountRecordId);
try {
@@ -341,10 +345,10 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
final LocalDate cancellationDate;
switch (entitlementPolicy) {
case IMMEDIATE:
- cancellationDate = new LocalDate(clock.getUTCNow(), eventsStream.getAccount().getTimeZone());
+ cancellationDate = new LocalDate(clock.getUTCNow(), eventsStream.getAccountTimeZone());
break;
case END_OF_TERM:
- cancellationDate = getSubscriptionBase().getChargedThroughDate() != null ? new LocalDate(getSubscriptionBase().getChargedThroughDate(), eventsStream.getAccount().getTimeZone()) : new LocalDate(clock.getUTCNow(), eventsStream.getAccount().getTimeZone());
+ cancellationDate = getSubscriptionBase().getChargedThroughDate() != null ? new LocalDate(getSubscriptionBase().getChargedThroughDate(), eventsStream.getAccountTimeZone()) : new LocalDate(clock.getUTCNow(), eventsStream.getAccountTimeZone());
break;
default:
throw new RuntimeException("Unsupported policy " + entitlementPolicy);
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlementApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlementApi.java
index 5be1c0e..dfd9cd6 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlementApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlementApi.java
@@ -17,9 +17,7 @@
package com.ning.billing.entitlement.api;
import java.io.IOException;
-import java.util.LinkedList;
import java.util.List;
-import java.util.Map;
import java.util.UUID;
import javax.inject.Inject;
@@ -41,15 +39,16 @@ import com.ning.billing.callcontext.InternalTenantContext;
import com.ning.billing.catalog.api.BillingActionPolicy;
import com.ning.billing.catalog.api.PlanPhaseSpecifier;
import com.ning.billing.clock.Clock;
+import com.ning.billing.entitlement.AccountEventsStreams;
import com.ning.billing.entitlement.DefaultEntitlementService;
import com.ning.billing.entitlement.EntitlementService;
import com.ning.billing.entitlement.EntitlementTransitionType;
+import com.ning.billing.entitlement.EventsStream;
import com.ning.billing.entitlement.block.BlockingChecker;
import com.ning.billing.entitlement.dao.BlockingStateDao;
import com.ning.billing.entitlement.engine.core.EntitlementNotificationKey;
import com.ning.billing.entitlement.engine.core.EntitlementNotificationKeyAction;
import com.ning.billing.entitlement.engine.core.EntitlementUtils;
-import com.ning.billing.entitlement.engine.core.EventsStream;
import com.ning.billing.entitlement.engine.core.EventsStreamBuilder;
import com.ning.billing.junction.DefaultBlockingState;
import com.ning.billing.notificationq.api.NotificationEvent;
@@ -141,8 +140,8 @@ public class DefaultEntitlementApi implements EntitlementApi {
}
// Check the base entitlement state is not blocked
- if (eventsStreamForBaseSubscription.getCurrentBlockingAggregator().isBlockChange()) {
- throw new EntitlementApiException(new BlockingApiException(ErrorCode.BLOCK_BLOCKED_ACTION, BlockingChecker.ACTION_CHANGE, BlockingChecker.TYPE_SUBSCRIPTION, eventsStreamForBaseSubscription.getSubscription().getId().toString()));
+ if (eventsStreamForBaseSubscription.isBlockChange()) {
+ throw new EntitlementApiException(new BlockingApiException(ErrorCode.BLOCK_BLOCKED_ACTION, BlockingChecker.ACTION_CHANGE, BlockingChecker.TYPE_SUBSCRIPTION, eventsStreamForBaseSubscription.getEntitlementId().toString()));
}
final DateTime requestedDate = dateHelper.fromLocalDateAndReferenceTime(effectiveDate, eventsStreamForBaseSubscription.getSubscription().getStartDate(), eventsStreamForBaseSubscription.getInternalTenantContext());
@@ -184,8 +183,21 @@ public class DefaultEntitlementApi implements EntitlementApi {
@Override
public List<Entitlement> getAllEntitlementsForBundle(final UUID bundleId, final TenantContext tenantContext) throws EntitlementApiException {
- final InternalTenantContext context = internalCallContextFactory.createInternalTenantContext(tenantContext);
- return getAllEntitlementsForBundle(subscriptionInternalApi.getSubscriptionsForBundle(bundleId, context), tenantContext);
+ final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContext(tenantContext);
+ final UUID accountId;
+ try {
+ accountId = subscriptionInternalApi.getBundleFromId(bundleId, internalContext).getAccountId();
+ } catch (SubscriptionBaseApiException e) {
+ throw new EntitlementApiException(e);
+ }
+
+ return ImmutableList.<Entitlement>copyOf(Iterables.<Entitlement>filter(getAllEntitlementsForAccountId(accountId, tenantContext),
+ new Predicate<Entitlement>() {
+ @Override
+ public boolean apply(final Entitlement input) {
+ return bundleId.equals(input.getBundleId());
+ }
+ }));
}
@Override
@@ -202,29 +214,20 @@ public class DefaultEntitlementApi implements EntitlementApi {
@Override
public List<Entitlement> getAllEntitlementsForAccountId(final UUID accountId, final TenantContext tenantContext) throws EntitlementApiException {
+ final EntitlementApi entitlementApi = this;
final InternalTenantContext context = internalCallContextFactory.createInternalTenantContext(accountId, tenantContext);
- final Map<UUID, List<SubscriptionBase>> subscriptionsPerBundle = subscriptionInternalApi.getSubscriptionsForAccount(context);
-
- final List<Entitlement> result = new LinkedList<Entitlement>();
- for (final UUID bundleId : subscriptionsPerBundle.keySet()) {
- final List<Entitlement> entitlements = getAllEntitlementsForBundle(subscriptionsPerBundle.get(bundleId), tenantContext);
- result.addAll(entitlements);
- }
- return result;
- }
- private List<Entitlement> getAllEntitlementsForBundle(final List<SubscriptionBase> subscriptions, final TenantContext context) {
- return Lists.transform(subscriptions,
- new Function<SubscriptionBase, Entitlement>() {
- @Override
- public Entitlement apply(final SubscriptionBase input) {
- try {
- return getEntitlementForId(input.getId(), context);
- } catch (EntitlementApiException e) {
- throw new RuntimeException("Failed to extract blocking state for subscription " + input.getId().toString());
- }
- }
- });
+ final AccountEventsStreams accountEventsStreams = eventsStreamBuilder.buildForAccount(context);
+ final List<EventsStream> eventsStreams = ImmutableList.<EventsStream>copyOf(Iterables.<EventsStream>concat(accountEventsStreams.getEventsStreams().values()));
+ return Lists.<EventsStream, Entitlement>transform(eventsStreams,
+ new Function<EventsStream, Entitlement>() {
+ @Override
+ public Entitlement apply(final EventsStream eventsStream) {
+ return new DefaultEntitlement(eventsStream, eventsStreamBuilder, entitlementApi,
+ blockingStateDao, subscriptionInternalApi, checker, notificationQueueService,
+ entitlementUtils, dateHelper, clock, internalCallContextFactory);
+ }
+ });
}
@Override
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscription.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscription.java
index 25842b7..3938659 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscription.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscription.java
@@ -16,29 +16,32 @@
package com.ning.billing.entitlement.api;
-import java.util.List;
+import java.util.Collection;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+
public class DefaultSubscription extends DefaultEntitlement implements Subscription {
- private final List<BlockingState> blockingStates;
+ private final Collection<BlockingState> currentSubscriptionBlockingStatesForServices;
- DefaultSubscription(final DefaultEntitlement entitlement, final List<BlockingState> blockingStates) {
+ DefaultSubscription(final DefaultEntitlement entitlement) {
super(entitlement);
- this.blockingStates = blockingStates;
+ this.currentSubscriptionBlockingStatesForServices = eventsStream.getCurrentSubscriptionEntitlementBlockingStatesForServices();
}
@Override
public LocalDate getBillingStartDate() {
- return new LocalDate(getSubscriptionBase().getStartDate(), getAccount().getTimeZone());
+ return new LocalDate(getSubscriptionBase().getStartDate(), getAccountTimeZone());
}
@Override
public LocalDate getBillingEndDate() {
final DateTime futureOrCurrentEndDateForSubscription = getSubscriptionBase().getEndDate() != null ? getSubscriptionBase().getEndDate() : getSubscriptionBase().getFutureEndDate();
- final DateTime futureOrCurrentEndDateForBaseSubscription = getEventsStream().getBaseSubscription().getEndDate() != null ? getEventsStream().getBaseSubscription().getEndDate() : getEventsStream().getBaseSubscription().getFutureEndDate();
+ final DateTime futureOrCurrentEndDateForBaseSubscription = getBaseSubscription().getEndDate() != null ? getBaseSubscription().getEndDate() : getBaseSubscription().getFutureEndDate();
final DateTime futureOrCurrentEndDate;
if (futureOrCurrentEndDateForBaseSubscription != null && futureOrCurrentEndDateForBaseSubscription.isBefore(futureOrCurrentEndDateForSubscription)) {
@@ -47,25 +50,27 @@ public class DefaultSubscription extends DefaultEntitlement implements Subscript
futureOrCurrentEndDate = futureOrCurrentEndDateForSubscription;
}
- return futureOrCurrentEndDate != null ? new LocalDate(futureOrCurrentEndDate, getAccount().getTimeZone()) : null;
+ return futureOrCurrentEndDate != null ? new LocalDate(futureOrCurrentEndDate, getAccountTimeZone()) : null;
}
@Override
public LocalDate getChargedThroughDate() {
- return getSubscriptionBase().getChargedThroughDate() != null ? new LocalDate(getSubscriptionBase().getChargedThroughDate(), getAccount().getTimeZone()) : null;
+ return getSubscriptionBase().getChargedThroughDate() != null ? new LocalDate(getSubscriptionBase().getChargedThroughDate(), getAccountTimeZone()) : null;
}
@Override
public String getCurrentStateForService(final String serviceName) {
-
- if (blockingStates == null) {
+ if (currentSubscriptionBlockingStatesForServices == null) {
return null;
+ } else {
+ final BlockingState blockingState = Iterables.<BlockingState>tryFind(currentSubscriptionBlockingStatesForServices,
+ new Predicate<BlockingState>() {
+ @Override
+ public boolean apply(final BlockingState input) {
+ return serviceName.equals(input.getService());
+ }
+ }).orNull();
+ return blockingState == null ? null : blockingState.getService();
}
- for (BlockingState cur : blockingStates) {
- if (cur.getService().equals(serviceName)) {
- return cur.getStateName();
- }
- }
- return null;
}
}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionApi.java
index 850317f..d3b480e 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionApi.java
@@ -17,11 +17,10 @@
package com.ning.billing.entitlement.api;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
+import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
-import java.util.Set;
+import java.util.Map;
import java.util.UUID;
import javax.inject.Inject;
@@ -29,217 +28,184 @@ import javax.inject.Inject;
import org.joda.time.DateTimeZone;
import com.ning.billing.ErrorCode;
-import com.ning.billing.account.api.Account;
-import com.ning.billing.account.api.AccountApiException;
-import com.ning.billing.account.api.AccountInternalApi;
+import com.ning.billing.ObjectType;
import com.ning.billing.callcontext.InternalTenantContext;
-import com.ning.billing.clock.Clock;
-import com.ning.billing.entitlement.block.BlockingChecker;
-import com.ning.billing.entitlement.dao.BlockingStateDao;
+import com.ning.billing.entitlement.AccountEntitlements;
+import com.ning.billing.entitlement.EntitlementInternalApi;
import com.ning.billing.subscription.api.SubscriptionBaseInternalApi;
import com.ning.billing.subscription.api.user.SubscriptionBaseApiException;
import com.ning.billing.subscription.api.user.SubscriptionBaseBundle;
+import com.ning.billing.util.cache.Cachable.CacheType;
+import com.ning.billing.util.cache.CacheControllerDispatcher;
import com.ning.billing.util.callcontext.InternalCallContextFactory;
import com.ning.billing.util.callcontext.TenantContext;
+import com.ning.billing.util.customfield.ShouldntHappenException;
+import com.ning.billing.util.dao.NonEntityDao;
-import com.google.common.base.Function;
+import com.google.common.base.Optional;
import com.google.common.base.Predicate;
-import com.google.common.collect.Collections2;
-import com.google.common.collect.LinkedListMultimap;
-import com.google.common.collect.ListMultimap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
public class DefaultSubscriptionApi implements SubscriptionApi {
+ private final EntitlementInternalApi entitlementInternalApi;
private final SubscriptionBaseInternalApi subscriptionInternalApi;
- private final EntitlementApi entitlementApi;
- private final BlockingChecker checker;
- private final BlockingStateDao blockingStateDao;
- private final EntitlementDateHelper dateHelper;
- private final Clock clock;
private final InternalCallContextFactory internalCallContextFactory;
- private final AccountInternalApi accountApi;
+ private final NonEntityDao nonEntityDao;
+ private final CacheControllerDispatcher cacheControllerDispatcher;
@Inject
- public DefaultSubscriptionApi(final SubscriptionBaseInternalApi subscriptionInternalApi, final EntitlementApi entitlementApi, final BlockingChecker checker, final BlockingStateDao blockingStateDao, final AccountInternalApi accountApi, final Clock clock, final InternalCallContextFactory internalCallContextFactory) {
+ public DefaultSubscriptionApi(final EntitlementInternalApi entitlementInternalApi, final SubscriptionBaseInternalApi subscriptionInternalApi,
+ final InternalCallContextFactory internalCallContextFactory, final NonEntityDao nonEntityDao, final CacheControllerDispatcher cacheControllerDispatcher) {
+ this.entitlementInternalApi = entitlementInternalApi;
this.subscriptionInternalApi = subscriptionInternalApi;
- this.entitlementApi = entitlementApi;
- this.accountApi = accountApi;
- this.checker = checker;
- this.blockingStateDao = blockingStateDao;
- this.dateHelper = new EntitlementDateHelper(accountApi, clock);
- ;
- this.clock = clock;
this.internalCallContextFactory = internalCallContextFactory;
+ this.nonEntityDao = nonEntityDao;
+ this.cacheControllerDispatcher = cacheControllerDispatcher;
}
@Override
public Subscription getSubscriptionForEntitlementId(final UUID entitlementId, final TenantContext context) throws SubscriptionApiException {
+ final Long accountRecordId = nonEntityDao.retrieveAccountRecordIdFromObject(entitlementId, ObjectType.SUBSCRIPTION, cacheControllerDispatcher.getCacheController(CacheType.ACCOUNT_RECORD_ID));
+ final UUID accountId = nonEntityDao.retrieveIdFromObject(accountRecordId, ObjectType.ACCOUNT);
+
+ // Retrieve entitlements
+ final AccountEntitlements accountEntitlements;
try {
- final Entitlement entitlement = entitlementApi.getEntitlementForId(entitlementId, context);
- final InternalTenantContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalTenantContext(entitlement.getAccountId(), context);
- return fromEntitlement(entitlement, contextWithValidAccountRecordId);
+ accountEntitlements = entitlementInternalApi.getAllEntitlementsForAccountId(accountId, context);
} catch (EntitlementApiException e) {
throw new SubscriptionApiException(e);
}
+
+ // Build subscriptions
+ final Iterable<Subscription> accountSubscriptions = Iterables.<Subscription>concat(buildSubscriptionsFromEntitlements(accountEntitlements).values());
+
+ return Iterables.<Subscription>find(accountSubscriptions,
+ new Predicate<Subscription>() {
+ @Override
+ public boolean apply(final Subscription subscription) {
+ return subscription.getId().equals(entitlementId);
+ }
+ });
}
@Override
public SubscriptionBundle getSubscriptionBundle(final UUID bundleId, final TenantContext context) throws SubscriptionApiException {
-
- try {
-
- final List<Entitlement> entitlements = entitlementApi.getAllEntitlementsForBundle(bundleId, context);
- if (entitlements.isEmpty()) {
- throw new SubscriptionApiException(ErrorCode.SUB_GET_INVALID_BUNDLE_ID, bundleId);
- }
- return getSubscriptionBundleFromEntitlements(bundleId, entitlements, context);
- } catch (EntitlementApiException e) {
- throw new SubscriptionApiException(e);
- } catch (SubscriptionBaseApiException e) {
- throw new SubscriptionApiException(e);
- } catch (AccountApiException e) {
- throw new SubscriptionApiException(e);
+ final Long accountRecordId = nonEntityDao.retrieveAccountRecordIdFromObject(bundleId, ObjectType.BUNDLE, cacheControllerDispatcher.getCacheController(CacheType.ACCOUNT_RECORD_ID));
+ final UUID accountId = nonEntityDao.retrieveIdFromObject(accountRecordId, ObjectType.ACCOUNT);
+
+ final Optional<SubscriptionBundle> bundleOptional = Iterables.<SubscriptionBundle>tryFind(getSubscriptionBundlesForAccount(accountId, context),
+ new Predicate<SubscriptionBundle>() {
+ @Override
+ public boolean apply(final SubscriptionBundle bundle) {
+ return bundle.getId().equals(bundleId);
+ }
+ });
+ if (!bundleOptional.isPresent()) {
+ throw new SubscriptionApiException(ErrorCode.SUB_GET_INVALID_BUNDLE_ID, bundleId);
+ } else {
+ return bundleOptional.get();
}
}
-
@Override
public List<SubscriptionBundle> getSubscriptionBundlesForAccountIdAndExternalKey(final UUID accountId, final String externalKey, final TenantContext context) throws SubscriptionApiException {
-
- try {
-
- final List<Entitlement> entitlements = entitlementApi.getAllEntitlementsForAccountIdAndExternalKey(accountId, externalKey, context);
- if (entitlements.isEmpty()) {
- throw new SubscriptionApiException(ErrorCode.SUB_GET_INVALID_BUNDLE_KEY, externalKey);
- }
- return getSubscriptionBundles(entitlements, context);
- } catch (EntitlementApiException e) {
- throw new SubscriptionApiException(e);
- } catch (SubscriptionBaseApiException e) {
- throw new SubscriptionApiException(e);
- } catch (AccountApiException e) {
- throw new SubscriptionApiException(e);
- }
+ return ImmutableList.<SubscriptionBundle>copyOf(Iterables.<SubscriptionBundle>filter(getSubscriptionBundlesForAccount(accountId, context),
+ new Predicate<SubscriptionBundle>() {
+ @Override
+ public boolean apply(final SubscriptionBundle bundle) {
+ return bundle.getExternalKey().equals(externalKey);
+ }
+ }));
}
@Override
- public SubscriptionBundle getActiveSubscriptionBundleForExternalKey(final String externalKey,
- final TenantContext context) throws SubscriptionApiException {
-
+ public SubscriptionBundle getActiveSubscriptionBundleForExternalKey(final String externalKey, final TenantContext context) throws SubscriptionApiException {
final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContext(context);
try {
final SubscriptionBaseBundle baseBundle = subscriptionInternalApi.getActiveBundleForKey(externalKey, internalContext);
- final List<Entitlement> allEntitlementsForBundle = entitlementApi.getAllEntitlementsForBundle(baseBundle.getId(), context);
-
- return getSubscriptionBundleFromEntitlements(baseBundle.getId(), allEntitlementsForBundle, context);
+ return getSubscriptionBundle(baseBundle.getId(), context);
} catch (SubscriptionBaseApiException e) {
throw new SubscriptionApiException(e);
- } catch (EntitlementApiException e) {
- throw new SubscriptionApiException(e);
- } catch (AccountApiException e) {
- throw new SubscriptionApiException(e);
}
}
@Override
public List<SubscriptionBundle> getSubscriptionBundlesForExternalKey(final String externalKey, final TenantContext context) throws SubscriptionApiException {
-
final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContext(context);
- try {
- final List<SubscriptionBaseBundle> baseBundles = subscriptionInternalApi.getBundlesForKey(externalKey, internalContext);
- final List<SubscriptionBundle> result = new ArrayList<SubscriptionBundle>(baseBundles.size());
- for (SubscriptionBaseBundle cur : baseBundles) {
- final List<Entitlement> allEntitlementsForBundle = entitlementApi.getAllEntitlementsForBundle(cur.getId(), context);
- final SubscriptionBundle bundle = getSubscriptionBundleFromEntitlements(cur.getId(), allEntitlementsForBundle, context);
- result.add(bundle);
- }
- return result;
- } catch (SubscriptionBaseApiException e) {
- throw new SubscriptionApiException(e);
- } catch (EntitlementApiException e) {
- throw new SubscriptionApiException(e);
- } catch (AccountApiException e) {
- throw new SubscriptionApiException(e);
+ final List<SubscriptionBaseBundle> baseBundles = subscriptionInternalApi.getBundlesForKey(externalKey, internalContext);
+
+ final List<SubscriptionBundle> result = new ArrayList<SubscriptionBundle>(baseBundles.size());
+ for (final SubscriptionBaseBundle cur : baseBundles) {
+ final SubscriptionBundle bundle = getSubscriptionBundle(cur.getId(), context);
+ result.add(bundle);
}
+
+ return result;
}
@Override
- public List<SubscriptionBundle> getSubscriptionBundlesForAccountId(final UUID accountId,
- final TenantContext context) throws SubscriptionApiException {
- try {
+ public List<SubscriptionBundle> getSubscriptionBundlesForAccountId(final UUID accountId, final TenantContext context) throws SubscriptionApiException {
+ return getSubscriptionBundlesForAccount(accountId, context);
+ }
- final List<Entitlement> entitlements = entitlementApi.getAllEntitlementsForAccountId(accountId, context);
- if (entitlements.isEmpty()) {
- return Collections.emptyList();
- }
- return getSubscriptionBundles(entitlements, context);
+ private List<SubscriptionBundle> getSubscriptionBundlesForAccount(final UUID accountId, final TenantContext tenantContext) throws SubscriptionApiException {
+ // Retrieve entitlements
+ final AccountEntitlements accountEntitlements;
+ try {
+ accountEntitlements = entitlementInternalApi.getAllEntitlementsForAccountId(accountId, tenantContext);
} catch (EntitlementApiException e) {
throw new SubscriptionApiException(e);
- } catch (SubscriptionBaseApiException e) {
- throw new SubscriptionApiException(e);
- } catch (AccountApiException e) {
- throw new SubscriptionApiException(e);
}
- }
- private List<SubscriptionBundle> getSubscriptionBundles(final List<Entitlement> entitlements, final TenantContext context) throws SubscriptionBaseApiException, AccountApiException {
- final ListMultimap<UUID, Entitlement> perBundleEntitlements = LinkedListMultimap.create();
- for (Entitlement cur : entitlements) {
- perBundleEntitlements.put(cur.getBundleId(), cur);
+ // Build subscriptions
+ final Map<UUID, List<Subscription>> subscriptionsPerBundle = buildSubscriptionsFromEntitlements(accountEntitlements);
+
+ final DateTimeZone accountTimeZone = accountEntitlements.getAccount().getTimeZone();
+
+ // Build subscription bundles
+ final List<SubscriptionBundle> bundles = new LinkedList<SubscriptionBundle>();
+ for (final UUID bundleId : subscriptionsPerBundle.keySet()) {
+ final List<Subscription> subscriptionsForBundle = subscriptionsPerBundle.get(bundleId);
+ final String externalKey = subscriptionsForBundle.get(0).getExternalKey();
+
+ final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone,
+ accountId,
+ bundleId,
+ externalKey,
+ accountEntitlements.getEntitlements().get(bundleId));
+
+ final SubscriptionBaseBundle baseBundle = accountEntitlements.getBundles().get(bundleId);
+ final SubscriptionBundle subscriptionBundle = new DefaultSubscriptionBundle(bundleId,
+ accountId,
+ externalKey,
+ subscriptionsForBundle,
+ timeline,
+ baseBundle.getOriginalCreatedDate(),
+ baseBundle.getCreatedDate(),
+ baseBundle.getUpdatedDate());
+ bundles.add(subscriptionBundle);
}
- final List<SubscriptionBundle> result = new ArrayList<SubscriptionBundle>(perBundleEntitlements.keySet().size());
- for (UUID bundleId : perBundleEntitlements.keySet()) {
- final List<Entitlement> allEntitlementsForBundle = perBundleEntitlements.get(bundleId);
- final SubscriptionBundle bundle = getSubscriptionBundleFromEntitlements(bundleId, allEntitlementsForBundle, context);
- result.add(bundle);
- }
- return result;
+ return bundles;
}
- private Subscription fromEntitlement(final Entitlement entitlement, final InternalTenantContext internalTenantContext) {
-
- final List<BlockingState> states = blockingStateDao.getBlockingState(entitlement.getId(), BlockingStateType.SUBSCRIPTION, internalTenantContext);
- final Subscription result = new DefaultSubscription((DefaultEntitlement) entitlement, states);
- return result;
- }
-
- private SubscriptionBundle getSubscriptionBundleFromEntitlements(final UUID bundleId, final List<Entitlement> entitlements, final TenantContext context) throws SubscriptionBaseApiException, AccountApiException {
- final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(context);
- final SubscriptionBaseBundle baseBundle = subscriptionInternalApi.getBundleFromId(bundleId, internalTenantContext);
- final List<Subscription> subscriptions = new ArrayList<Subscription>(entitlements.size());
- subscriptions.addAll(Collections2.transform(entitlements, new Function<Entitlement, Subscription>() {
- @Override
- public Subscription apply(final Entitlement input) {
- return fromEntitlement(input, internalTenantContext);
+ private Map<UUID, List<Subscription>> buildSubscriptionsFromEntitlements(final AccountEntitlements accountEntitlements) {
+ final Map<UUID, List<Subscription>> subscriptionsPerBundle = new HashMap<UUID, List<Subscription>>();
+ for (final UUID bundleId : accountEntitlements.getEntitlements().keySet()) {
+ if (subscriptionsPerBundle.get(bundleId) == null) {
+ subscriptionsPerBundle.put(bundleId, new LinkedList<Subscription>());
}
- }));
-
- final Account account = accountApi.getAccountById(baseBundle.getAccountId(), internalTenantContext);
-
- final InternalTenantContext internalTenantContextWithAccountRecordId = internalCallContextFactory.createInternalTenantContext(account.getId(), context);
-
- final DateTimeZone accountTimeZone = account.getTimeZone();
- final List<BlockingState> allBlockingStatesPerAccountRecordId = blockingStateDao.getBlockingAllForAccountRecordId(internalTenantContextWithAccountRecordId);
- final Set<UUID> allEntitlementIds = new HashSet<UUID>(Collections2.<Entitlement, UUID>transform(entitlements, new Function<Entitlement, UUID>() {
- @Override
- public UUID apply(final Entitlement input) {
- return input.getId();
+ for (final Entitlement entitlement : accountEntitlements.getEntitlements().get(bundleId)) {
+ if (entitlement instanceof DefaultEntitlement) {
+ subscriptionsPerBundle.get(bundleId).add(new DefaultSubscription((DefaultEntitlement) entitlement));
+ } else {
+ throw new ShouldntHappenException("Entitlement should be a DefaultEntitlement instance");
+ }
}
- }));
-
- final List<BlockingState> filteredBlockingStates = new LinkedList<BlockingState>(Collections2.filter(allBlockingStatesPerAccountRecordId, new Predicate<BlockingState>() {
- @Override
- public boolean apply(final BlockingState input) {
- return input.getType() == BlockingStateType.ACCOUNT ||
- (input.getType() == BlockingStateType.SUBSCRIPTION_BUNDLE && input.getBlockedId().equals(bundleId)) ||
- (input.getType() == BlockingStateType.SUBSCRIPTION && allEntitlementIds.contains(input.getBlockedId()));
- }
- }));
-
- final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, account.getId(), bundleId, baseBundle.getExternalKey(), entitlements, filteredBlockingStates);
- final DefaultSubscriptionBundle bundle = new DefaultSubscriptionBundle(bundleId, baseBundle.getAccountId(), baseBundle.getExternalKey(), subscriptions, timeline, baseBundle.getOriginalCreatedDate() , baseBundle.getCreatedDate(), baseBundle.getUpdatedDate());
- return bundle;
+ }
+ return subscriptionsPerBundle;
}
}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionBundleTimeline.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionBundleTimeline.java
index 796eb06..608e6b6 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionBundleTimeline.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionBundleTimeline.java
@@ -21,6 +21,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
@@ -56,13 +57,11 @@ import com.google.common.collect.ImmutableList;
public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTimeline {
-
private final Logger logger = LoggerFactory.getLogger(DefaultSubscriptionBundleTimeline.class);
// STEPH This is added to give us confidence the timeline we generate behaves as expected. Could be removed at some point
private final static String TIMELINE_WARN_LOG = "Sanity Timeline: ";
-
public static final String BILLING_SERVICE_NAME = "billing-service";
public static final String ENT_BILLING_SERVICE_NAME = "entitlement+billing-service";
@@ -71,6 +70,18 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
private final UUID bundleId;
private final String externalKey;
+ public DefaultSubscriptionBundleTimeline(final DateTimeZone accountTimeZone, final UUID accountId, final UUID bundleId, final String externalKey, final Collection<Entitlement> entitlements) {
+ final Collection<BlockingState> blockingStates = new HashSet<BlockingState>();
+ for (final Entitlement entitlement : entitlements) {
+ blockingStates.addAll(((DefaultEntitlement) entitlement).getEventsStream().getSubscriptionEntitlementStates());
+ blockingStates.addAll(((DefaultEntitlement) entitlement).getEventsStream().getBundleEntitlementStates());
+ blockingStates.addAll(((DefaultEntitlement) entitlement).getEventsStream().getAccountEntitlementStates());
+ }
+ this.accountId = accountId;
+ this.bundleId = bundleId;
+ this.externalKey = externalKey;
+ this.events = computeEvents(entitlements, new LinkedList<BlockingState>(blockingStates), accountTimeZone);
+ }
public DefaultSubscriptionBundleTimeline(final DateTimeZone accountTimeZone, final UUID accountId, final UUID bundleId, final String externalKey, final List<Entitlement> entitlements, List<BlockingState> allBlockingStates) {
this.accountId = accountId;
@@ -85,7 +96,7 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
// - base subscription events are already ordered for each Entitlement and so when we reorder at the bundle level we try not to break that initial ordering
// - blocking state events occur at various level (account, bundle and subscription) so for higher level, we need to dispatch that on each subscription.
//
- private List<SubscriptionEvent> computeEvents(final List<Entitlement> entitlements, List<BlockingState> allBlockingStates, final DateTimeZone accountTimeZone) {
+ private List<SubscriptionEvent> computeEvents(final Collection<Entitlement> entitlements, List<BlockingState> allBlockingStates, final DateTimeZone accountTimeZone) {
// Extract ids for all entitlement in the list
final Set<UUID> allEntitlementUUIDs = new TreeSet(Collections2.transform(entitlements, new Function<Entitlement, UUID>() {
@@ -171,7 +182,7 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
int currentIndex = i;
if (shouldSwap) {
- Collections.swap(events, i, i+1);
+ Collections.swap(events, i, i + 1);
}
if (shouldReverseSort) {
while (currentIndex >= 1) {
@@ -189,7 +200,6 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
}
}
-
private boolean shouldSwap(DefaultSubscriptionEvent cur, DefaultSubscriptionEvent other, boolean isAscending) {
// For a given date, order by subscriptionId, and within subscription by event type
@@ -198,12 +208,11 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
((isAscending &&
((idComp > 0) ||
(idComp == 0 && cur.getSubscriptionEventType().ordinal() > other.getSubscriptionEventType().ordinal()))) ||
- (!isAscending &&
- ((idComp < 0) ||
- (idComp == 0 && cur.getSubscriptionEventType().ordinal() < other.getSubscriptionEventType().ordinal())))));
+ (!isAscending &&
+ ((idComp < 0) ||
+ (idComp == 0 && cur.getSubscriptionEventType().ordinal() < other.getSubscriptionEventType().ordinal())))));
}
-
private void insertAfterIndex(final LinkedList<SubscriptionEvent> original, final List<SubscriptionEvent> newEvents, int index) {
final boolean firstPosition = (index == -1);
@@ -227,7 +236,6 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
//
private int insertFromBlockingEvent(final DateTimeZone accountTimeZone, final Set<UUID> allEntitlementUUIDs, final List<SubscriptionEvent> result, final BlockingState bs, final DateTime bsEffectiveDate, final List<SubscriptionEvent> newEvents) {
-
// Keep the current state per entitlement
final Map<UUID, TargetState> targetStates = new HashMap<UUID, TargetState>();
for (UUID cur : allEntitlementUUIDs) {
@@ -325,7 +333,7 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
}
// Compute the initial stream of events based on the subscription base events
- private LinkedList<SubscriptionEvent> computeSubscriptionBaseEvents(final List<Entitlement> entitlements, final DateTimeZone accountTimeZone) {
+ private LinkedList<SubscriptionEvent> computeSubscriptionBaseEvents(final Collection<Entitlement> entitlements, final DateTimeZone accountTimeZone) {
final LinkedList<SubscriptionEvent> result = new LinkedList<SubscriptionEvent>();
for (final Entitlement cur : entitlements) {
final SubscriptionBase base = ((DefaultEntitlement) cur).getSubscriptionBase();
@@ -404,7 +412,6 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
result.add(index, event);
}
-
private SubscriptionEvent toSubscriptionEvent(final SubscriptionEvent prev, final SubscriptionEvent next, final UUID entitlementId, final BlockingState in, final SubscriptionEventType eventType, final DateTimeZone accountTimeZone) {
return new DefaultSubscriptionEvent(in.getId(),
entitlementId,
@@ -430,7 +437,6 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
accountTimeZone);
}
-
private SubscriptionEvent toSubscriptionEvent(final SubscriptionBaseTransition in, final SubscriptionEventType eventType, final DateTimeZone accountTimeZone) {
return new DefaultSubscriptionEvent(in.getId(),
in.getSubscriptionId(),
@@ -526,415 +532,413 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
return events;
}
-//
-// Internal class to keep the state associated with each subscription
-//
-private final static class TargetState {
-
- private boolean isEntitlementStarted;
- private boolean isEntitlementStopped;
- private boolean isBillingStarted;
- private boolean isBillingStopped;
- private Map<String, BlockingState> perServiceBlockingState;
-
- public TargetState() {
- this.isEntitlementStarted = false;
- this.isEntitlementStopped = false;
- this.isBillingStarted = false;
- this.isBillingStopped = false;
- this.perServiceBlockingState = new HashMap<String, BlockingState>();
- }
+ //
+ // Internal class to keep the state associated with each subscription
+ //
+ private final static class TargetState {
- public void setEntitlementStarted() {
- isEntitlementStarted = true;
- }
+ private boolean isEntitlementStarted;
+ private boolean isEntitlementStopped;
+ private boolean isBillingStarted;
+ private boolean isBillingStopped;
+ private Map<String, BlockingState> perServiceBlockingState;
- public void setEntitlementStopped() {
- isEntitlementStopped = true;
- }
+ public TargetState() {
+ this.isEntitlementStarted = false;
+ this.isEntitlementStopped = false;
+ this.isBillingStarted = false;
+ this.isBillingStopped = false;
+ this.perServiceBlockingState = new HashMap<String, BlockingState>();
+ }
- public void setBillingStarted() {
- isBillingStarted = true;
- }
+ public void setEntitlementStarted() {
+ isEntitlementStarted = true;
+ }
- public void setBillingStopped() {
- isBillingStopped = true;
- }
+ public void setEntitlementStopped() {
+ isEntitlementStopped = true;
+ }
- public void addEntitlementEvent(final SubscriptionEvent e) {
- final BlockingState converted = new DefaultBlockingState(e.getEntitlementId(), BlockingStateType.SUBSCRIPTION,
- e.getServiceStateName(), e.getServiceName(), false, e.isBlockedEntitlement(), e.isBlockedBilling(),
- ((DefaultSubscriptionEvent) e).getEffectiveDateTime());
- perServiceBlockingState.put(converted.getService(), converted);
+ public void setBillingStarted() {
+ isBillingStarted = true;
+ }
- }
+ public void setBillingStopped() {
+ isBillingStopped = true;
+ }
+
+ public void addEntitlementEvent(final SubscriptionEvent e) {
+ final BlockingState converted = new DefaultBlockingState(e.getEntitlementId(), BlockingStateType.SUBSCRIPTION,
+ e.getServiceStateName(), e.getServiceName(), false, e.isBlockedEntitlement(), e.isBlockedBilling(),
+ ((DefaultSubscriptionEvent) e).getEffectiveDateTime());
+ perServiceBlockingState.put(converted.getService(), converted);
- //
- // From the current state of that subscription, compute the effect of the new state based on the incoming blockingState event
- //
- private List<SubscriptionEventType> addStateAndReturnEventTypes(final BlockingState bs) {
-
- // Turn off isBlockedEntitlement and isBlockedBilling if there was not start event
- final BlockingState fixedBlockingState = new DefaultBlockingState(bs.getBlockedId(),
- bs.getType(),
- bs.getStateName(),
- bs.getService(),
- bs.isBlockChange(),
- (bs.isBlockEntitlement() && isEntitlementStarted && !isEntitlementStopped),
- (bs.isBlockBilling() && isBillingStarted && !isBillingStopped),
- bs.getEffectiveDate());
-
- final List<SubscriptionEventType> result = new ArrayList<SubscriptionEventType>(4);
- if (fixedBlockingState.getStateName().equals(DefaultEntitlementApi.ENT_STATE_CANCELLED)) {
- isEntitlementStopped = true;
- result.add(SubscriptionEventType.STOP_ENTITLEMENT);
- return result;
}
//
- // We look at the effect of the incoming event for the specific service, and then recompute the state after so we can compare if anything has changed
- // across all services
+ // From the current state of that subscription, compute the effect of the new state based on the incoming blockingState event
//
- final BlockingAggregator stateBefore = getState();
- perServiceBlockingState.put(fixedBlockingState.getService(), fixedBlockingState);
- final BlockingAggregator stateAfter = getState();
+ private List<SubscriptionEventType> addStateAndReturnEventTypes(final BlockingState bs) {
+
+ // Turn off isBlockedEntitlement and isBlockedBilling if there was not start event
+ final BlockingState fixedBlockingState = new DefaultBlockingState(bs.getBlockedId(),
+ bs.getType(),
+ bs.getStateName(),
+ bs.getService(),
+ bs.isBlockChange(),
+ (bs.isBlockEntitlement() && isEntitlementStarted && !isEntitlementStopped),
+ (bs.isBlockBilling() && isBillingStarted && !isBillingStopped),
+ bs.getEffectiveDate());
+
+ final List<SubscriptionEventType> result = new ArrayList<SubscriptionEventType>(4);
+ if (fixedBlockingState.getStateName().equals(DefaultEntitlementApi.ENT_STATE_CANCELLED)) {
+ isEntitlementStopped = true;
+ result.add(SubscriptionEventType.STOP_ENTITLEMENT);
+ return result;
+ }
- final boolean shouldResumeEntitlement = isEntitlementStarted && !isEntitlementStopped && stateBefore.isBlockEntitlement() && !stateAfter.isBlockEntitlement();
- if (shouldResumeEntitlement) {
- result.add(SubscriptionEventType.RESUME_ENTITLEMENT);
- }
- final boolean shouldResumeBilling = isBillingStarted && !isBillingStopped && stateBefore.isBlockBilling() && !stateAfter.isBlockBilling();
- if (shouldResumeBilling) {
- result.add(SubscriptionEventType.RESUME_BILLING);
- }
+ //
+ // We look at the effect of the incoming event for the specific service, and then recompute the state after so we can compare if anything has changed
+ // across all services
+ //
+ final BlockingAggregator stateBefore = getState();
+ perServiceBlockingState.put(fixedBlockingState.getService(), fixedBlockingState);
+ final BlockingAggregator stateAfter = getState();
- final boolean shouldBlockEntitlement = isEntitlementStarted && !isEntitlementStopped && !stateBefore.isBlockEntitlement() && stateAfter.isBlockEntitlement();
- if (shouldBlockEntitlement) {
- result.add(SubscriptionEventType.PAUSE_ENTITLEMENT);
- }
- final boolean shouldBlockBilling = isBillingStarted && !isBillingStopped && !stateBefore.isBlockBilling() && stateAfter.isBlockBilling();
- if (shouldBlockBilling) {
- result.add(SubscriptionEventType.PAUSE_BILLING);
- }
+ final boolean shouldResumeEntitlement = isEntitlementStarted && !isEntitlementStopped && stateBefore.isBlockEntitlement() && !stateAfter.isBlockEntitlement();
+ if (shouldResumeEntitlement) {
+ result.add(SubscriptionEventType.RESUME_ENTITLEMENT);
+ }
+ final boolean shouldResumeBilling = isBillingStarted && !isBillingStopped && stateBefore.isBlockBilling() && !stateAfter.isBlockBilling();
+ if (shouldResumeBilling) {
+ result.add(SubscriptionEventType.RESUME_BILLING);
+ }
+
+ final boolean shouldBlockEntitlement = isEntitlementStarted && !isEntitlementStopped && !stateBefore.isBlockEntitlement() && stateAfter.isBlockEntitlement();
+ if (shouldBlockEntitlement) {
+ result.add(SubscriptionEventType.PAUSE_ENTITLEMENT);
+ }
+ final boolean shouldBlockBilling = isBillingStarted && !isBillingStopped && !stateBefore.isBlockBilling() && stateAfter.isBlockBilling();
+ if (shouldBlockBilling) {
+ result.add(SubscriptionEventType.PAUSE_BILLING);
+ }
- if (!shouldResumeEntitlement && !shouldBlockEntitlement && !shouldBlockEntitlement && !shouldBlockBilling && !fixedBlockingState.getService().equals(DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME)) {
- result.add(SubscriptionEventType.SERVICE_STATE_CHANGE);
+ if (!shouldResumeEntitlement && !shouldBlockEntitlement && !shouldBlockEntitlement && !shouldBlockBilling && !fixedBlockingState.getService().equals(DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME)) {
+ result.add(SubscriptionEventType.SERVICE_STATE_CHANGE);
+ }
+ return result;
}
- return result;
- }
- private BlockingAggregator getState() {
- final DefaultBlockingAggregator aggrBefore = new DefaultBlockingAggregator();
- for (BlockingState cur : perServiceBlockingState.values()) {
- aggrBefore.or(cur);
+ private BlockingAggregator getState() {
+ final DefaultBlockingAggregator aggrBefore = new DefaultBlockingAggregator();
+ for (BlockingState cur : perServiceBlockingState.values()) {
+ aggrBefore.or(cur);
+ }
+ return aggrBefore;
+ }
+ }
+
+ protected static final class DefaultSubscriptionEvent implements SubscriptionEvent {
+
+ private final UUID id;
+ private final UUID entitlementId;
+ private final DateTime effectiveDate;
+ private final DateTime requestedDate;
+ private final SubscriptionEventType eventType;
+ private final boolean isBlockingEntitlement;
+ private final boolean isBlockingBilling;
+ private final String serviceName;
+ private final String serviceStateName;
+ private final Product prevProduct;
+ private final Plan prevPlan;
+ private final PlanPhase prevPlanPhase;
+ private final PriceList prevPriceList;
+ private final BillingPeriod prevBillingPeriod;
+ private final Product nextProduct;
+ private final Plan nextPlan;
+ private final PlanPhase nextPlanPhase;
+ private final PriceList nextPriceList;
+ private final BillingPeriod nextBillingPeriod;
+ private final DateTime createdDate;
+ private final DateTimeZone accountTimeZone;
+
+ public DefaultSubscriptionEvent(final UUID id,
+ final UUID entitlementId,
+ final DateTime effectiveDate,
+ final DateTime requestedDate,
+ final SubscriptionEventType eventType,
+ final boolean blockingEntitlement,
+ final boolean blockingBilling,
+ final String serviceName,
+ final String serviceStateName,
+ final Product prevProduct,
+ final Plan prevPlan,
+ final PlanPhase prevPlanPhase,
+ final PriceList prevPriceList,
+ final BillingPeriod prevBillingPeriod,
+ final Product nextProduct,
+ final Plan nextPlan,
+ final PlanPhase nextPlanPhase,
+ final PriceList nextPriceList,
+ final BillingPeriod nextBillingPeriod,
+ final DateTime createDate,
+ final DateTimeZone accountTimeZone) {
+ this.id = id;
+ this.entitlementId = entitlementId;
+ this.effectiveDate = effectiveDate;
+ this.requestedDate = requestedDate;
+ this.eventType = eventType;
+ this.isBlockingEntitlement = blockingEntitlement;
+ this.isBlockingBilling = blockingBilling;
+ this.serviceName = serviceName;
+ this.serviceStateName = serviceStateName;
+ this.prevProduct = prevProduct;
+ this.prevPlan = prevPlan;
+ this.prevPlanPhase = prevPlanPhase;
+ this.prevPriceList = prevPriceList;
+ this.prevBillingPeriod = prevBillingPeriod;
+ this.nextProduct = nextProduct;
+ this.nextPlan = nextPlan;
+ this.nextPlanPhase = nextPlanPhase;
+ this.nextPriceList = nextPriceList;
+ this.nextBillingPeriod = nextBillingPeriod;
+ this.createdDate = createDate;
+ this.accountTimeZone = accountTimeZone;
+ }
+
+ private DefaultSubscriptionEvent(DefaultSubscriptionEvent copy, SubscriptionEventType newEventType) {
+ this(copy.getId(),
+ copy.getEntitlementId(),
+ copy.getEffectiveDateTime(),
+ copy.getRequestedDateTime(),
+ newEventType,
+ copy.isBlockedEntitlement(),
+ copy.isBlockedBilling(),
+ copy.getServiceName(),
+ copy.getServiceStateName(),
+ copy.getPrevProduct(),
+ copy.getPrevPlan(),
+ copy.getPrevPhase(),
+ copy.getPrevPriceList(),
+ copy.getPrevBillingPeriod(),
+ copy.getNextProduct(),
+ copy.getNextPlan(),
+ copy.getNextPhase(),
+ copy.getNextPriceList(),
+ copy.getNextBillingPeriod(),
+ copy.getCreatedDate(),
+ copy.getAccountTimeZone());
}
- return aggrBefore;
- }
-}
+ public DateTimeZone getAccountTimeZone() {
+ return accountTimeZone;
+ }
-protected static final class DefaultSubscriptionEvent implements SubscriptionEvent {
-
- private final UUID id;
- private final UUID entitlementId;
- private final DateTime effectiveDate;
- private final DateTime requestedDate;
- private final SubscriptionEventType eventType;
- private final boolean isBlockingEntitlement;
- private final boolean isBlockingBilling;
- private final String serviceName;
- private final String serviceStateName;
- private final Product prevProduct;
- private final Plan prevPlan;
- private final PlanPhase prevPlanPhase;
- private final PriceList prevPriceList;
- private final BillingPeriod prevBillingPeriod;
- private final Product nextProduct;
- private final Plan nextPlan;
- private final PlanPhase nextPlanPhase;
- private final PriceList nextPriceList;
- private final BillingPeriod nextBillingPeriod;
- private final DateTime createdDate;
- private final DateTimeZone accountTimeZone;
-
-
- public DefaultSubscriptionEvent(final UUID id,
- final UUID entitlementId,
- final DateTime effectiveDate,
- final DateTime requestedDate,
- final SubscriptionEventType eventType,
- final boolean blockingEntitlement,
- final boolean blockingBilling,
- final String serviceName,
- final String serviceStateName,
- final Product prevProduct,
- final Plan prevPlan,
- final PlanPhase prevPlanPhase,
- final PriceList prevPriceList,
- final BillingPeriod prevBillingPeriod,
- final Product nextProduct,
- final Plan nextPlan,
- final PlanPhase nextPlanPhase,
- final PriceList nextPriceList,
- final BillingPeriod nextBillingPeriod,
- final DateTime createDate,
- final DateTimeZone accountTimeZone) {
- this.id = id;
- this.entitlementId = entitlementId;
- this.effectiveDate = effectiveDate;
- this.requestedDate = requestedDate;
- this.eventType = eventType;
- this.isBlockingEntitlement = blockingEntitlement;
- this.isBlockingBilling = blockingBilling;
- this.serviceName = serviceName;
- this.serviceStateName = serviceStateName;
- this.prevProduct = prevProduct;
- this.prevPlan = prevPlan;
- this.prevPlanPhase = prevPlanPhase;
- this.prevPriceList = prevPriceList;
- this.prevBillingPeriod = prevBillingPeriod;
- this.nextProduct = nextProduct;
- this.nextPlan = nextPlan;
- this.nextPlanPhase = nextPlanPhase;
- this.nextPriceList = nextPriceList;
- this.nextBillingPeriod = nextBillingPeriod;
- this.createdDate = createDate;
- this.accountTimeZone = accountTimeZone;
- }
+ public DateTime getEffectiveDateTime() {
+ return effectiveDate;
+ }
- private DefaultSubscriptionEvent(DefaultSubscriptionEvent copy, SubscriptionEventType newEventType) {
- this(copy.getId(),
- copy.getEntitlementId(),
- copy.getEffectiveDateTime(),
- copy.getRequestedDateTime(),
- newEventType,
- copy.isBlockedEntitlement(),
- copy.isBlockedBilling(),
- copy.getServiceName(),
- copy.getServiceStateName(),
- copy.getPrevProduct(),
- copy.getPrevPlan(),
- copy.getPrevPhase(),
- copy.getPrevPriceList(),
- copy.getPrevBillingPeriod(),
- copy.getNextProduct(),
- copy.getNextPlan(),
- copy.getNextPhase(),
- copy.getNextPriceList(),
- copy.getNextBillingPeriod(),
- copy.getCreatedDate(),
- copy.getAccountTimeZone());
- }
+ public DateTime getRequestedDateTime() {
+ return requestedDate;
+ }
- public DateTimeZone getAccountTimeZone() {
- return accountTimeZone;
- }
+ @Override
+ public UUID getId() {
+ return id;
+ }
- public DateTime getEffectiveDateTime() {
- return effectiveDate;
- }
+ @Override
+ public UUID getEntitlementId() {
+ return entitlementId;
+ }
- public DateTime getRequestedDateTime() {
- return requestedDate;
- }
+ @Override
+ public LocalDate getEffectiveDate() {
+ return effectiveDate != null ? new LocalDate(effectiveDate, accountTimeZone) : null;
+ }
- @Override
- public UUID getId() {
- return id;
- }
+ @Override
+ public LocalDate getRequestedDate() {
+ return requestedDate != null ? new LocalDate(requestedDate, accountTimeZone) : null;
+ }
- @Override
- public UUID getEntitlementId() {
- return entitlementId;
- }
+ @Override
+ public SubscriptionEventType getSubscriptionEventType() {
+ return eventType;
+ }
- @Override
- public LocalDate getEffectiveDate() {
- return effectiveDate != null ? new LocalDate(effectiveDate, accountTimeZone) : null;
- }
+ @Override
+ public boolean isBlockedBilling() {
+ return isBlockingBilling;
+ }
- @Override
- public LocalDate getRequestedDate() {
- return requestedDate != null ? new LocalDate(requestedDate, accountTimeZone) : null;
- }
+ @Override
+ public boolean isBlockedEntitlement() {
+ return isBlockingEntitlement;
+ }
- @Override
- public SubscriptionEventType getSubscriptionEventType() {
- return eventType;
- }
+ @Override
+ public String getServiceName() {
+ return serviceName;
+ }
- @Override
- public boolean isBlockedBilling() {
- return isBlockingBilling;
- }
+ @Override
+ public String getServiceStateName() {
+ return serviceStateName;
+ }
- @Override
- public boolean isBlockedEntitlement() {
- return isBlockingEntitlement;
- }
+ @Override
+ public Product getPrevProduct() {
+ return prevProduct;
+ }
- @Override
- public String getServiceName() {
- return serviceName;
- }
+ @Override
+ public Plan getPrevPlan() {
+ return prevPlan;
+ }
- @Override
- public String getServiceStateName() {
- return serviceStateName;
- }
+ @Override
+ public PlanPhase getPrevPhase() {
+ return prevPlanPhase;
+ }
- @Override
- public Product getPrevProduct() {
- return prevProduct;
- }
+ @Override
+ public PriceList getPrevPriceList() {
+ return prevPriceList;
+ }
- @Override
- public Plan getPrevPlan() {
- return prevPlan;
- }
+ @Override
+ public BillingPeriod getPrevBillingPeriod() {
+ return prevBillingPeriod;
+ }
- @Override
- public PlanPhase getPrevPhase() {
- return prevPlanPhase;
- }
+ @Override
+ public Product getNextProduct() {
+ return nextProduct;
+ }
- @Override
- public PriceList getPrevPriceList() {
- return prevPriceList;
- }
+ @Override
+ public Plan getNextPlan() {
+ return nextPlan;
+ }
- @Override
- public BillingPeriod getPrevBillingPeriod() {
- return prevBillingPeriod;
- }
+ @Override
+ public PlanPhase getNextPhase() {
+ return nextPlanPhase;
+ }
- @Override
- public Product getNextProduct() {
- return nextProduct;
- }
+ @Override
+ public PriceList getNextPriceList() {
+ return nextPriceList;
+ }
- @Override
- public Plan getNextPlan() {
- return nextPlan;
- }
+ @Override
+ public BillingPeriod getNextBillingPeriod() {
+ return nextBillingPeriod;
+ }
- @Override
- public PlanPhase getNextPhase() {
- return nextPlanPhase;
- }
+ public DateTime getCreatedDate() {
+ return createdDate;
+ }
- @Override
- public PriceList getNextPriceList() {
- return nextPriceList;
- }
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
- @Override
- public BillingPeriod getNextBillingPeriod() {
- return nextBillingPeriod;
- }
+ final DefaultSubscriptionEvent that = (DefaultSubscriptionEvent) o;
- public DateTime getCreatedDate() {
- return createdDate;
- }
+ if (isBlockingBilling != that.isBlockingBilling) {
+ return false;
+ }
+ if (isBlockingEntitlement != that.isBlockingEntitlement) {
+ return false;
+ }
+ if (createdDate != null ? !createdDate.equals(that.createdDate) : that.createdDate != null) {
+ return false;
+ }
+ if (effectiveDate != null ? !effectiveDate.equals(that.effectiveDate) : that.effectiveDate != null) {
+ return false;
+ }
+ if (entitlementId != null ? !entitlementId.equals(that.entitlementId) : that.entitlementId != null) {
+ return false;
+ }
+ if (eventType != that.eventType) {
+ return false;
+ }
+ if (id != null ? !id.equals(that.id) : that.id != null) {
+ return false;
+ }
+ if (nextBillingPeriod != that.nextBillingPeriod) {
+ return false;
+ }
+ if (nextPlan != null ? !nextPlan.equals(that.nextPlan) : that.nextPlan != null) {
+ return false;
+ }
+ if (nextPlanPhase != null ? !nextPlanPhase.equals(that.nextPlanPhase) : that.nextPlanPhase != null) {
+ return false;
+ }
+ if (nextPriceList != null ? !nextPriceList.equals(that.nextPriceList) : that.nextPriceList != null) {
+ return false;
+ }
+ if (nextProduct != null ? !nextProduct.equals(that.nextProduct) : that.nextProduct != null) {
+ return false;
+ }
+ if (prevBillingPeriod != that.prevBillingPeriod) {
+ return false;
+ }
+ if (prevPlan != null ? !prevPlan.equals(that.prevPlan) : that.prevPlan != null) {
+ return false;
+ }
+ if (prevPlanPhase != null ? !prevPlanPhase.equals(that.prevPlanPhase) : that.prevPlanPhase != null) {
+ return false;
+ }
+ if (prevPriceList != null ? !prevPriceList.equals(that.prevPriceList) : that.prevPriceList != null) {
+ return false;
+ }
+ if (prevProduct != null ? !prevProduct.equals(that.prevProduct) : that.prevProduct != null) {
+ return false;
+ }
+ if (requestedDate != null ? !requestedDate.equals(that.requestedDate) : that.requestedDate != null) {
+ return false;
+ }
+ if (serviceName != null ? !serviceName.equals(that.serviceName) : that.serviceName != null) {
+ return false;
+ }
+ if (serviceStateName != null ? !serviceStateName.equals(that.serviceStateName) : that.serviceStateName != null) {
+ return false;
+ }
- @Override
- public boolean equals(final Object o) {
- if (this == o) {
return true;
}
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
-
- final DefaultSubscriptionEvent that = (DefaultSubscriptionEvent) o;
- if (isBlockingBilling != that.isBlockingBilling) {
- return false;
- }
- if (isBlockingEntitlement != that.isBlockingEntitlement) {
- return false;
- }
- if (createdDate != null ? !createdDate.equals(that.createdDate) : that.createdDate != null) {
- return false;
- }
- if (effectiveDate != null ? !effectiveDate.equals(that.effectiveDate) : that.effectiveDate != null) {
- return false;
- }
- if (entitlementId != null ? !entitlementId.equals(that.entitlementId) : that.entitlementId != null) {
- return false;
- }
- if (eventType != that.eventType) {
- return false;
- }
- if (id != null ? !id.equals(that.id) : that.id != null) {
- return false;
- }
- if (nextBillingPeriod != that.nextBillingPeriod) {
- return false;
- }
- if (nextPlan != null ? !nextPlan.equals(that.nextPlan) : that.nextPlan != null) {
- return false;
- }
- if (nextPlanPhase != null ? !nextPlanPhase.equals(that.nextPlanPhase) : that.nextPlanPhase != null) {
- return false;
- }
- if (nextPriceList != null ? !nextPriceList.equals(that.nextPriceList) : that.nextPriceList != null) {
- return false;
- }
- if (nextProduct != null ? !nextProduct.equals(that.nextProduct) : that.nextProduct != null) {
- return false;
- }
- if (prevBillingPeriod != that.prevBillingPeriod) {
- return false;
- }
- if (prevPlan != null ? !prevPlan.equals(that.prevPlan) : that.prevPlan != null) {
- return false;
- }
- if (prevPlanPhase != null ? !prevPlanPhase.equals(that.prevPlanPhase) : that.prevPlanPhase != null) {
- return false;
- }
- if (prevPriceList != null ? !prevPriceList.equals(that.prevPriceList) : that.prevPriceList != null) {
- return false;
- }
- if (prevProduct != null ? !prevProduct.equals(that.prevProduct) : that.prevProduct != null) {
- return false;
- }
- if (requestedDate != null ? !requestedDate.equals(that.requestedDate) : that.requestedDate != null) {
- return false;
- }
- if (serviceName != null ? !serviceName.equals(that.serviceName) : that.serviceName != null) {
- return false;
- }
- if (serviceStateName != null ? !serviceStateName.equals(that.serviceStateName) : that.serviceStateName != null) {
- return false;
+ @Override
+ public int hashCode() {
+ int result = id != null ? id.hashCode() : 0;
+ result = 31 * result + (entitlementId != null ? entitlementId.hashCode() : 0);
+ result = 31 * result + (effectiveDate != null ? effectiveDate.hashCode() : 0);
+ result = 31 * result + (requestedDate != null ? requestedDate.hashCode() : 0);
+ result = 31 * result + (eventType != null ? eventType.hashCode() : 0);
+ result = 31 * result + (isBlockingEntitlement ? 1 : 0);
+ result = 31 * result + (isBlockingBilling ? 1 : 0);
+ result = 31 * result + (serviceName != null ? serviceName.hashCode() : 0);
+ result = 31 * result + (serviceStateName != null ? serviceStateName.hashCode() : 0);
+ result = 31 * result + (prevProduct != null ? prevProduct.hashCode() : 0);
+ result = 31 * result + (prevPlan != null ? prevPlan.hashCode() : 0);
+ result = 31 * result + (prevPlanPhase != null ? prevPlanPhase.hashCode() : 0);
+ result = 31 * result + (prevPriceList != null ? prevPriceList.hashCode() : 0);
+ result = 31 * result + (prevBillingPeriod != null ? prevBillingPeriod.hashCode() : 0);
+ result = 31 * result + (nextProduct != null ? nextProduct.hashCode() : 0);
+ result = 31 * result + (nextPlan != null ? nextPlan.hashCode() : 0);
+ result = 31 * result + (nextPlanPhase != null ? nextPlanPhase.hashCode() : 0);
+ result = 31 * result + (nextPriceList != null ? nextPriceList.hashCode() : 0);
+ result = 31 * result + (nextBillingPeriod != null ? nextBillingPeriod.hashCode() : 0);
+ result = 31 * result + (createdDate != null ? createdDate.hashCode() : 0);
+ return result;
}
-
- return true;
}
-
- @Override
- public int hashCode() {
- int result = id != null ? id.hashCode() : 0;
- result = 31 * result + (entitlementId != null ? entitlementId.hashCode() : 0);
- result = 31 * result + (effectiveDate != null ? effectiveDate.hashCode() : 0);
- result = 31 * result + (requestedDate != null ? requestedDate.hashCode() : 0);
- result = 31 * result + (eventType != null ? eventType.hashCode() : 0);
- result = 31 * result + (isBlockingEntitlement ? 1 : 0);
- result = 31 * result + (isBlockingBilling ? 1 : 0);
- result = 31 * result + (serviceName != null ? serviceName.hashCode() : 0);
- result = 31 * result + (serviceStateName != null ? serviceStateName.hashCode() : 0);
- result = 31 * result + (prevProduct != null ? prevProduct.hashCode() : 0);
- result = 31 * result + (prevPlan != null ? prevPlan.hashCode() : 0);
- result = 31 * result + (prevPlanPhase != null ? prevPlanPhase.hashCode() : 0);
- result = 31 * result + (prevPriceList != null ? prevPriceList.hashCode() : 0);
- result = 31 * result + (prevBillingPeriod != null ? prevBillingPeriod.hashCode() : 0);
- result = 31 * result + (nextProduct != null ? nextProduct.hashCode() : 0);
- result = 31 * result + (nextPlan != null ? nextPlan.hashCode() : 0);
- result = 31 * result + (nextPlanPhase != null ? nextPlanPhase.hashCode() : 0);
- result = 31 * result + (nextPriceList != null ? nextPriceList.hashCode() : 0);
- result = 31 * result + (nextBillingPeriod != null ? nextBillingPeriod.hashCode() : 0);
- result = 31 * result + (createdDate != null ? createdDate.hashCode() : 0);
- return result;
- }
-}
}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultAccountEntitlements.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultAccountEntitlements.java
new file mode 100644
index 0000000..72898be
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultAccountEntitlements.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2010-2013 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.svcs;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.UUID;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.entitlement.AccountEntitlements;
+import com.ning.billing.entitlement.AccountEventsStreams;
+import com.ning.billing.entitlement.api.Entitlement;
+import com.ning.billing.subscription.api.user.SubscriptionBaseBundle;
+
+public class DefaultAccountEntitlements implements AccountEntitlements {
+
+ private final AccountEventsStreams accountEventsStreams;
+ private final Map<UUID, Collection<Entitlement>> entitlements;
+
+ public DefaultAccountEntitlements(final AccountEventsStreams accountEventsStreams, final Map<UUID, Collection<Entitlement>> entitlements) {
+ this.accountEventsStreams = accountEventsStreams;
+ this.entitlements = entitlements;
+ }
+
+ @Override
+ public Account getAccount() {
+ return accountEventsStreams.getAccount();
+ }
+
+ @Override
+ public Map<UUID, SubscriptionBaseBundle> getBundles() {
+ return accountEventsStreams.getBundles();
+ }
+
+ @Override
+ public Map<UUID, Collection<Entitlement>> getEntitlements() {
+ return entitlements;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("DefaultAccountEntitlements{");
+ sb.append("accountEventsStreams=").append(accountEventsStreams);
+ sb.append(", entitlements=").append(entitlements);
+ sb.append('}');
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ final DefaultAccountEntitlements that = (DefaultAccountEntitlements) o;
+
+ if (accountEventsStreams != null ? !accountEventsStreams.equals(that.accountEventsStreams) : that.accountEventsStreams != null) {
+ return false;
+ }
+ if (entitlements != null ? !entitlements.equals(that.entitlements) : that.entitlements != null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = accountEventsStreams != null ? accountEventsStreams.hashCode() : 0;
+ result = 31 * result + (entitlements != null ? entitlements.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultAccountEventsStreams.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultAccountEventsStreams.java
new file mode 100644
index 0000000..d05df80
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultAccountEventsStreams.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2010-2013 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.svcs;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.entitlement.AccountEventsStreams;
+import com.ning.billing.entitlement.EventsStream;
+import com.ning.billing.subscription.api.user.SubscriptionBaseBundle;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+public class DefaultAccountEventsStreams implements AccountEventsStreams {
+
+ private final Account account;
+ private final Map<UUID, Collection<EventsStream>> eventsStreams;
+ private final Map<UUID, SubscriptionBaseBundle> bundles = new HashMap<UUID, SubscriptionBaseBundle>();
+
+ public DefaultAccountEventsStreams(final Account account,
+ final Iterable<SubscriptionBaseBundle> bundles,
+ final Map<UUID, Collection<EventsStream>> eventsStreams) {
+ this.account = account;
+ this.eventsStreams = eventsStreams;
+ for (final SubscriptionBaseBundle baseBundle : bundles) {
+ this.bundles.put(baseBundle.getId(), baseBundle);
+ }
+ }
+
+ public DefaultAccountEventsStreams(final Account account) {
+ this(account, ImmutableList.<SubscriptionBaseBundle>of(), ImmutableMap.<UUID, Collection<EventsStream>>of());
+ }
+
+ @Override
+ public Account getAccount() {
+ return account;
+ }
+
+ @Override
+ public Map<UUID, SubscriptionBaseBundle> getBundles() {
+ return bundles;
+ }
+
+ @Override
+ public Map<UUID, Collection<EventsStream>> getEventsStreams() {
+ return eventsStreams;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("DefaultAccountEventsStreams{");
+ sb.append("account=").append(account);
+ sb.append(", eventsStreams=").append(eventsStreams);
+ sb.append(", bundles=").append(bundles);
+ sb.append('}');
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ final DefaultAccountEventsStreams that = (DefaultAccountEventsStreams) o;
+
+ if (account != null ? !account.equals(that.account) : that.account != null) {
+ return false;
+ }
+ if (bundles != null ? !bundles.equals(that.bundles) : that.bundles != null) {
+ return false;
+ }
+ if (eventsStreams != null ? !eventsStreams.equals(that.eventsStreams) : that.eventsStreams != null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = account != null ? account.hashCode() : 0;
+ result = 31 * result + (eventsStreams != null ? eventsStreams.hashCode() : 0);
+ result = 31 * result + (bundles != null ? bundles.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultEntitlementInternalApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultEntitlementInternalApi.java
new file mode 100644
index 0000000..f941e9b
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultEntitlementInternalApi.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2010-2013 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.svcs;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.UUID;
+
+import javax.inject.Inject;
+
+import com.ning.billing.account.api.AccountInternalApi;
+import com.ning.billing.callcontext.InternalTenantContext;
+import com.ning.billing.clock.Clock;
+import com.ning.billing.entitlement.AccountEntitlements;
+import com.ning.billing.entitlement.AccountEventsStreams;
+import com.ning.billing.entitlement.EntitlementInternalApi;
+import com.ning.billing.entitlement.EventsStream;
+import com.ning.billing.entitlement.api.DefaultEntitlement;
+import com.ning.billing.entitlement.api.Entitlement;
+import com.ning.billing.entitlement.api.EntitlementApi;
+import com.ning.billing.entitlement.api.EntitlementApiException;
+import com.ning.billing.entitlement.api.EntitlementDateHelper;
+import com.ning.billing.entitlement.block.BlockingChecker;
+import com.ning.billing.entitlement.dao.BlockingStateDao;
+import com.ning.billing.entitlement.engine.core.EntitlementUtils;
+import com.ning.billing.entitlement.engine.core.EventsStreamBuilder;
+import com.ning.billing.notificationq.api.NotificationQueueService;
+import com.ning.billing.subscription.api.SubscriptionBaseInternalApi;
+import com.ning.billing.util.callcontext.InternalCallContextFactory;
+import com.ning.billing.util.callcontext.TenantContext;
+
+public class DefaultEntitlementInternalApi implements EntitlementInternalApi {
+
+ private final EntitlementApi entitlementApi;
+ private final SubscriptionBaseInternalApi subscriptionInternalApi;
+ private final Clock clock;
+ private final InternalCallContextFactory internalCallContextFactory;
+ private final BlockingChecker checker;
+ private final BlockingStateDao blockingStateDao;
+ private final EntitlementDateHelper dateHelper;
+ private final EventsStreamBuilder eventsStreamBuilder;
+ private final EntitlementUtils entitlementUtils;
+ private final NotificationQueueService notificationQueueService;
+
+ @Inject
+ public DefaultEntitlementInternalApi(final EntitlementApi entitlementApi, final InternalCallContextFactory internalCallContextFactory,
+ final SubscriptionBaseInternalApi subscriptionInternalApi,
+ final AccountInternalApi accountApi, final BlockingStateDao blockingStateDao, final Clock clock,
+ final BlockingChecker checker, final NotificationQueueService notificationQueueService,
+ final EventsStreamBuilder eventsStreamBuilder, final EntitlementUtils entitlementUtils) {
+ this.entitlementApi = entitlementApi;
+ this.internalCallContextFactory = internalCallContextFactory;
+ this.subscriptionInternalApi = subscriptionInternalApi;
+ this.clock = clock;
+ this.checker = checker;
+ this.blockingStateDao = blockingStateDao;
+ this.notificationQueueService = notificationQueueService;
+ this.eventsStreamBuilder = eventsStreamBuilder;
+ this.entitlementUtils = entitlementUtils;
+ this.dateHelper = new EntitlementDateHelper(accountApi, clock);
+ }
+
+ @Override
+ public AccountEntitlements getAllEntitlementsForAccountId(final UUID accountId, final TenantContext tenantContext) throws EntitlementApiException {
+ final InternalTenantContext context = internalCallContextFactory.createInternalTenantContext(accountId, tenantContext);
+
+ final AccountEventsStreams accountEventsStreams = eventsStreamBuilder.buildForAccount(context);
+
+ final Map<UUID, Collection<Entitlement>> entitlementsPerBundle = new HashMap<UUID, Collection<Entitlement>>();
+ for (final UUID bundleId : accountEventsStreams.getEventsStreams().keySet()) {
+ if (entitlementsPerBundle.get(bundleId) == null) {
+ entitlementsPerBundle.put(bundleId, new LinkedList<Entitlement>());
+ }
+
+ for (final EventsStream eventsStream : accountEventsStreams.getEventsStreams().get(bundleId)) {
+ final Entitlement entitlement = new DefaultEntitlement(eventsStream, eventsStreamBuilder, entitlementApi,
+ blockingStateDao, subscriptionInternalApi, checker, notificationQueueService,
+ entitlementUtils, dateHelper, clock, internalCallContextFactory);
+ entitlementsPerBundle.get(bundleId).add(entitlement);
+ }
+ }
+
+ return new DefaultAccountEntitlements(accountEventsStreams, entitlementsPerBundle);
+ }
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/dao/OptimizedProxyBlockingStateDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/dao/OptimizedProxyBlockingStateDao.java
new file mode 100644
index 0000000..604b488
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/dao/OptimizedProxyBlockingStateDao.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2010-2013 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.dao;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+import org.skife.jdbi.v2.IDBI;
+
+import com.ning.billing.callcontext.InternalTenantContext;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.clock.Clock;
+import com.ning.billing.entitlement.EventsStream;
+import com.ning.billing.entitlement.api.BlockingState;
+import com.ning.billing.entitlement.api.BlockingStateType;
+import com.ning.billing.entitlement.api.EntitlementApiException;
+import com.ning.billing.entitlement.engine.core.EventsStreamBuilder;
+import com.ning.billing.subscription.api.SubscriptionBase;
+import com.ning.billing.subscription.api.SubscriptionBaseInternalApi;
+import com.ning.billing.subscription.api.user.SubscriptionBaseBundle;
+import com.ning.billing.util.cache.CacheControllerDispatcher;
+import com.ning.billing.util.dao.NonEntityDao;
+
+import com.google.common.collect.ImmutableList;
+
+public class OptimizedProxyBlockingStateDao extends ProxyBlockingStateDao {
+
+ public OptimizedProxyBlockingStateDao(final EventsStreamBuilder eventsStreamBuilder, final SubscriptionBaseInternalApi subscriptionBaseInternalApi,
+ final IDBI dbi, final Clock clock, final CacheControllerDispatcher cacheControllerDispatcher,
+ final NonEntityDao nonEntityDao) {
+ super(eventsStreamBuilder, subscriptionBaseInternalApi, dbi, clock, cacheControllerDispatcher, nonEntityDao);
+ }
+
+ // Special signature for EventsStreamBuilder to save some DAO calls
+ public List<BlockingState> getBlockingHistoryForService(final List<BlockingState> blockingStatesOnDisk,
+ final SubscriptionBaseBundle bundle,
+ @Nullable final SubscriptionBase baseSubscription,
+ final SubscriptionBase subscription,
+ final List<SubscriptionBase> allSubscriptionsForBundle,
+ final InternalTenantContext context) throws EntitlementApiException {
+ // blockable id points to a subscription, but make sure it's an add-on
+ if (!ProductCategory.ADD_ON.equals(subscription.getCategory())) {
+ // blockable id points to a base or standalone subscription, there is nothing to do
+ return blockingStatesOnDisk;
+ }
+
+ // Find all base entitlements that we care about (for which we want to find future cancelled add-ons)
+ final Iterable<EventsStream> eventsStreams = ImmutableList.<EventsStream>of(eventsStreamBuilder.buildForEntitlement(bundle,
+ baseSubscription,
+ allSubscriptionsForBundle,
+ context));
+
+ return addBlockingStatesNotOnDisk(subscription.getId(),
+ BlockingStateType.SUBSCRIPTION,
+ new LinkedList<BlockingState>(blockingStatesOnDisk),
+ ImmutableList.<SubscriptionBase>of(baseSubscription),
+ eventsStreams);
+ }
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/dao/ProxyBlockingStateDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/dao/ProxyBlockingStateDao.java
index 13f3cd5..850dd0f 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/dao/ProxyBlockingStateDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/dao/ProxyBlockingStateDao.java
@@ -20,6 +20,7 @@ import java.util.Collection;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import java.util.UUID;
import javax.annotation.Nullable;
@@ -36,12 +37,12 @@ import com.ning.billing.callcontext.InternalTenantContext;
import com.ning.billing.catalog.api.ProductCategory;
import com.ning.billing.clock.Clock;
import com.ning.billing.entitlement.EntitlementService;
+import com.ning.billing.entitlement.EventsStream;
import com.ning.billing.entitlement.api.BlockingState;
import com.ning.billing.entitlement.api.BlockingStateType;
import com.ning.billing.entitlement.api.DefaultEntitlementApi;
import com.ning.billing.entitlement.api.Entitlement.EntitlementState;
import com.ning.billing.entitlement.api.EntitlementApiException;
-import com.ning.billing.entitlement.engine.core.EventsStream;
import com.ning.billing.entitlement.engine.core.EventsStreamBuilder;
import com.ning.billing.subscription.api.SubscriptionBase;
import com.ning.billing.subscription.api.SubscriptionBaseInternalApi;
@@ -79,10 +80,11 @@ public class ProxyBlockingStateDao implements BlockingStateDao {
}
});
- private final EventsStreamBuilder eventsStreamBuilder;
private final SubscriptionBaseInternalApi subscriptionInternalApi;
private final Clock clock;
- private final DefaultBlockingStateDao delegate;
+
+ protected final EventsStreamBuilder eventsStreamBuilder;
+ protected final DefaultBlockingStateDao delegate;
@Inject
public ProxyBlockingStateDao(final EventsStreamBuilder eventsStreamBuilder, final SubscriptionBaseInternalApi subscriptionBaseInternalApi,
@@ -182,11 +184,12 @@ public class ProxyBlockingStateDao implements BlockingStateDao {
// Find all base entitlements that we care about (for which we want to find future cancelled add-ons)
final Iterable<SubscriptionBase> baseSubscriptionsToConsider;
+ final Iterable<EventsStream> eventsStreams;
try {
if (blockingStateType == null) {
// We're coming from getBlockingAllForAccountRecordId
- final Iterable<SubscriptionBase> subscriptions = Iterables.<SubscriptionBase>concat(subscriptionInternalApi.getSubscriptionsForAccount(context).values());
- baseSubscriptionsToConsider = Iterables.<SubscriptionBase>filter(subscriptions,
+ final Map<UUID, List<SubscriptionBase>> subscriptions = subscriptionInternalApi.getSubscriptionsForAccount(context);
+ baseSubscriptionsToConsider = Iterables.<SubscriptionBase>filter(Iterables.<SubscriptionBase>concat(subscriptions.values()),
new Predicate<SubscriptionBase>() {
@Override
public boolean apply(final SubscriptionBase input) {
@@ -194,6 +197,7 @@ public class ProxyBlockingStateDao implements BlockingStateDao {
!EntitlementState.CANCELLED.equals(input.getState());
}
});
+ eventsStreams = Iterables.<EventsStream>concat(eventsStreamBuilder.buildForAccount(subscriptions, context).getEventsStreams().values());
} else if (BlockingStateType.SUBSCRIPTION.equals(blockingStateType)) {
// We're coming from getBlockingHistoryForService / getBlockingAll
final SubscriptionBase subscription = subscriptionInternalApi.getSubscriptionFromId(blockableId, context);
@@ -202,6 +206,7 @@ public class ProxyBlockingStateDao implements BlockingStateDao {
if (ProductCategory.ADD_ON.equals(subscription.getCategory())) {
final SubscriptionBase baseSubscription = subscriptionInternalApi.getBaseSubscription(subscription.getBundleId(), context);
baseSubscriptionsToConsider = ImmutableList.<SubscriptionBase>of(baseSubscription);
+ eventsStreams = ImmutableList.<EventsStream>of(eventsStreamBuilder.buildForEntitlement(baseSubscription, context));
} else {
// blockable id points to a base or standalone subscription, there is nothing to do
return blockingStatesOnDisk;
@@ -213,25 +218,22 @@ public class ProxyBlockingStateDao implements BlockingStateDao {
} catch (SubscriptionBaseApiException e) {
log.error("Error retrieving subscriptions for account record id " + context.getAccountRecordId(), e);
throw new RuntimeException(e);
- }
-
- // Retrieve the cancellation blocking state on disk, if it exists (will be used later)
- final BlockingState cancellationBlockingStateOnDisk = findEntitlementCancellationBlockingState(blockableId, blockingStatesOnDiskCopy);
-
- final List<EventsStream> eventsStreams;
- try {
- if (blockingStateType == null) {
- // We're coming from getBlockingAllForAccountRecordId
- eventsStreams = eventsStreamBuilder.buildForAccount(context);
- } else {
- // We're coming from getBlockingHistoryForService / getBlockingAll
- eventsStreams = ImmutableList.<EventsStream>of(eventsStreamBuilder.buildForEntitlement(baseSubscriptionsToConsider.iterator().next().getId(), context));
- }
} catch (EntitlementApiException e) {
log.error("Error computing blocking states for addons for account record id " + context.getAccountRecordId(), e);
throw new RuntimeException(e);
}
+ return addBlockingStatesNotOnDisk(blockableId, blockingStateType, blockingStatesOnDiskCopy, baseSubscriptionsToConsider, eventsStreams);
+ }
+
+ protected List<BlockingState> addBlockingStatesNotOnDisk(@Nullable final UUID blockableId,
+ @Nullable final BlockingStateType blockingStateType,
+ final Collection<BlockingState> blockingStatesOnDiskCopy,
+ final Iterable<SubscriptionBase> baseSubscriptionsToConsider,
+ final Iterable<EventsStream> eventsStreams) {
+ // Retrieve the cancellation blocking state on disk, if it exists (will be used later)
+ final BlockingState cancellationBlockingStateOnDisk = findEntitlementCancellationBlockingState(blockableId, blockingStatesOnDiskCopy);
+
// Compute the blocking states not on disk for all base subscriptions
final DateTime now = clock.getUTCNow();
for (final SubscriptionBase baseSubscription : baseSubscriptionsToConsider) {
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/EventsStreamBuilder.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/EventsStreamBuilder.java
index 926464e..f9f9cfb 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/EventsStreamBuilder.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/EventsStreamBuilder.java
@@ -16,16 +16,17 @@
package com.ning.billing.entitlement.engine.core;
+import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
+import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Singleton;
-import org.joda.time.DateTime;
import org.skife.jdbi.v2.IDBI;
import com.ning.billing.ObjectType;
@@ -35,14 +36,16 @@ import com.ning.billing.account.api.AccountInternalApi;
import com.ning.billing.callcontext.InternalTenantContext;
import com.ning.billing.catalog.api.ProductCategory;
import com.ning.billing.clock.Clock;
+import com.ning.billing.entitlement.AccountEventsStreams;
import com.ning.billing.entitlement.EntitlementService;
+import com.ning.billing.entitlement.EventsStream;
import com.ning.billing.entitlement.api.BlockingState;
import com.ning.billing.entitlement.api.BlockingStateType;
import com.ning.billing.entitlement.api.EntitlementApiException;
+import com.ning.billing.entitlement.api.svcs.DefaultAccountEventsStreams;
import com.ning.billing.entitlement.block.BlockingChecker;
-import com.ning.billing.entitlement.block.BlockingChecker.BlockingAggregator;
-import com.ning.billing.entitlement.dao.BlockingStateDao;
import com.ning.billing.entitlement.dao.DefaultBlockingStateDao;
+import com.ning.billing.entitlement.dao.OptimizedProxyBlockingStateDao;
import com.ning.billing.subscription.api.SubscriptionBase;
import com.ning.billing.subscription.api.SubscriptionBaseInternalApi;
import com.ning.billing.subscription.api.user.SubscriptionBaseApiException;
@@ -52,6 +55,7 @@ import com.ning.billing.util.callcontext.InternalCallContextFactory;
import com.ning.billing.util.callcontext.TenantContext;
import com.ning.billing.util.dao.NonEntityDao;
+import com.google.common.base.Objects;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
@@ -64,31 +68,29 @@ public class EventsStreamBuilder {
private final AccountInternalApi accountInternalApi;
private final SubscriptionBaseInternalApi subscriptionInternalApi;
private final BlockingChecker checker;
- private final BlockingStateDao blockingStateDao;
+ private final OptimizedProxyBlockingStateDao blockingStateDao;
private final DefaultBlockingStateDao defaultBlockingStateDao;
private final Clock clock;
- private final NonEntityDao nonEntityDao;
private final InternalCallContextFactory internalCallContextFactory;
@Inject
public EventsStreamBuilder(final AccountInternalApi accountInternalApi, final SubscriptionBaseInternalApi subscriptionInternalApi,
final BlockingChecker checker, final IDBI dbi, final Clock clock,
final CacheControllerDispatcher cacheControllerDispatcher,
- final BlockingStateDao blockingStateDao, final NonEntityDao nonEntityDao,
+ final NonEntityDao nonEntityDao,
final InternalCallContextFactory internalCallContextFactory) {
this.accountInternalApi = accountInternalApi;
this.subscriptionInternalApi = subscriptionInternalApi;
this.checker = checker;
this.clock = clock;
- this.blockingStateDao = blockingStateDao;
- this.nonEntityDao = nonEntityDao;
this.internalCallContextFactory = internalCallContextFactory;
this.defaultBlockingStateDao = new DefaultBlockingStateDao(dbi, clock, cacheControllerDispatcher, nonEntityDao);
+ this.blockingStateDao = new OptimizedProxyBlockingStateDao(this, subscriptionInternalApi, dbi, clock, cacheControllerDispatcher, nonEntityDao);
}
public EventsStream refresh(final EventsStream eventsStream, final TenantContext tenantContext) throws EntitlementApiException {
- return buildForEntitlement(eventsStream.getSubscription().getId(), tenantContext);
+ return buildForEntitlement(eventsStream.getEntitlementId(), tenantContext);
}
public EventsStream buildForBaseSubscription(final UUID bundleId, final TenantContext tenantContext) throws EntitlementApiException {
@@ -108,25 +110,33 @@ public class EventsStreamBuilder {
return buildForEntitlement(entitlementId, internalTenantContext);
}
- public List<EventsStream> buildForAccount(final InternalTenantContext internalTenantContext) throws EntitlementApiException {
+ public AccountEventsStreams buildForAccount(final InternalTenantContext internalTenantContext) throws EntitlementApiException {
// Retrieve the subscriptions (map bundle id -> subscriptions)
final Map<UUID, List<SubscriptionBase>> subscriptions = subscriptionInternalApi.getSubscriptionsForAccount(internalTenantContext);
- if (subscriptions.isEmpty()) {
- // Bail early
- return ImmutableList.<EventsStream>of();
- }
+ return buildForAccount(subscriptions, internalTenantContext);
+ }
+ // Special signature for ProxyBlockingStateDao to save a DAO call
+ public AccountEventsStreams buildForAccount(final Map<UUID, List<SubscriptionBase>> subscriptions, final InternalTenantContext internalTenantContext) throws EntitlementApiException {
// Retrieve the account
- final UUID accountId = nonEntityDao.retrieveIdFromObject(internalTenantContext.getAccountRecordId(), ObjectType.ACCOUNT);
final Account account;
try {
- account = accountInternalApi.getAccountById(accountId, internalTenantContext);
+ account = accountInternalApi.getAccountByRecordId(internalTenantContext.getAccountRecordId(), internalTenantContext);
} catch (AccountApiException e) {
throw new EntitlementApiException(e);
}
+ return buildForAccount(account, subscriptions, internalTenantContext);
+ }
+
+ private AccountEventsStreams buildForAccount(final Account account, final Map<UUID, List<SubscriptionBase>> subscriptions, final InternalTenantContext internalTenantContext) throws EntitlementApiException {
+ if (subscriptions.isEmpty()) {
+ // Bail early
+ return new DefaultAccountEventsStreams(account);
+ }
+
// Retrieve the bundles
- final List<SubscriptionBaseBundle> bundles = subscriptionInternalApi.getBundlesForAccount(accountId, internalTenantContext);
+ final List<SubscriptionBaseBundle> bundles = subscriptionInternalApi.getBundlesForAccount(account.getId(), internalTenantContext);
// Map bundle id -> bundles
final Map<UUID, SubscriptionBaseBundle> bundlesPerId = new HashMap<UUID, SubscriptionBaseBundle>();
for (final SubscriptionBaseBundle bundle : bundles) {
@@ -135,19 +145,32 @@ public class EventsStreamBuilder {
// Retrieve the blocking states
final List<BlockingState> blockingStatesForAccount = BLOCKING_STATE_ORDERING.immutableSortedCopy(defaultBlockingStateDao.getBlockingAllForAccountRecordId(internalTenantContext));
- // Copy fully the list (avoid lazy loading)
- final List<BlockingState> accountEntitlementStates = ImmutableList.<BlockingState>copyOf(Iterables.<BlockingState>filter(blockingStatesForAccount,
- new Predicate<BlockingState>() {
- @Override
- public boolean apply(final BlockingState input) {
- return BlockingStateType.ACCOUNT.equals(input.getType()) &&
- EntitlementService.ENTITLEMENT_SERVICE_NAME.equals(input.getService()) &&
- accountId.equals(input.getBlockedId());
- }
- }));
+
+ // Optimization: build lookup tables for entitlement states
+ final List<BlockingState> accountEntitlementStates = new LinkedList<BlockingState>();
+ final Map<UUID, List<BlockingState>> entitlementStatesPerSubscription = new HashMap<UUID, List<BlockingState>>();
+ final Map<UUID, List<BlockingState>> entitlementStatesPerBundle = new HashMap<UUID, List<BlockingState>>();
+ for (final BlockingState blockingState : blockingStatesForAccount) {
+ if (!EntitlementService.ENTITLEMENT_SERVICE_NAME.equals(blockingState.getService())) {
+ continue;
+ } else if (BlockingStateType.SUBSCRIPTION.equals(blockingState.getType())) {
+ if (entitlementStatesPerSubscription.get(blockingState.getBlockedId()) == null) {
+ entitlementStatesPerSubscription.put(blockingState.getBlockedId(), new LinkedList<BlockingState>());
+ }
+ entitlementStatesPerSubscription.get(blockingState.getBlockedId()).add(blockingState);
+ } else if (BlockingStateType.SUBSCRIPTION_BUNDLE.equals(blockingState.getType())) {
+ if (entitlementStatesPerBundle.get(blockingState.getBlockedId()) == null) {
+ entitlementStatesPerBundle.put(blockingState.getBlockedId(), new LinkedList<BlockingState>());
+ }
+ entitlementStatesPerBundle.get(blockingState.getBlockedId()).add(blockingState);
+ } else if (BlockingStateType.ACCOUNT.equals(blockingState.getType()) &&
+ account.getId().equals(blockingState.getBlockedId())) {
+ accountEntitlementStates.add(blockingState);
+ }
+ }
// Build the EventsStream objects
- final List<EventsStream> results = new LinkedList<EventsStream>();
+ final Map<UUID, Collection<EventsStream>> entitlementsPerBundle = new HashMap<UUID, Collection<EventsStream>>();
for (final UUID bundleId : subscriptions.keySet()) {
final SubscriptionBaseBundle bundle = bundlesPerId.get(bundleId);
final List<SubscriptionBase> allSubscriptionsForBundle = subscriptions.get(bundleId);
@@ -158,34 +181,34 @@ public class EventsStreamBuilder {
return ProductCategory.BASE.equals(input.getLastActiveProduct().getCategory());
}
}).orNull();
- // Copy fully the list (avoid lazy loading)
- final List<BlockingState> bundleEntitlementStates = ImmutableList.<BlockingState>copyOf(Iterables.<BlockingState>filter(blockingStatesForAccount,
- new Predicate<BlockingState>() {
- @Override
- public boolean apply(final BlockingState input) {
- return BlockingStateType.SUBSCRIPTION_BUNDLE.equals(input.getType()) &&
- EntitlementService.ENTITLEMENT_SERVICE_NAME.equals(input.getService()) &&
- bundle.getId().equals(input.getBlockedId());
- }
- }));
-
- for (final SubscriptionBase subscriptionBase : allSubscriptionsForBundle) {
- // Copy fully the list (avoid lazy loading)
- final List<BlockingState> subscriptionEntitlementStates = ImmutableList.<BlockingState>copyOf(Iterables.<BlockingState>filter(blockingStatesForAccount,
- new Predicate<BlockingState>() {
- @Override
- public boolean apply(final BlockingState input) {
- return BlockingStateType.SUBSCRIPTION.equals(input.getType()) &&
- EntitlementService.ENTITLEMENT_SERVICE_NAME.equals(input.getService()) &&
- subscriptionBase.getId().equals(input.getBlockedId());
- }
- }));
-
- results.add(buildForEntitlement(account, bundle, baseSubscription, subscriptionBase, allSubscriptionsForBundle, subscriptionEntitlementStates, bundleEntitlementStates, accountEntitlementStates, internalTenantContext));
+ final List<BlockingState> bundleEntitlementStates = Objects.firstNonNull(entitlementStatesPerBundle.get(bundleId), ImmutableList.<BlockingState>of());
+
+ if (entitlementsPerBundle.get(bundleId) == null) {
+ entitlementsPerBundle.put(bundleId, new LinkedList<EventsStream>());
+ }
+
+ for (final SubscriptionBase subscription : allSubscriptionsForBundle) {
+ final List<BlockingState> subscriptionBlockingStatesOnDisk = Objects.firstNonNull(entitlementStatesPerSubscription.get(subscription.getId()), ImmutableList.<BlockingState>of());
+
+ // We cannot use blockingStatesForAccount here: we need subscriptionEntitlementStates to contain the events not on disk when building an EventsStream
+ // for an add-on - which means going through the magic of ProxyBlockingStateDao, which will recursively
+ // create EventsStream objects. To avoid an infinite recursion, bypass ProxyBlockingStateDao when it's not
+ // needed, i.e. if this EventStream is for a standalone or a base subscription
+ final List<BlockingState> subscriptionEntitlementStates = (baseSubscription == null || subscription.getId().equals(baseSubscription.getId())) ?
+ subscriptionBlockingStatesOnDisk :
+ blockingStateDao.getBlockingHistoryForService(subscriptionBlockingStatesOnDisk,
+ bundle,
+ baseSubscription,
+ subscription,
+ allSubscriptionsForBundle,
+ internalTenantContext);
+
+ final EventsStream eventStream = buildForEntitlement(account, bundle, baseSubscription, subscription, allSubscriptionsForBundle, subscriptionEntitlementStates, bundleEntitlementStates, accountEntitlementStates, internalTenantContext);
+ entitlementsPerBundle.get(bundleId).add(eventStream);
}
}
- return results;
+ return new DefaultAccountEventsStreams(account, bundles, entitlementsPerBundle);
}
public EventsStream buildForEntitlement(final UUID entitlementId, final InternalTenantContext internalTenantContext) throws EntitlementApiException {
@@ -211,8 +234,29 @@ public class EventsStreamBuilder {
return buildForEntitlement(bundle, baseSubscription, subscription, allSubscriptionsForBundle, internalTenantContext);
}
+ // Special signature for ProxyBlockingStateDao to save some DAO calls
+ public EventsStream buildForEntitlement(final SubscriptionBase subscription, final InternalTenantContext internalTenantContext) throws EntitlementApiException {
+ final SubscriptionBaseBundle bundle;
+ try {
+ bundle = subscriptionInternalApi.getBundleFromId(subscription.getBundleId(), internalTenantContext);
+ } catch (SubscriptionBaseApiException e) {
+ throw new EntitlementApiException(e);
+ }
+
+ final List<SubscriptionBase> allSubscriptionsForBundle = subscriptionInternalApi.getSubscriptionsForBundle(subscription.getBundleId(), internalTenantContext);
+ return buildForEntitlement(bundle, subscription, subscription, allSubscriptionsForBundle, internalTenantContext);
+ }
+
+ // Special signature for OptimizedProxyBlockingStateDao to save some DAO calls
+ public EventsStream buildForEntitlement(final SubscriptionBaseBundle bundle,
+ final SubscriptionBase subscription,
+ final List<SubscriptionBase> allSubscriptionsForBundle,
+ final InternalTenantContext internalTenantContext) throws EntitlementApiException {
+ return buildForEntitlement(bundle, subscription, subscription, allSubscriptionsForBundle, internalTenantContext);
+ }
+
private EventsStream buildForEntitlement(final SubscriptionBaseBundle bundle,
- final SubscriptionBase baseSubscription,
+ @Nullable final SubscriptionBase baseSubscription,
final SubscriptionBase subscription,
final List<SubscriptionBase> allSubscriptionsForBundle,
final InternalTenantContext internalTenantContext) throws EntitlementApiException {
@@ -225,56 +269,43 @@ public class EventsStreamBuilder {
final List<BlockingState> bundleEntitlementStates = BLOCKING_STATE_ORDERING.immutableSortedCopy(defaultBlockingStateDao.getBlockingHistoryForService(bundle.getId(), BlockingStateType.SUBSCRIPTION_BUNDLE, EntitlementService.ENTITLEMENT_SERVICE_NAME, internalTenantContext));
final List<BlockingState> accountEntitlementStates = BLOCKING_STATE_ORDERING.immutableSortedCopy(defaultBlockingStateDao.getBlockingHistoryForService(account.getId(), BlockingStateType.ACCOUNT, EntitlementService.ENTITLEMENT_SERVICE_NAME, internalTenantContext));
- // TODO PIERRE Explain the magic
- final List<BlockingState> subscriptionEntitlementStates = subscription.getId().equals(baseSubscription.getId()) ?
- BLOCKING_STATE_ORDERING.immutableSortedCopy(defaultBlockingStateDao.getBlockingHistoryForService(subscription.getId(), BlockingStateType.SUBSCRIPTION, EntitlementService.ENTITLEMENT_SERVICE_NAME, internalTenantContext)) :
- BLOCKING_STATE_ORDERING.immutableSortedCopy(blockingStateDao.getBlockingHistoryForService(subscription.getId(), BlockingStateType.SUBSCRIPTION, EntitlementService.ENTITLEMENT_SERVICE_NAME, internalTenantContext));
+ final ImmutableList<BlockingState> subscriptionEntitlementStatesOnDisk = BLOCKING_STATE_ORDERING.immutableSortedCopy(defaultBlockingStateDao.getBlockingHistoryForService(subscription.getId(), BlockingStateType.SUBSCRIPTION, EntitlementService.ENTITLEMENT_SERVICE_NAME, internalTenantContext));
+
+ // We need subscriptionEntitlementStates to contain the events not on disk when building an EventsStream
+ // for an add-on - which means going through the magic of ProxyBlockingStateDao, which will recursively
+ // create EventsStream objects. To avoid an infinite recursion, bypass ProxyBlockingStateDao when it's not
+ // needed, i.e. if this EventStream is for a standalone or a base subscription
+ final List<BlockingState> subscriptionEntitlementStates = (baseSubscription == null || subscription.getId().equals(baseSubscription.getId())) ?
+ subscriptionEntitlementStatesOnDisk :
+ blockingStateDao.getBlockingHistoryForService(subscriptionEntitlementStatesOnDisk,
+ bundle,
+ baseSubscription,
+ subscription,
+ allSubscriptionsForBundle,
+ internalTenantContext);
return buildForEntitlement(account, bundle, baseSubscription, subscription, allSubscriptionsForBundle, subscriptionEntitlementStates, bundleEntitlementStates, accountEntitlementStates, internalTenantContext);
}
private EventsStream buildForEntitlement(final Account account,
final SubscriptionBaseBundle bundle,
- final SubscriptionBase baseSubscription,
+ @Nullable final SubscriptionBase baseSubscription,
final SubscriptionBase subscription,
final List<SubscriptionBase> allSubscriptionsForBundle,
final List<BlockingState> subscriptionEntitlementStates,
final List<BlockingState> bundleEntitlementStates,
final List<BlockingState> accountEntitlementStates,
final InternalTenantContext internalTenantContext) throws EntitlementApiException {
- final BlockingAggregator blockingAggregator = checker.getBlockedStatus(filterCurrentBlockableStatePerService(accountEntitlementStates),
- filterCurrentBlockableStatePerService(bundleEntitlementStates),
- filterCurrentBlockableStatePerService(subscriptionEntitlementStates),
- internalTenantContext);
-
- return new EventsStream(account,
- bundle,
- subscriptionEntitlementStates,
- bundleEntitlementStates,
- accountEntitlementStates,
- blockingAggregator,
- baseSubscription,
- subscription,
- allSubscriptionsForBundle,
- internalTenantContext,
- clock.getUTCNow());
- }
-
- private List<BlockingState> filterCurrentBlockableStatePerService(final Iterable<BlockingState> allBlockingStates) {
- final DateTime now = clock.getUTCNow();
-
- final Map<String, BlockingState> currentBlockingStatePerService = new HashMap<String, BlockingState>();
- for (final BlockingState blockingState : allBlockingStates) {
- if (blockingState.getEffectiveDate().isAfter(now)) {
- continue;
- }
-
- if (currentBlockingStatePerService.get(blockingState.getService()) == null ||
- currentBlockingStatePerService.get(blockingState.getService()).getEffectiveDate().isBefore(blockingState.getEffectiveDate())) {
- currentBlockingStatePerService.put(blockingState.getService(), blockingState);
- }
- }
-
- return ImmutableList.<BlockingState>copyOf(currentBlockingStatePerService.values());
+ return new DefaultEventsStream(account,
+ bundle,
+ subscriptionEntitlementStates,
+ bundleEntitlementStates,
+ accountEntitlementStates,
+ checker,
+ baseSubscription,
+ subscription,
+ allSubscriptionsForBundle,
+ internalTenantContext,
+ clock.getUTCNow());
}
}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/glue/DefaultEntitlementModule.java b/entitlement/src/main/java/com/ning/billing/entitlement/glue/DefaultEntitlementModule.java
index d95248b..975419d 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/glue/DefaultEntitlementModule.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/glue/DefaultEntitlementModule.java
@@ -19,11 +19,13 @@ package com.ning.billing.entitlement.glue;
import org.skife.config.ConfigSource;
import com.ning.billing.entitlement.DefaultEntitlementService;
+import com.ning.billing.entitlement.EntitlementInternalApi;
import com.ning.billing.entitlement.EntitlementService;
import com.ning.billing.entitlement.api.DefaultEntitlementApi;
import com.ning.billing.entitlement.api.DefaultSubscriptionApi;
import com.ning.billing.entitlement.api.EntitlementApi;
import com.ning.billing.entitlement.api.SubscriptionApi;
+import com.ning.billing.entitlement.api.svcs.DefaultEntitlementInternalApi;
import com.ning.billing.entitlement.api.svcs.DefaultInternalBlockingApi;
import com.ning.billing.entitlement.block.BlockingChecker;
import com.ning.billing.entitlement.block.DefaultBlockingChecker;
@@ -38,7 +40,6 @@ import com.google.inject.AbstractModule;
public class DefaultEntitlementModule extends AbstractModule implements EntitlementModule {
-
public DefaultEntitlementModule(final ConfigSource configSource) {
}
@@ -47,6 +48,7 @@ public class DefaultEntitlementModule extends AbstractModule implements Entitlem
installBlockingStateDao();
installBlockingApi();
installEntitlementApi();
+ installEntitlementInternalApi();
installSubscriptionApi();
installBlockingChecker();
bind(EntitlementService.class).to(DefaultEntitlementService.class).asEagerSingleton();
@@ -70,6 +72,11 @@ public class DefaultEntitlementModule extends AbstractModule implements Entitlem
}
@Override
+ public void installEntitlementInternalApi() {
+ bind(EntitlementInternalApi.class).to(DefaultEntitlementInternalApi.class).asEagerSingleton();
+ }
+
+ @Override
public void installSubscriptionApi() {
bind(SubscriptionApi.class).to(DefaultSubscriptionApi.class).asEagerSingleton();
}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/engine/core/TestEntitlementUtils.java b/entitlement/src/test/java/com/ning/billing/entitlement/engine/core/TestEntitlementUtils.java
index a0c387c..78ba189 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/engine/core/TestEntitlementUtils.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/engine/core/TestEntitlementUtils.java
@@ -29,6 +29,7 @@ import org.testng.annotations.Test;
import com.ning.billing.account.api.Account;
import com.ning.billing.api.TestApiListener.NextEvent;
+import com.ning.billing.callcontext.InternalTenantContext;
import com.ning.billing.catalog.api.BillingActionPolicy;
import com.ning.billing.catalog.api.BillingPeriod;
import com.ning.billing.catalog.api.PlanPhaseSpecifier;
@@ -36,6 +37,7 @@ import com.ning.billing.catalog.api.PriceListSet;
import com.ning.billing.catalog.api.ProductCategory;
import com.ning.billing.entitlement.EntitlementService;
import com.ning.billing.entitlement.EntitlementTestSuiteWithEmbeddedDB;
+import com.ning.billing.entitlement.EventsStream;
import com.ning.billing.entitlement.api.BlockingState;
import com.ning.billing.entitlement.api.BlockingStateType;
import com.ning.billing.entitlement.api.DefaultEntitlement;
@@ -46,6 +48,8 @@ import com.ning.billing.entitlement.api.EntitlementApiException;
import com.ning.billing.entitlement.dao.BlockingStateSqlDao;
import com.google.common.base.Objects;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
@@ -354,7 +358,14 @@ public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
// Test the "read" path
private void checkFutureBlockingStatesToCancel(final DefaultEntitlement baseEntitlement, @Nullable final DefaultEntitlement addOnEntitlement, @Nullable final DateTime effectiveCancellationDateTime) throws EntitlementApiException {
- final Collection<BlockingState> blockingStatesForCancellation = computeFutureBlockingStatesForAssociatedAddons(baseEntitlement);
+ final Collection<BlockingState> blockingStatesForCancellationViaEntitlement = computeFutureBlockingStatesForAssociatedAddonsViaEntitlement(baseEntitlement);
+ doCheckFutureBlockingStatesToCancel(addOnEntitlement, effectiveCancellationDateTime, blockingStatesForCancellationViaEntitlement);
+
+ final Collection<BlockingState> blockingStatesForCancellationViaAccount = computeFutureBlockingStatesForAssociatedAddonsViaAccount(baseEntitlement);
+ doCheckFutureBlockingStatesToCancel(addOnEntitlement, effectiveCancellationDateTime, blockingStatesForCancellationViaAccount);
+ }
+
+ private void doCheckFutureBlockingStatesToCancel(final DefaultEntitlement addOnEntitlement, final DateTime effectiveCancellationDateTime, final Collection<BlockingState> blockingStatesForCancellation) {
if (addOnEntitlement == null || effectiveCancellationDateTime == null) {
Assert.assertEquals(blockingStatesForCancellation.size(), 0);
} else {
@@ -370,7 +381,14 @@ public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
// Test the "write" path
private void checkActualBlockingStatesToCancel(final DefaultEntitlement baseEntitlement, final DefaultEntitlement addOnEntitlement, @Nullable final DateTime effectiveCancellationDateTime, final boolean approximateDateCheck) throws EntitlementApiException {
- final Collection<BlockingState> blockingStatesForCancellation = computeBlockingStatesForAssociatedAddons(baseEntitlement, Objects.firstNonNull(effectiveCancellationDateTime, initialDate.toDateTimeAtStartOfDay()));
+ final Collection<BlockingState> blockingStatesForCancellationViaEntitlement = computeBlockingStatesForAssociatedAddonsViaEntitlement(baseEntitlement, Objects.firstNonNull(effectiveCancellationDateTime, initialDate.toDateTimeAtStartOfDay()));
+ doCheckActualBlockingStatesToCancel(addOnEntitlement, effectiveCancellationDateTime, approximateDateCheck, blockingStatesForCancellationViaEntitlement);
+
+ final Collection<BlockingState> blockingStatesForCancellationViaAccount = computeBlockingStatesForAssociatedAddonsViaAccount(baseEntitlement, Objects.firstNonNull(effectiveCancellationDateTime, initialDate.toDateTimeAtStartOfDay()));
+ doCheckActualBlockingStatesToCancel(addOnEntitlement, effectiveCancellationDateTime, approximateDateCheck, blockingStatesForCancellationViaAccount);
+ }
+
+ private void doCheckActualBlockingStatesToCancel(final DefaultEntitlement addOnEntitlement, final DateTime effectiveCancellationDateTime, final boolean approximateDateCheck, final Collection<BlockingState> blockingStatesForCancellation) {
if (effectiveCancellationDateTime == null) {
Assert.assertEquals(blockingStatesForCancellation.size(), 0);
} else {
@@ -415,13 +433,37 @@ public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
Assert.assertEquals(blockingStatesForAddOn.get(0).getStateName(), DefaultEntitlementApi.ENT_STATE_CANCELLED);
}
- private Collection<BlockingState> computeFutureBlockingStatesForAssociatedAddons(final DefaultEntitlement baseEntitlement) throws EntitlementApiException {
+ private Collection<BlockingState> computeFutureBlockingStatesForAssociatedAddonsViaEntitlement(final DefaultEntitlement baseEntitlement) throws EntitlementApiException {
final EventsStream eventsStream = eventsStreamBuilder.buildForEntitlement(baseEntitlement.getId(), callContext);
return eventsStream.computeAddonsBlockingStatesForFutureSubscriptionBaseEvents();
}
- private Collection<BlockingState> computeBlockingStatesForAssociatedAddons(final DefaultEntitlement baseEntitlement, final DateTime effectiveDate) throws EntitlementApiException {
+ private Collection<BlockingState> computeFutureBlockingStatesForAssociatedAddonsViaAccount(final DefaultEntitlement baseEntitlement) throws EntitlementApiException {
+ final InternalTenantContext context = internalCallContextFactory.createInternalTenantContext(baseEntitlement.getAccountId(), callContext);
+ final EventsStream eventsStream = Iterables.<EventsStream>find(Iterables.<EventsStream>concat(eventsStreamBuilder.buildForAccount(context).getEventsStreams().values()),
+ new Predicate<EventsStream>() {
+ @Override
+ public boolean apply(final EventsStream input) {
+ return input.getSubscription().getId().equals(baseEntitlement.getId());
+ }
+ });
+ return eventsStream.computeAddonsBlockingStatesForFutureSubscriptionBaseEvents();
+ }
+
+ private Collection<BlockingState> computeBlockingStatesForAssociatedAddonsViaEntitlement(final DefaultEntitlement baseEntitlement, final DateTime effectiveDate) throws EntitlementApiException {
final EventsStream eventsStream = eventsStreamBuilder.buildForEntitlement(baseEntitlement.getId(), callContext);
return eventsStream.computeAddonsBlockingStatesForNextSubscriptionBaseEvent(effectiveDate);
}
+
+ private Collection<BlockingState> computeBlockingStatesForAssociatedAddonsViaAccount(final DefaultEntitlement baseEntitlement, final DateTime effectiveDate) throws EntitlementApiException {
+ final InternalTenantContext context = internalCallContextFactory.createInternalTenantContext(baseEntitlement.getAccountId(), callContext);
+ final EventsStream eventsStream = Iterables.<EventsStream>find(Iterables.<EventsStream>concat(eventsStreamBuilder.buildForAccount(context).getEventsStreams().values()),
+ new Predicate<EventsStream>() {
+ @Override
+ public boolean apply(final EventsStream input) {
+ return input.getSubscription().getId().equals(baseEntitlement.getId());
+ }
+ });
+ return eventsStream.computeAddonsBlockingStatesForNextSubscriptionBaseEvent(effectiveDate);
+ }
}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java b/entitlement/src/test/java/com/ning/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java
index 2227fd6..99711d9 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java
@@ -52,6 +52,7 @@ import com.ning.billing.subscription.api.SubscriptionBaseInternalApi;
import com.ning.billing.subscription.api.SubscriptionBaseService;
import com.ning.billing.subscription.engine.core.DefaultSubscriptionBaseService;
import com.ning.billing.tag.TagInternalApi;
+import com.ning.billing.util.callcontext.InternalCallContextFactory;
import com.ning.billing.util.svcsapi.bus.BusService;
import com.ning.billing.util.tag.dao.TagDao;
@@ -106,6 +107,8 @@ public class EntitlementTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuiteWi
protected EntitlementUtils entitlementUtils;
@Inject
protected EventsStreamBuilder eventsStreamBuilder;
+ @Inject
+ protected InternalCallContextFactory internalCallContextFactory;
protected Catalog catalog;
diff --git a/subscription/src/main/java/com/ning/billing/subscription/glue/DefaultSubscriptionModule.java b/subscription/src/main/java/com/ning/billing/subscription/glue/DefaultSubscriptionModule.java
index 0c35d1c..953a2ea 100644
--- a/subscription/src/main/java/com/ning/billing/subscription/glue/DefaultSubscriptionModule.java
+++ b/subscription/src/main/java/com/ning/billing/subscription/glue/DefaultSubscriptionModule.java
@@ -23,6 +23,7 @@ import com.ning.billing.glue.SubscriptionModule;
import com.ning.billing.subscription.alignment.MigrationPlanAligner;
import com.ning.billing.subscription.alignment.PlanAligner;
import com.ning.billing.subscription.api.SubscriptionBaseApiService;
+import com.ning.billing.subscription.api.SubscriptionBaseInternalApi;
import com.ning.billing.subscription.api.SubscriptionBaseService;
import com.ning.billing.subscription.api.migration.DefaultSubscriptionBaseMigrationApi;
import com.ning.billing.subscription.api.migration.SubscriptionBaseMigrationApi;
@@ -40,7 +41,6 @@ import com.ning.billing.subscription.engine.dao.DefaultSubscriptionDao;
import com.ning.billing.subscription.engine.dao.RepairSubscriptionDao;
import com.ning.billing.subscription.engine.dao.SubscriptionDao;
import com.ning.billing.util.config.SubscriptionConfig;
-import com.ning.billing.subscription.api.SubscriptionBaseInternalApi;
import com.google.inject.AbstractModule;
import com.google.inject.name.Names;
@@ -68,7 +68,6 @@ public class DefaultSubscriptionModule extends AbstractModule implements Subscri
}
protected void installSubscriptionCore() {
-
bind(SubscriptionBaseApiService.class).annotatedWith(Names.named(REPAIR_NAMED)).to(RepairSubscriptionApiService.class).asEagerSingleton();
bind(SubscriptionBaseApiService.class).to(DefaultSubscriptionBaseApiService.class).asEagerSingleton();
@@ -106,7 +105,6 @@ public class DefaultSubscriptionModule extends AbstractModule implements Subscri
bind(SubscriptionBaseMigrationApi.class).to(DefaultSubscriptionBaseMigrationApi.class).asEagerSingleton();
}
-
@Override
public void installSubscriptionInternalApi() {
bind(SubscriptionBaseInternalApi.class).to(DefaultSubscriptionInternalApi.class).asEagerSingleton();
diff --git a/util/src/main/java/com/ning/billing/util/customfield/ShouldntHappenException.java b/util/src/main/java/com/ning/billing/util/customfield/ShouldntHappenException.java
new file mode 100644
index 0000000..5d4cc2d
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/customfield/ShouldntHappenException.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2010-2013 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.util.customfield;
+
+public class ShouldntHappenException extends RuntimeException {
+
+ public ShouldntHappenException(final String message) {
+ super(message);
+ }
+}
diff --git a/util/src/test/java/com/ning/billing/mock/glue/MockEntitlementModule.java b/util/src/test/java/com/ning/billing/mock/glue/MockEntitlementModule.java
index 407e98f..60f9fe8 100644
--- a/util/src/test/java/com/ning/billing/mock/glue/MockEntitlementModule.java
+++ b/util/src/test/java/com/ning/billing/mock/glue/MockEntitlementModule.java
@@ -18,6 +18,7 @@ package com.ning.billing.mock.glue;
import org.mockito.Mockito;
+import com.ning.billing.entitlement.EntitlementInternalApi;
import com.ning.billing.entitlement.api.EntitlementApi;
import com.ning.billing.entitlement.api.SubscriptionApi;
import com.ning.billing.glue.EntitlementModule;
@@ -29,6 +30,7 @@ public class MockEntitlementModule extends AbstractModule implements Entitlement
private final BlockingInternalApi blockingApi = Mockito.mock(BlockingInternalApi.class);
private final EntitlementApi entitlementApi = Mockito.mock(EntitlementApi.class);
+ private final EntitlementInternalApi entitlementInternalApi = Mockito.mock(EntitlementInternalApi.class);
private final SubscriptionApi subscriptionApi = Mockito.mock(SubscriptionApi.class);
@Override
@@ -54,6 +56,11 @@ public class MockEntitlementModule extends AbstractModule implements Entitlement
}
@Override
+ public void installEntitlementInternalApi() {
+ bind(EntitlementInternalApi.class).toInstance(entitlementInternalApi);
+ }
+
+ @Override
public void installSubscriptionApi() {
bind(SubscriptionApi.class).toInstance(subscriptionApi);
}
@@ -61,5 +68,4 @@ public class MockEntitlementModule extends AbstractModule implements Entitlement
@Override
public void installBlockingChecker() {
}
-
}