killbill-memoizeit
Changes
junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingCalculator.java 167(+3 -164)
junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingStateNesting.java 75(+75 -0)
junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DisabledDuration.java 116(+116 -0)
Details
diff --git a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingCalculator.java b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingCalculator.java
index ab0c806..864d7f9 100644
--- a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingCalculator.java
+++ b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingCalculator.java
@@ -33,7 +33,6 @@ import java.util.concurrent.atomic.AtomicLong;
import javax.annotation.Nullable;
import org.joda.time.DateTime;
-import org.joda.time.Days;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.catalog.api.BillingPeriod;
import org.killbill.billing.catalog.api.Catalog;
@@ -66,74 +65,6 @@ public class BlockingCalculator {
private final BlockingInternalApi blockingApi;
private final CatalogService catalogService;
- protected static class DisabledDuration implements Comparable<DisabledDuration> {
-
- private final DateTime start;
- private DateTime end;
-
- public DisabledDuration(final DateTime start, final DateTime end) {
- this.start = start;
- this.end = end;
- }
-
- public DateTime getStart() {
- return start;
- }
-
- public DateTime getEnd() {
- return end;
- }
-
- public void setEnd(final DateTime end) {
- this.end = end;
- }
-
-
- // Order by start date first and then end date
- @Override
- public int compareTo(final DisabledDuration o) {
- int result = start.compareTo(o.getStart());
- if (result == 0) {
- result = end.compareTo(o.getEnd());
- }
- return result;
- }
-
- //
- //
- // Assumptions (based on ordering):
- // * this.start <= o.start
- // * this.end <= o.end when this.start == o.start
- //
- // Case 1: this contained into o => false
- // |---------| this
- // |--------------| o
- //
- // Case 2: this overlaps with o => false
- // |---------| this
- // |--------------| o
- //
- // Case 3: o contains into this => false
- // |---------| this
- // |---| o
- //
- // Case 4: this and o are adjacent => false
- // |---------| this
- // |---| o
- // Case 5: this and o are disjoint => true
- // |---------| this
- // |---| o
- public boolean isDisjoint(final DisabledDuration o) {
- return end.compareTo(o.getStart()) < 0;
- }
-
- public static DisabledDuration mergeDuration(DisabledDuration d1, DisabledDuration d2) {
- final DateTime endDate = d1.getEnd().compareTo(d2.getEnd()) < 0 ? d2.getEnd() : d1.getEnd();
- return new DisabledDuration(d1.getStart(), endDate);
- }
-
- }
-
@Inject
public BlockingCalculator(final BlockingInternalApi blockingApi, final CatalogService catalogService) {
this.blockingApi = blockingApi;
@@ -368,7 +299,7 @@ public class BlockingCalculator {
// In ascending order
protected List<DisabledDuration> createBlockingDurations(final Iterable<BlockingState> inputBundleEvents) {
- final List<DisabledDuration> result = new ArrayList<BlockingCalculator.DisabledDuration>();
+ final List<DisabledDuration> result = new ArrayList<DisabledDuration>();
final Set<String> services = ImmutableSet.copyOf(Iterables.transform(inputBundleEvents, new Function<BlockingState, String>() {
@Override
@@ -383,43 +314,14 @@ public class BlockingCalculator {
}
for (final BlockingState e : inputBundleEvents) {
-
final BlockingStateNesting svcBlockingStateNesting = svcBlockedNesting.get(e.getService());
- int blockedNesting = svcBlockingStateNesting.getBlockedNesting();
- BlockingState first = svcBlockingStateNesting.getFirst();
-
- if (e.isBlockBilling() && blockedNesting == 0) {
- // First blocking event of contiguous series of blocking events
- first = e;
- blockedNesting++;
- } else if (e.isBlockBilling() && blockedNesting > 0) {
- // Nest blocking states
- blockedNesting++;
- } else if (!e.isBlockBilling() && blockedNesting > 0) {
- blockedNesting--;
- if (blockedNesting == 0) {
- // End of the interval
- svcBlockingStateNesting.addDisabledDuration(first, e);
- first = null;
- }
- }
-
- svcBlockingStateNesting.setFirst(first);
- svcBlockingStateNesting.setBlockedNesting(blockedNesting);
- svcBlockingStateNesting.setLastOne(e);
- }
-
-
- for (final BlockingStateNesting svc : svcBlockedNesting.values()) {
- if (svc.getFirst() != null) {
- svc.addDisabledDuration(svc.getFirst(), svc.getLastOne().isBlockBilling() ? null : svc.getLastOne());
- }
+ svcBlockingStateNesting.addBlockingState(e);
}
Iterable<DisabledDuration> unorderedDisabledDuration = Iterables.concat(Iterables.transform(svcBlockedNesting.values(), new Function<BlockingStateNesting, List<DisabledDuration>>() {
@Override
public List<DisabledDuration> apply(final BlockingStateNesting input) {
- return input.getResult();
+ return input.build();
}
}));
@@ -446,69 +348,6 @@ public class BlockingCalculator {
return result;
}
-
- private static class BlockingStateNesting {
-
- final List<DisabledDuration> result;
-
- private int blockedNesting;
- private BlockingState first;
-
- private BlockingState lastOne;
-
- public BlockingStateNesting() {
- this.blockedNesting = 0;
- this.first = null;
- this.lastOne = null;
- this.result = new ArrayList<DisabledDuration>();
- }
-
- public int getBlockedNesting() {
- return blockedNesting;
- }
-
- public BlockingState getFirst() {
- return first;
- }
-
- public List<DisabledDuration> getResult() {
- return result;
- }
-
- public BlockingState getLastOne() {
- return lastOne;
- }
-
- public void setLastOne(final BlockingState lastOne) {
- this.lastOne = lastOne;
- }
-
- public void setBlockedNesting(final int blockedNesting) {
- this.blockedNesting = blockedNesting;
- }
-
- public void setFirst(final BlockingState input) {
- first = input;
- }
-
- private void addDisabledDuration(final BlockingState firstBlocking, @Nullable final BlockingState firstNonBlocking) {
-
- final DisabledDuration lastOne = result.isEmpty() ? null : result.get(result.size() - 1);
-
- final DateTime startDate = firstBlocking.getEffectiveDate();
- final DateTime endDate = firstNonBlocking == null ? null : firstNonBlocking.getEffectiveDate();
-
-
- if (lastOne != null && lastOne.getEnd().compareTo(startDate) >= 0) {
- lastOne.setEnd(endDate);
- } else if (endDate == null || Days.daysBetween(startDate, endDate).getDays() >= 1) {
- // Don't disable for periods less than a day (see https://github.com/killbill/killbill/issues/267)
- result.add(new DisabledDuration(startDate, endDate));
- }
- }
- }
-
-
@VisibleForTesting
static AtomicLong getGlobalTotalOrder() {
return globaltotalOrder;
diff --git a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingStateNesting.java b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingStateNesting.java
new file mode 100644
index 0000000..1e5c730
--- /dev/null
+++ b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingStateNesting.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2014-2017 Groupon, Inc
+ * Copyright 2014-2017 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.killbill.billing.junction.plumbing.billing;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+import org.joda.time.DateTime;
+import org.joda.time.Days;
+import org.killbill.billing.entitlement.api.BlockingState;
+
+public class BlockingStateNesting {
+
+ private int nestingLevel;
+ private BlockingState first;
+
+ final List<DisabledDuration> result;
+
+ public BlockingStateNesting() {
+ this.nestingLevel = 0;
+ this.first = null;
+ this.result = new ArrayList<DisabledDuration>();
+ }
+
+
+ public List<DisabledDuration> build() {
+ if (first != null) {
+ addDisabledDuration(null);
+ }
+ return result;
+ }
+
+ public void addBlockingState(final BlockingState currentBlockingState) {
+
+ if (currentBlockingState.isBlockBilling()) {
+ if (nestingLevel == 0) {
+ first = currentBlockingState;
+ }
+ nestingLevel++;
+ }
+
+ if (!currentBlockingState.isBlockBilling() && nestingLevel > 0) {
+ nestingLevel--;
+ if (nestingLevel == 0) {
+ addDisabledDuration(currentBlockingState.getEffectiveDate());
+ first = null;
+ }
+ }
+ }
+
+ private void addDisabledDuration(@Nullable final DateTime disableDurationEndDate) {
+
+ if (disableDurationEndDate == null || Days.daysBetween(first.getEffectiveDate(), disableDurationEndDate).getDays() >= 1) {
+ // Don't disable for periods less than a day (see https://github.com/killbill/killbill/issues/267)
+ result.add(new DisabledDuration(first.getEffectiveDate(), disableDurationEndDate));
+ }
+ }
+}
diff --git a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DisabledDuration.java b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DisabledDuration.java
new file mode 100644
index 0000000..78633b9
--- /dev/null
+++ b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DisabledDuration.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2014-2017 Groupon, Inc
+ * Copyright 2014-2017 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.killbill.billing.junction.plumbing.billing;
+
+import org.joda.time.DateTime;
+
+class DisabledDuration implements Comparable<DisabledDuration> {
+
+ private final DateTime start;
+ private DateTime end;
+
+ public DisabledDuration(final DateTime start, final DateTime end) {
+ this.start = start;
+ this.end = end;
+ }
+
+ public DateTime getStart() {
+ return start;
+ }
+
+ public DateTime getEnd() {
+ return end;
+ }
+
+ public void setEnd(final DateTime end) {
+ this.end = end;
+ }
+
+ // Order by start date first and then end date
+ @Override
+ public int compareTo(final DisabledDuration o) {
+ int result = start.compareTo(o.getStart());
+ if (result == 0) {
+ if (end == null && o.getEnd() == null) {
+ result = 0;
+ } else if (end != null && o.getEnd() != null) {
+ result = end.compareTo(o.getEnd());
+ } else if (o.getEnd() == null) {
+ return -1;
+ } else {
+ return 1;
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof DisabledDuration)) {
+ return false;
+ }
+
+ final DisabledDuration that = (DisabledDuration) o;
+
+ return compareTo(that) == 0;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = start != null ? start.hashCode() : 0;
+ result = 31 * result + (end != null ? end.hashCode() : 0);
+ return result;
+ }
+
+ //
+ //
+ // Assumptions (based on ordering):
+ // * this.start <= o.start
+ // * this.end <= o.end when this.start == o.start
+ //
+ // Case 1: this contained into o => false
+ // |---------| this
+ // |--------------| o
+ //
+ // Case 2: this overlaps with o => false
+ // |---------| this
+ // |--------------| o
+ //
+ // Case 3: o contains into this => false
+ // |---------| this
+ // |---| o
+ //
+ // Case 4: this and o are adjacent => false
+ // |---------| this
+ // |---| o
+ // Case 5: this and o are disjoint => true
+ // |---------| this
+ // |---| o
+ public boolean isDisjoint(final DisabledDuration o) {
+ return end.compareTo(o.getStart()) < 0;
+ }
+
+ public static DisabledDuration mergeDuration(DisabledDuration d1, DisabledDuration d2) {
+ final DateTime endDate = d1.getEnd().compareTo(d2.getEnd()) < 0 ? d2.getEnd() : d1.getEnd();
+ return new DisabledDuration(d1.getStart(), endDate);
+ }
+
+}
diff --git a/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBlockingCalculator.java b/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBlockingCalculator.java
index 32a7bb7..0affa91 100644
--- a/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBlockingCalculator.java
+++ b/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBlockingCalculator.java
@@ -47,7 +47,6 @@ import org.killbill.billing.entitlement.dao.MockBlockingStateDao;
import org.killbill.billing.junction.BillingEvent;
import org.killbill.billing.junction.DefaultBlockingState;
import org.killbill.billing.junction.JunctionTestSuiteNoDB;
-import org.killbill.billing.junction.plumbing.billing.BlockingCalculator.DisabledDuration;
import org.killbill.billing.subscription.api.SubscriptionBase;
import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
import org.mockito.Mockito;
@@ -154,7 +153,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
@Test(groups = "fast")
public void testEventsToRemoveOpenPrev() {
final DateTime now = clock.getUTCNow();
- final List<DisabledDuration> disabledDuration = new ArrayList<BlockingCalculator.DisabledDuration>();
+ final List<DisabledDuration> disabledDuration = new ArrayList<DisabledDuration>();
final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
disabledDuration.add(new DisabledDuration(now, null));
@@ -170,7 +169,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
@Test(groups = "fast")
public void testEventsToRemoveOpenPrevFollow() {
final DateTime now = clock.getUTCNow();
- final List<DisabledDuration> disabledDuration = new ArrayList<BlockingCalculator.DisabledDuration>();
+ final List<DisabledDuration> disabledDuration = new ArrayList<DisabledDuration>();
final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
disabledDuration.add(new DisabledDuration(now, null));
@@ -190,7 +189,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
@Test(groups = "fast")
public void testEventsToRemoveOpenFollow() {
final DateTime now = clock.getUTCNow();
- final List<DisabledDuration> disabledDuration = new ArrayList<BlockingCalculator.DisabledDuration>();
+ final List<DisabledDuration> disabledDuration = new ArrayList<DisabledDuration>();
final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
disabledDuration.add(new DisabledDuration(now, null));
@@ -208,7 +207,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
@Test(groups = "fast")
public void testEventsToRemoveOpenSameTime() {
final DateTime now = clock.getUTCNow();
- final List<DisabledDuration> disabledDuration = new ArrayList<BlockingCalculator.DisabledDuration>();
+ final List<DisabledDuration> disabledDuration = new ArrayList<DisabledDuration>();
final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
disabledDuration.add(new DisabledDuration(now, null));
@@ -226,7 +225,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
@Test(groups = "fast")
public void testEventsToRemoveClosedPrev() {
final DateTime now = clock.getUTCNow();
- final List<DisabledDuration> disabledDuration = new ArrayList<BlockingCalculator.DisabledDuration>();
+ final List<DisabledDuration> disabledDuration = new ArrayList<DisabledDuration>();
final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
disabledDuration.add(new DisabledDuration(now, now.plusDays(2)));
@@ -243,7 +242,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
@Test(groups = "fast")
public void testEventsToRemoveClosedPrevBetw() {
final DateTime now = clock.getUTCNow();
- final List<DisabledDuration> disabledDuration = new ArrayList<BlockingCalculator.DisabledDuration>();
+ final List<DisabledDuration> disabledDuration = new ArrayList<DisabledDuration>();
final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
disabledDuration.add(new DisabledDuration(now, now.plusDays(2)));
@@ -263,7 +262,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
@Test(groups = "fast")
public void testEventsToRemoveClosedPrevBetwNext() {
final DateTime now = clock.getUTCNow();
- final List<DisabledDuration> disabledDuration = new ArrayList<BlockingCalculator.DisabledDuration>();
+ final List<DisabledDuration> disabledDuration = new ArrayList<DisabledDuration>();
final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
disabledDuration.add(new DisabledDuration(now, now.plusDays(2)));
@@ -285,7 +284,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
@Test(groups = "fast")
public void testEventsToRemoveClosedBetwn() {
final DateTime now = clock.getUTCNow();
- final List<DisabledDuration> disabledDuration = new ArrayList<BlockingCalculator.DisabledDuration>();
+ final List<DisabledDuration> disabledDuration = new ArrayList<DisabledDuration>();
final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
disabledDuration.add(new DisabledDuration(now, now.plusDays(2)));
@@ -303,7 +302,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
@Test(groups = "fast")
public void testEventsToRemoveClosedBetweenFollow() {
final DateTime now = clock.getUTCNow();
- final List<DisabledDuration> disabledDuration = new ArrayList<BlockingCalculator.DisabledDuration>();
+ final List<DisabledDuration> disabledDuration = new ArrayList<DisabledDuration>();
final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
disabledDuration.add(new DisabledDuration(now, now.plusDays(2)));
@@ -324,7 +323,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
@Test(groups = "fast")
public void testEventsToRemoveClosedFollow() {
final DateTime now = clock.getUTCNow();
- final List<DisabledDuration> disabledDuration = new ArrayList<BlockingCalculator.DisabledDuration>();
+ final List<DisabledDuration> disabledDuration = new ArrayList<DisabledDuration>();
final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
disabledDuration.add(new DisabledDuration(now, now.plusDays(2)));
@@ -343,7 +342,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
@Test(groups = "fast")
public void testCreateNewEventsOpenPrev() throws CatalogApiException {
final DateTime now = clock.getUTCNow();
- final List<DisabledDuration> disabledDuration = new ArrayList<BlockingCalculator.DisabledDuration>();
+ final List<DisabledDuration> disabledDuration = new ArrayList<DisabledDuration>();
final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
disabledDuration.add(new DisabledDuration(now, null));
@@ -364,7 +363,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
@Test(groups = "fast")
public void testCreateNewEventsOpenPrevFollow() throws CatalogApiException {
final DateTime now = clock.getUTCNow();
- final List<DisabledDuration> disabledDuration = new ArrayList<BlockingCalculator.DisabledDuration>();
+ final List<DisabledDuration> disabledDuration = new ArrayList<DisabledDuration>();
final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
disabledDuration.add(new DisabledDuration(now, null));
@@ -386,7 +385,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
@Test(groups = "fast")
public void testCreateNewEventsOpenFollow() throws CatalogApiException {
final DateTime now = clock.getUTCNow();
- final List<DisabledDuration> disabledDuration = new ArrayList<BlockingCalculator.DisabledDuration>();
+ final List<DisabledDuration> disabledDuration = new ArrayList<DisabledDuration>();
final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
disabledDuration.add(new DisabledDuration(now, null));
@@ -402,7 +401,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
@Test(groups = "fast")
public void testCreateNewEventsOpenSameTime() throws CatalogApiException {
final DateTime now = clock.getUTCNow();
- final List<DisabledDuration> disabledDuration = new ArrayList<BlockingCalculator.DisabledDuration>();
+ final List<DisabledDuration> disabledDuration = new ArrayList<DisabledDuration>();
final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
disabledDuration.add(new DisabledDuration(now, null));
@@ -418,7 +417,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
@Test(groups = "fast")
public void testCreateNewEventsClosedPrev() throws CatalogApiException {
final DateTime now = clock.getUTCNow();
- final List<DisabledDuration> disabledDuration = new ArrayList<BlockingCalculator.DisabledDuration>();
+ final List<DisabledDuration> disabledDuration = new ArrayList<DisabledDuration>();
final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
disabledDuration.add(new DisabledDuration(now, now.plusDays(2)));
@@ -443,7 +442,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
@Test(groups = "fast")
public void testCreateNewEventsClosedPrevBetw() throws CatalogApiException {
final DateTime now = clock.getUTCNow();
- final List<DisabledDuration> disabledDuration = new ArrayList<BlockingCalculator.DisabledDuration>();
+ final List<DisabledDuration> disabledDuration = new ArrayList<DisabledDuration>();
final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
disabledDuration.add(new DisabledDuration(now, now.plusDays(2)));
@@ -468,7 +467,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
@Test(groups = "fast")
public void testCreateNewEventsClosedPrevBetwNext() throws CatalogApiException {
final DateTime now = clock.getUTCNow();
- final List<DisabledDuration> disabledDuration = new ArrayList<BlockingCalculator.DisabledDuration>();
+ final List<DisabledDuration> disabledDuration = new ArrayList<DisabledDuration>();
final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
disabledDuration.add(new DisabledDuration(now, now.plusDays(2)));
@@ -494,7 +493,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
@Test(groups = "fast")
public void testCreateNewEventsClosedBetwn() throws CatalogApiException {
final DateTime now = clock.getUTCNow();
- final List<DisabledDuration> disabledDuration = new ArrayList<BlockingCalculator.DisabledDuration>();
+ final List<DisabledDuration> disabledDuration = new ArrayList<DisabledDuration>();
final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
disabledDuration.add(new DisabledDuration(now, now.plusDays(2)));
@@ -513,7 +512,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
@Test(groups = "fast")
public void testCreateNewEventsClosedBetweenFollow() throws CatalogApiException {
final DateTime now = clock.getUTCNow();
- final List<DisabledDuration> disabledDuration = new ArrayList<BlockingCalculator.DisabledDuration>();
+ final List<DisabledDuration> disabledDuration = new ArrayList<DisabledDuration>();
final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
disabledDuration.add(new DisabledDuration(now, now.plusDays(2)));
@@ -532,7 +531,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
@Test(groups = "fast")
public void testCreateNewEventsClosedFollow() throws CatalogApiException {
final DateTime now = clock.getUTCNow();
- final List<DisabledDuration> disabledDuration = new ArrayList<BlockingCalculator.DisabledDuration>();
+ final List<DisabledDuration> disabledDuration = new ArrayList<DisabledDuration>();
final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
disabledDuration.add(new DisabledDuration(now, now.plusDays(2)));
diff --git a/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBlockingStateNesting.java b/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBlockingStateNesting.java
new file mode 100644
index 0000000..b4e0806
--- /dev/null
+++ b/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBlockingStateNesting.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright 2014-2017 Groupon, Inc
+ * Copyright 2014-2017 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.killbill.billing.junction.plumbing.billing;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.killbill.billing.entitlement.api.BlockingState;
+import org.killbill.billing.entitlement.api.BlockingStateType;
+import org.killbill.billing.junction.DefaultBlockingState;
+import org.killbill.billing.junction.JunctionTestSuiteNoDB;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+
+import static org.testng.Assert.assertEquals;
+
+public class TestBlockingStateNesting extends JunctionTestSuiteNoDB {
+
+
+ //
+ // In all tests:
+ // * events are B(locked) or U(nblocked)
+ // * Types does not matter as we only care about nesting level (A(ccount), B(undle), S(ubscription))
+
+ // B B U U
+ // |----|-----|-----|
+ // A B A B
+ //
+ // Expected: B----------------U
+ //
+ @Test(groups = "fast")
+ public void testZeroNestingLevel() throws Exception {
+
+ final List<BlockingState> input = new ArrayList<BlockingState>();
+
+ final DateTimeZone tz = DateTimeZone.forID("America/Los_Angeles");
+ final DateTime testInit = new DateTime(2017, 04, 29, 14, 15, 53, tz);
+ clock.setTime(testInit);
+ input.add(createBillingBlockingState(BlockingStateType.ACCOUNT, "SVC", true, testInit));
+ input.add(createBillingBlockingState(BlockingStateType.SUBSCRIPTION_BUNDLE, "SVC", true, testInit.plusDays(1)));
+ input.add(createBillingBlockingState(BlockingStateType.ACCOUNT, "SVC", false, testInit.plusDays(2)));
+ input.add(createBillingBlockingState(BlockingStateType.SUBSCRIPTION_BUNDLE, "SVC", false, testInit.plusDays(3)));
+
+ final BlockingStateNesting test = new BlockingStateNesting();
+ for (BlockingState cur : input) {
+ test.addBlockingState(cur);
+ }
+ final List<DisabledDuration> result = test.build();
+
+ final List<DisabledDuration> expected = ImmutableList.of(new DisabledDuration(testInit, testInit.plusDays(3)));
+
+ verify(result, expected);
+ }
+
+
+ // B B U
+ // |----|-----|-----
+ // A B A
+ //
+ // Expected: B-------------------
+ //
+ @Test(groups = "fast")
+ public void testPositiveNestingLevel() throws Exception {
+
+ final List<BlockingState> input = new ArrayList<BlockingState>();
+
+ final DateTimeZone tz = DateTimeZone.forID("America/Los_Angeles");
+ final DateTime testInit = new DateTime(2017, 04, 29, 14, 15, 53, tz);
+ clock.setTime(testInit);
+ input.add(createBillingBlockingState(BlockingStateType.ACCOUNT, "SVC", true, testInit));
+ input.add(createBillingBlockingState(BlockingStateType.SUBSCRIPTION_BUNDLE, "SVC", true, testInit.plusDays(1)));
+ input.add(createBillingBlockingState(BlockingStateType.ACCOUNT, "SVC", false, testInit.plusDays(2)));
+
+ final BlockingStateNesting test = new BlockingStateNesting();
+ for (BlockingState cur : input) {
+ test.addBlockingState(cur);
+ }
+ final List<DisabledDuration> result = test.build();
+
+ final List<DisabledDuration> expected = ImmutableList.of(new DisabledDuration(testInit, null));
+
+ verify(result, expected);
+ }
+
+ // B U B U
+ // |----|-----|-----|
+ // A A A A
+ //
+ // Expected: B----------------U
+ //
+ @Test(groups = "fast")
+ public void testMultipleDisabledDurations() throws Exception {
+
+ final List<BlockingState> input = new ArrayList<BlockingState>();
+
+ final DateTimeZone tz = DateTimeZone.forID("America/Los_Angeles");
+ final DateTime testInit = new DateTime(2017, 04, 29, 14, 15, 53, tz);
+ clock.setTime(testInit);
+ input.add(createBillingBlockingState(BlockingStateType.ACCOUNT, "SVC", true, testInit));
+ input.add(createBillingBlockingState(BlockingStateType.ACCOUNT, "SVC", false, testInit.plusDays(1)));
+ input.add(createBillingBlockingState(BlockingStateType.ACCOUNT, "SVC", true, testInit.plusDays(2)));
+ input.add(createBillingBlockingState(BlockingStateType.ACCOUNT, "SVC", false, testInit.plusDays(3)));
+
+ final BlockingStateNesting test = new BlockingStateNesting();
+ for (BlockingState cur : input) {
+ test.addBlockingState(cur);
+ }
+ final List<DisabledDuration> result = test.build();
+
+ final List<DisabledDuration> expected = ImmutableList.of(new DisabledDuration(testInit, testInit.plusDays(1)),
+ new DisabledDuration(testInit.plusDays(2), testInit.plusDays(3)));
+
+ verify(result, expected);
+ }
+
+
+ // B U U
+ // |----|-----|
+ // AB B A
+ //
+ // Expected: B----------U
+ //
+ @Test(groups = "fast")
+ public void testSameBlockingDates() throws Exception {
+
+ final List<BlockingState> input = new ArrayList<BlockingState>();
+
+ final DateTimeZone tz = DateTimeZone.forID("America/Los_Angeles");
+ final DateTime testInit = new DateTime(2017, 04, 29, 14, 15, 53, tz);
+ clock.setTime(testInit);
+ input.add(createBillingBlockingState(BlockingStateType.ACCOUNT, "SVC", true, testInit));
+ input.add(createBillingBlockingState(BlockingStateType.SUBSCRIPTION_BUNDLE, "SVC", true, testInit));
+ input.add(createBillingBlockingState(BlockingStateType.SUBSCRIPTION_BUNDLE, "SVC", false, testInit.plusDays(1)));
+ input.add(createBillingBlockingState(BlockingStateType.ACCOUNT, "SVC", false, testInit.plusDays(2)));
+
+ final BlockingStateNesting test = new BlockingStateNesting();
+ for (BlockingState cur : input) {
+ test.addBlockingState(cur);
+ }
+ final List<DisabledDuration> result = test.build();
+
+ final List<DisabledDuration> expected = ImmutableList.of(new DisabledDuration(testInit, testInit.plusDays(2)));
+
+ verify(result, expected);
+ }
+
+
+ // BU
+ // |
+ // AA
+ //
+ // Expected: None
+ //
+ @Test(groups = "fast")
+ public void testSameBlockingUnblockingDates() throws Exception {
+
+ final List<BlockingState> input = new ArrayList<BlockingState>();
+
+ final DateTimeZone tz = DateTimeZone.forID("America/Los_Angeles");
+ final DateTime testInit = new DateTime(2017, 04, 29, 14, 15, 53, tz);
+ clock.setTime(testInit);
+ input.add(createBillingBlockingState(BlockingStateType.ACCOUNT, "SVC", true, testInit));
+ input.add(createBillingBlockingState(BlockingStateType.ACCOUNT, "SVC", false, testInit));
+
+ final BlockingStateNesting test = new BlockingStateNesting();
+ for (BlockingState cur : input) {
+ test.addBlockingState(cur);
+ }
+ final List<DisabledDuration> result = test.build();
+
+ final List<DisabledDuration> expected = ImmutableList.of();
+
+ verify(result, expected);
+ }
+
+
+ // B U
+ // |-|
+ // A A
+ //
+ // Expected: None
+ //
+ @Test(groups = "fast")
+ public void testBlockingUnblockingDatesLessThanADay() throws Exception {
+
+ final List<BlockingState> input = new ArrayList<BlockingState>();
+
+ final DateTimeZone tz = DateTimeZone.forID("America/Los_Angeles");
+ final DateTime testInit = new DateTime(2017, 04, 29, 14, 15, 53, tz);
+ clock.setTime(testInit);
+ input.add(createBillingBlockingState(BlockingStateType.ACCOUNT, "SVC", true, testInit));
+ input.add(createBillingBlockingState(BlockingStateType.ACCOUNT, "SVC", false, testInit.plusHours(10)));
+
+ final BlockingStateNesting test = new BlockingStateNesting();
+ for (BlockingState cur : input) {
+ test.addBlockingState(cur);
+ }
+ final List<DisabledDuration> result = test.build();
+
+ final List<DisabledDuration> expected = ImmutableList.of();
+
+ verify(result, expected);
+ }
+
+
+
+ private void verify(final List<DisabledDuration> actual, final List<DisabledDuration> expected) {
+ assertEquals(expected.size(), actual.size());
+ for (int i = 0; i < actual.size(); i++) {
+ assertEquals(actual.get(i), expected.get(i));
+ }
+ }
+
+ private BlockingState createBillingBlockingState(final BlockingStateType type, final String service, final boolean blockBilling, final DateTime effectiveDate) {
+ return new DefaultBlockingState(UUID.randomUUID(), type, UUID.randomUUID().toString(), service, false, false, blockBilling, effectiveDate);
+ }
+
+}
\ No newline at end of file