killbill-uncached
Changes
beatrix/pom.xml 5(+5 -0)
beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueIntegration.java 140(+140 -0)
bin/clean-and-install 22(+22 -0)
pom.xml 12(+12 -0)
Details
beatrix/pom.xml 5(+5 -0)
diff --git a/beatrix/pom.xml b/beatrix/pom.xml
index da3f0e6..0e5dc26 100644
--- a/beatrix/pom.xml
+++ b/beatrix/pom.xml
@@ -102,6 +102,11 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>com.ning.billing</groupId>
+ <artifactId>killbill-overdue</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>org.jdbi</groupId>
<artifactId>jdbi</artifactId>
<scope>test</scope>
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueIntegration.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueIntegration.java
new file mode 100644
index 0000000..2dfe2ee
--- /dev/null
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueIntegration.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+//package com.ning.billing.beatrix.integration.overdue;
+//
+//import static org.testng.Assert.assertNotNull;
+//
+//import java.io.ByteArrayInputStream;
+//import java.io.InputStream;
+//
+//import org.joda.time.DateTime;
+//import org.joda.time.Interval;
+//
+//import com.google.inject.Inject;
+//import com.ning.billing.account.api.Account;
+//import com.ning.billing.beatrix.integration.TestIntegrationBase;
+//import com.ning.billing.catalog.api.BillingPeriod;
+//import com.ning.billing.catalog.api.Duration;
+//import com.ning.billing.catalog.api.PlanPhaseSpecifier;
+//import com.ning.billing.catalog.api.PriceListSet;
+//import com.ning.billing.catalog.api.ProductCategory;
+//import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+//import com.ning.billing.entitlement.api.user.SubscriptionData;
+//import com.ning.billing.junction.api.BlockingApi;
+//import com.ning.billing.overdue.config.OverdueConfig;
+//import com.ning.billing.payment.provider.MockPaymentProviderPlugin;
+//import com.ning.billing.util.clock.ClockMock;
+//import com.ning.billing.util.config.XMLLoader;
+//
+//public class TestOverdueIntegration extends TestIntegrationBase {
+// private final String configXml =
+// "<overdueConfig>" +
+// " <bundleOverdueStates>" +
+// " <state name=\"OD1\">" +
+// " <condition>" +
+// " <timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+// " <unit>MONTHS</unit><number>1</number>" +
+// " </timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+// " </condition>" +
+// " <externalMessage>Reached OD1</externalMessage>" +
+// " <blockChanges>true</blockChanges>" +
+// " <disableEntitlementAndChangesBlocked>false</disableEntitlementAndChangesBlocked>" +
+// " </state>" +
+// " <state name=\"OD2\">" +
+// " <condition>" +
+// " <timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+// " <unit>MONTHS</unit><number>2</number>" +
+// " </timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+// " </condition>" +
+// " <externalMessage>Reached OD1</externalMessage>" +
+// " <blockChanges>true</blockChanges>" +
+// " <disableEntitlementAndChangesBlocked>true</disableEntitlementAndChangesBlocked>" +
+// " </state>" +
+// " </bundleOverdueStates>" +
+// "</overdueConfig>";
+// private OverdueConfig config;
+//
+// @Inject
+// private ClockMock clock;
+//
+// @Inject
+// private MockPaymentProviderPlugin paymentPlugin;
+//
+// @Inject
+// private BlockingApi blockingApi;
+//
+// private Account account;
+// private SubscriptionBundle bundle;
+// private String productName;
+// private BillingPeriod term;
+// private String planSetName;
+//
+// long twoWeeks = new Interval(clock.getUTCNow(), clock.getUTCNow().plusWeeks(2)).toDurationMillis();
+// long fourWeeks = new Interval(clock.getUTCNow(), clock.getUTCNow().plusWeeks(4)).toDurationMillis();
+//
+// //@BeforeMethod
+// public void setup() throws Exception {
+// InputStream is = new ByteArrayInputStream(configXml.getBytes());
+// config = XMLLoader.getObjectFromStreamNoValidation(is, OverdueConfig.class);
+// Account account = accountUserApi.createAccount(getAccountData(25), null, null, context);
+// assertNotNull(account);
+//
+// bundle = entitlementUserApi.createBundleForAccount(account.getId(), "whatever", context);
+//
+// productName = "Shotgun";
+// term = BillingPeriod.MONTHLY;
+// planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
+//
+// // create account
+// // set mock payments to fail
+// // reset clock
+// // configure basic OD state rules for 2 states OD1 1-2month, OD2 2-3 month
+// }
+//
+// //@AfterMethod
+// public void cleanup(){
+// // Clear databases
+// }
+//
+// public void testBasicOverdueState() throws Exception {
+// DateTime initialDate = new DateTime(2012, 4, 25, 0, 3, 42, 0);
+// clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
+//
+//
+// // set next invoice to fail and create network
+// paymentPlugin.makeNextInvoiceFail();
+// SubscriptionData baseSubscription = subscriptionDataFromSubscription(entitlementUserApi.createSubscription(bundle.getId(),
+// new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, planSetName, null), null, context));
+// assertNotNull(baseSubscription);
+//
+//
+// // advance time 2weeks
+// clock.addDeltaFromReality(twoWeeks);
+//
+// // should still be in clear state
+// blockingApi.getBlockingStateFor(bundle);
+//
+// // set next invoice to fail and advance time 1 month
+// clock.addDeltaFromReality(fourWeeks);
+//
+// // should now be in OD1 state
+// // set next invoice to fail and advance time 1 month
+// // should now be in OD2 state
+//
+//
+// }
+//}
bin/clean-and-install 22(+22 -0)
diff --git a/bin/clean-and-install b/bin/clean-and-install
new file mode 100755
index 0000000..c6c3ac4
--- /dev/null
+++ b/bin/clean-and-install
@@ -0,0 +1,22 @@
+#! /usr/bin/env bash
+
+###################################################################################
+# #
+# Copyright 2010-2011 Ning, Inc. #
+# #
+# Ning licenses this file to you under the Apache License, version 2.0 #
+# (the "License"); you may not use this file except in compliance with the #
+# License. You may obtain a copy of the License at: #
+# #
+# http://www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, software #
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT #
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the #
+# License for the specific language governing permissions and limitations #
+# under the License. #
+# #
+###################################################################################
+
+bin/db-helper -a clean -d killbill;
+mvn -Dcom.ning.billing.dbi.test.useLocalDb=true clean install
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigration.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigration.java
index 0d7aed7..e1f6c28 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigration.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/migration/TestMigration.java
@@ -161,6 +161,7 @@ public abstract class TestMigration extends TestApiBase {
assertEquals(subscription.getCurrentPlan().getName(), "assault-rifle-annual");
assertEquals(subscription.getChargedThroughDate(), startDate.plusYears(1));
+
testListener.pushExpectedEvent(NextEvent.MIGRATE_BILLING);
testListener.pushExpectedEvent(NextEvent.CANCEL);
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java
index f3ea92a..7d1389e 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java
@@ -382,7 +382,7 @@ public abstract class TestApiBase implements TestListenerStatus {
@Override
public DateTime addToDateTime(DateTime dateTime) {
- return null; //To change body of implemented methods use File | Settings | File Templates.
+ return dateTime.plusYears(years);
}
@Override
public Period toJodaPeriod() {
diff --git a/overdue/src/main/java/com/ning/billing/overdue/config/DefaultOverdueState.java b/overdue/src/main/java/com/ning/billing/overdue/config/DefaultOverdueState.java
index 7d84e7c..9a448c2 100644
--- a/overdue/src/main/java/com/ning/billing/overdue/config/DefaultOverdueState.java
+++ b/overdue/src/main/java/com/ning/billing/overdue/config/DefaultOverdueState.java
@@ -42,12 +42,12 @@ public class DefaultOverdueState<T extends Blockable> extends ValidatingConfig<O
@XmlElement(required=false, name="externalMessage")
private String externalMessage = "";
-
- @XmlElement(required=false, name="disableEntitlementAndChangesBlocked")
- private Boolean disableEntitlement = false;
@XmlElement(required=false, name="blockChanges")
private Boolean blockChanges = false;
+
+ @XmlElement(required=false, name="disableEntitlementAndChangesBlocked")
+ private Boolean disableEntitlement = false;
@XmlElement(required=false, name="daysBetweenPaymentRetries")
private Integer daysBetweenPaymentRetries = 8;
diff --git a/overdue/src/test/java/com/ning/billing/overdue/config/TestOverdueConfig.java b/overdue/src/test/java/com/ning/billing/overdue/config/TestOverdueConfig.java
new file mode 100644
index 0000000..df4f4c3
--- /dev/null
+++ b/overdue/src/test/java/com/ning/billing/overdue/config/TestOverdueConfig.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.overdue.config;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
+import org.testng.annotations.Test;
+
+import com.ning.billing.util.config.XMLLoader;
+
+public class TestOverdueConfig {
+ private String xml =
+ "<overdueConfig>" +
+ " <bundleOverdueStates>" +
+ " <state name=\"OD1\">" +
+ " <condition>" +
+ " <timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+ " <unit>MONTHS</unit><number>1</number>" +
+ " </timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+ " </condition>" +
+ " <externalMessage>Reached OD1</externalMessage>" +
+ " <blockChanges>true</blockChanges>" +
+ " <disableEntitlementAndChangesBlocked>false</disableEntitlementAndChangesBlocked>" +
+ " </state>" +
+ " <state name=\"OD2\">" +
+ " <condition>" +
+ " <timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+ " <unit>MONTHS</unit><number>2</number>" +
+ " </timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+ " </condition>" +
+ " <externalMessage>Reached OD1</externalMessage>" +
+ " <blockChanges>true</blockChanges>" +
+ " <disableEntitlementAndChangesBlocked>true</disableEntitlementAndChangesBlocked>" +
+ " </state>" +
+ " </bundleOverdueStates>" +
+ "</overdueConfig>";
+
+ @Test
+ public void testParseConfig() throws Exception {
+ InputStream is = new ByteArrayInputStream(xml.getBytes());
+ OverdueConfig c = XMLLoader.getObjectFromStreamNoValidation(is, OverdueConfig.class);
+
+ }
+
+}
pom.xml 12(+12 -0)
diff --git a/pom.xml b/pom.xml
index 02b1aa6..7fe6a40 100644
--- a/pom.xml
+++ b/pom.xml
@@ -176,6 +176,18 @@
</dependency>
<dependency>
<groupId>com.ning.billing</groupId>
+ <artifactId>killbill-overdue</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.ning.billing</groupId>
+ <artifactId>killbill-overdue</artifactId>
+ <version>${project.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.ning.billing</groupId>
<artifactId>killbill-util</artifactId>
<version>${project.version}</version>
<type>test-jar</type>
diff --git a/util/src/test/java/com/ning/billing/util/clock/ClockMock.java b/util/src/test/java/com/ning/billing/util/clock/ClockMock.java
index 9e51a80..ab702cb 100644
--- a/util/src/test/java/com/ning/billing/util/clock/ClockMock.java
+++ b/util/src/test/java/com/ning/billing/util/clock/ClockMock.java
@@ -16,147 +16,134 @@
package com.ning.billing.util.clock;
-import com.ning.billing.catalog.api.Duration;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
+import org.joda.time.Days;
+import org.joda.time.Months;
+import org.joda.time.MutablePeriod;
+import org.joda.time.Period;
+import org.joda.time.ReadablePeriod;
+import org.joda.time.Weeks;
+import org.joda.time.Years;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.util.ArrayList;
-import java.util.List;
-
-// STEPH should really be in tests but not accessible from other sub modules
-public class ClockMock extends DefaultClock {
+import com.ning.billing.catalog.api.Duration;
+import com.ning.billing.catalog.api.TimeUnit;
+public class ClockMock implements Clock {
+
+ private MutablePeriod delta = new MutablePeriod();
private static final Logger log = LoggerFactory.getLogger(ClockMock.class);
- private enum DeltaType {
- DELTA_NONE,
- DELTA_DURATION,
- DELTA_ABS
- }
-
- private long deltaFromRealityMs;
- private List<Duration> deltaFromRealityDuration;
- private long deltaFromRealityDurationEpsilon;
- private DeltaType deltaType;
-
- public ClockMock() {
- deltaType = DeltaType.DELTA_NONE;
- deltaFromRealityMs = 0;
- deltaFromRealityDurationEpsilon = 0;
- deltaFromRealityDuration = null;
- }
-
+
@Override
public synchronized DateTime getNow(DateTimeZone tz) {
- return adjust(super.getNow(tz));
+ return getUTCNow().toDateTime(tz);
}
@Override
public synchronized DateTime getUTCNow() {
- return getNow(DateTimeZone.UTC);
+ return truncate(adjust(now()));
+ }
+
+ private DateTime adjust(DateTime now) {
+ return now.plus(delta);
}
- private void logClockAdjustment(DateTime prev, DateTime next) {
- log.info(String.format(" ************ ADJUSTING CLOCK FROM %s to %s ********************", prev, next));
+ public synchronized void setTime(DateTime time) {
+ DateTime prev = getUTCNow();
+ delta = new MutablePeriod(now(), time);
+ logChange(prev);
+ }
+
+ public synchronized void addDays(int days) {
+ adjustTo(Days.days(days));
+ }
+
+ public synchronized void addWeeks(int weeks) {
+ adjustTo(Weeks.weeks(weeks));
+ }
+
+ public synchronized void addMonths(int months) {
+ adjustTo(Months.months(months));
+ }
+
+ public synchronized void addYears(int years) {
+ adjustTo(Years.years(years));
+ }
+
+ public synchronized void reset() {
+ delta = new MutablePeriod();
+ }
+
+ @Override
+ public String toString() {
+ return getUTCNow().toString();
+ }
+
+ private void adjustTo(ReadablePeriod period) {
+ DateTime prev = getUTCNow();
+ delta.add(period);
+ logChange(prev);
+ }
+
+ private void logChange(DateTime prev) {
+ DateTime now = getUTCNow();
+ log.info(String.format(" ************ ADJUSTING CLOCK FROM %s to %s ********************", prev, now));
+ }
+
+ private DateTime now() {
+ return new DateTime(DateTimeZone.UTC);
}
- public synchronized void setDeltaFromReality(Duration delta, long epsilon) {
+ private DateTime truncate(DateTime time) {
+ return time.minus(time.getMillisOfSecond());
+ }
+
+ //
+ //Backward compatibility stuff
+ //
+ public synchronized void setDeltaFromReality(Duration duration, long epsilon) {
DateTime prev = getUTCNow();
- deltaType = DeltaType.DELTA_DURATION;
- deltaFromRealityDuration = new ArrayList<Duration>();
- deltaFromRealityDuration.add(delta);
- deltaFromRealityDurationEpsilon = epsilon;
- deltaFromRealityMs = 0;
- logClockAdjustment(prev, getUTCNow());
+ delta.addMillis((int)epsilon);
+ addDeltaFromReality(duration);
+ logChange(prev);
+
}
public synchronized void addDeltaFromReality(Duration delta) {
- DateTime prev = getUTCNow();
- if (deltaType != DeltaType.DELTA_DURATION) {
- throw new RuntimeException("ClockMock should be set with type DELTA_DURATION");
- }
- deltaFromRealityDuration.add(delta);
- logClockAdjustment(prev, getUTCNow());
+ adjustTo(periodFromDuration(delta));
}
public synchronized void setDeltaFromReality(long delta) {
- DateTime prev = getUTCNow();
- deltaType = DeltaType.DELTA_ABS;
- deltaFromRealityDuration = null;
- deltaFromRealityDurationEpsilon = 0;
- deltaFromRealityMs = delta;
- logClockAdjustment(prev, getUTCNow());
+ adjustTo(new Period(delta));
}
public synchronized void addDeltaFromReality(long delta) {
- DateTime prev = getUTCNow();
- if (deltaType != DeltaType.DELTA_ABS) {
- throw new RuntimeException("ClockMock should be set with type DELTA_ABS");
- }
- deltaFromRealityDuration = null;
- deltaFromRealityDurationEpsilon = 0;
- deltaFromRealityMs += delta;
- logClockAdjustment(prev, getUTCNow());
+ adjustTo(new Period(delta));
}
public synchronized void resetDeltaFromReality() {
- deltaType = DeltaType.DELTA_NONE;
- deltaFromRealityDuration = null;
- deltaFromRealityDurationEpsilon = 0;
- deltaFromRealityMs = 0;
+ reset();
}
+
+ public ReadablePeriod periodFromDuration(Duration duration) {
+ if (duration.getUnit() != TimeUnit.UNLIMITED) {return new Period();}
- private DateTime adjust(DateTime realNow) {
- switch(deltaType) {
- case DELTA_NONE:
- return realNow;
- case DELTA_ABS:
- return adjustFromAbsolute(realNow);
- case DELTA_DURATION:
- return adjustFromDuration(realNow);
- default:
- return null;
- }
- }
-
- private DateTime adjustFromDuration(DateTime input) {
-
- DateTime result = input;
- for (Duration cur : deltaFromRealityDuration) {
- switch (cur.getUnit()) {
+ switch (duration.getUnit()) {
case DAYS:
- result = result.plusDays(cur.getNumber());
- break;
-
+ return Days.days(duration.getNumber());
case MONTHS:
- result = result.plusMonths(cur.getNumber());
- break;
-
+ return Months.months(duration.getNumber());
case YEARS:
- result = result.plusYears(cur.getNumber());
- break;
-
+ return Years.years(duration.getNumber());
case UNLIMITED:
- default:
- throw new RuntimeException("ClockMock is adjusting an unlimited time period");
- }
+ return Years.years(100);
+ default:
+ return new Period();
}
- if (deltaFromRealityDurationEpsilon != 0) {
- result = result.plus(deltaFromRealityDurationEpsilon);
- }
- return result;
- }
-
- private DateTime adjustFromAbsolute(DateTime input) {
- return truncateMs(input.plus(deltaFromRealityMs));
}
-
- @Override
- public String toString() {
- return getUTCNow().toString();
- }
-
+
}
diff --git a/util/src/test/java/com/ning/billing/util/clock/MockClockModule.java b/util/src/test/java/com/ning/billing/util/clock/MockClockModule.java
index a8f0605..ece94b0 100644
--- a/util/src/test/java/com/ning/billing/util/clock/MockClockModule.java
+++ b/util/src/test/java/com/ning/billing/util/clock/MockClockModule.java
@@ -28,3 +28,4 @@ public class MockClockModule extends AbstractModule {
}
}
+
\ No newline at end of file
diff --git a/util/src/test/java/com/ning/billing/util/clock/OldClockMock.java b/util/src/test/java/com/ning/billing/util/clock/OldClockMock.java
new file mode 100644
index 0000000..b31db77
--- /dev/null
+++ b/util/src/test/java/com/ning/billing/util/clock/OldClockMock.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.util.clock;
+
+import com.ning.billing.catalog.api.Duration;
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+// STEPH should really be in tests but not accessible from other sub modules
+public class OldClockMock extends DefaultClock {
+
+ private static final Logger log = LoggerFactory.getLogger(OldClockMock.class);
+
+ private enum DeltaType {
+ DELTA_NONE,
+ DELTA_DURATION,
+ DELTA_ABS
+ }
+
+ private long deltaFromRealityMs;
+ private List<Duration> deltaFromRealityDuration;
+ private long deltaFromRealityDurationEpsilon;
+ private DeltaType deltaType;
+
+ public OldClockMock() {
+ deltaType = DeltaType.DELTA_NONE;
+ deltaFromRealityMs = 0;
+ deltaFromRealityDurationEpsilon = 0;
+ deltaFromRealityDuration = null;
+ }
+
+ @Override
+ public synchronized DateTime getNow(DateTimeZone tz) {
+ return adjust(super.getNow(tz));
+ }
+
+ @Override
+ public synchronized DateTime getUTCNow() {
+ return getNow(DateTimeZone.UTC);
+ }
+
+ private void logClockAdjustment(DateTime prev, DateTime next) {
+ log.info(String.format(" ************ ADJUSTING CLOCK FROM %s to %s ********************", prev, next));
+ }
+
+ public synchronized void setDeltaFromReality(Duration delta, long epsilon) {
+ DateTime prev = getUTCNow();
+ deltaType = DeltaType.DELTA_DURATION;
+ deltaFromRealityDuration = new ArrayList<Duration>();
+ deltaFromRealityDuration.add(delta);
+ deltaFromRealityDurationEpsilon = epsilon;
+ deltaFromRealityMs = 0;
+ logClockAdjustment(prev, getUTCNow());
+ }
+
+ public synchronized void addDeltaFromReality(Duration delta) {
+ DateTime prev = getUTCNow();
+ if (deltaType != DeltaType.DELTA_DURATION) {
+ throw new RuntimeException("ClockMock should be set with type DELTA_DURATION");
+ }
+ deltaFromRealityDuration.add(delta);
+ logClockAdjustment(prev, getUTCNow());
+ }
+
+ public synchronized void setDeltaFromReality(long delta) {
+ DateTime prev = getUTCNow();
+ deltaType = DeltaType.DELTA_ABS;
+ deltaFromRealityDuration = null;
+ deltaFromRealityDurationEpsilon = 0;
+ deltaFromRealityMs = delta;
+ logClockAdjustment(prev, getUTCNow());
+ }
+
+ public synchronized void addDeltaFromReality(long delta) {
+ DateTime prev = getUTCNow();
+ if (deltaType != DeltaType.DELTA_ABS) {
+ throw new RuntimeException("ClockMock should be set with type DELTA_ABS");
+ }
+ deltaFromRealityDuration = null;
+ deltaFromRealityDurationEpsilon = 0;
+ deltaFromRealityMs += delta;
+ logClockAdjustment(prev, getUTCNow());
+ }
+
+ public synchronized void resetDeltaFromReality() {
+ deltaType = DeltaType.DELTA_NONE;
+ deltaFromRealityDuration = null;
+ deltaFromRealityDurationEpsilon = 0;
+ deltaFromRealityMs = 0;
+ }
+
+ private DateTime adjust(DateTime realNow) {
+ switch(deltaType) {
+ case DELTA_NONE:
+ return realNow;
+ case DELTA_ABS:
+ return adjustFromAbsolute(realNow);
+ case DELTA_DURATION:
+ return adjustFromDuration(realNow);
+ default:
+ return null;
+ }
+ }
+
+ private DateTime adjustFromDuration(DateTime input) {
+
+ DateTime result = input;
+ for (Duration cur : deltaFromRealityDuration) {
+ switch (cur.getUnit()) {
+ case DAYS:
+ result = result.plusDays(cur.getNumber());
+ break;
+
+ case MONTHS:
+ result = result.plusMonths(cur.getNumber());
+ break;
+
+ case YEARS:
+ result = result.plusYears(cur.getNumber());
+ break;
+
+ case UNLIMITED:
+ default:
+ throw new RuntimeException("ClockMock is adjusting an unlimited time period");
+ }
+ }
+ if (deltaFromRealityDurationEpsilon != 0) {
+ result = result.plus(deltaFromRealityDurationEpsilon);
+ }
+ return result;
+ }
+
+ private DateTime adjustFromAbsolute(DateTime input) {
+ return truncateMs(input.plus(deltaFromRealityMs));
+ }
+
+ @Override
+ public String toString() {
+ return getUTCNow().toString();
+ }
+
+
+}