killbill-memoizeit
Changes
entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultBlockingTransitionInternalEvent.java 153(+153 -0)
Details
diff --git a/api/src/main/java/com/ning/billing/events/BlockingTransitionInternalEvent.java b/api/src/main/java/com/ning/billing/events/BlockingTransitionInternalEvent.java
new file mode 100644
index 0000000..bb6cb7a
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/events/BlockingTransitionInternalEvent.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.events;
+
+import java.util.UUID;
+
+import com.ning.billing.entitlement.api.BlockingStateType;
+
+public interface BlockingTransitionInternalEvent extends BusInternalEvent {
+
+ public UUID getBlockableId();
+
+ public BlockingStateType getBlockingType();
+
+ public Boolean isTransitionedToBlockedBilling();
+
+ public Boolean isTransitionedToUnblockedBilling();
+
+ public Boolean isTransitionedToBlockedEntitlement();
+
+ public Boolean isTransitionedToUnblockedEntitlement();
+}
diff --git a/api/src/main/java/com/ning/billing/events/BusInternalEvent.java b/api/src/main/java/com/ning/billing/events/BusInternalEvent.java
index 81fbf5f..0a798ad 100644
--- a/api/src/main/java/com/ning/billing/events/BusInternalEvent.java
+++ b/api/src/main/java/com/ning/billing/events/BusInternalEvent.java
@@ -22,27 +22,28 @@ import com.ning.billing.bus.api.BusEvent;
public interface BusInternalEvent extends BusEvent {
public enum BusInternalEventType {
- ACCOUNT_CREATE,
ACCOUNT_CHANGE,
- SUBSCRIPTION_TRANSITION,
- ENTITLEMENT_TRANSITION,
+ ACCOUNT_CREATE,
+ BLOCKING_STATE,
BUNDLE_REPAIR,
- INVOICE_EMPTY,
- INVOICE_CREATION,
- INVOICE_ADJUSTMENT,
- PAYMENT_INFO,
- PAYMENT_ERROR,
- CONTROL_TAG_CREATION,
- CONTROL_TAG_DELETION,
- USER_TAG_CREATION,
- USER_TAG_DELETION,
CONTROL_TAGDEFINITION_CREATION,
CONTROL_TAGDEFINITION_DELETION,
- USER_TAGDEFINITION_CREATION,
- USER_TAGDEFINITION_DELETION,
- OVERDUE_CHANGE,
+ CONTROL_TAG_CREATION,
+ CONTROL_TAG_DELETION,
CUSTOM_FIELD_CREATION,
CUSTOM_FIELD_DELETION,
+ ENTITLEMENT_TRANSITION,
+ INVOICE_ADJUSTMENT,
+ INVOICE_CREATION,
+ INVOICE_EMPTY,
+ OVERDUE_CHANGE,
+ PAYMENT_ERROR,
+ PAYMENT_INFO,
+ SUBSCRIPTION_TRANSITION,
+ USER_TAGDEFINITION_CREATION,
+ USER_TAGDEFINITION_DELETION,
+ USER_TAG_CREATION,
+ USER_TAG_DELETION
}
public BusInternalEventType getBusEventType();
diff --git a/api/src/main/java/com/ning/billing/invoice/api/InvoiceInternalApi.java b/api/src/main/java/com/ning/billing/invoice/api/InvoiceInternalApi.java
index 6f672c7..c982029 100644
--- a/api/src/main/java/com/ning/billing/invoice/api/InvoiceInternalApi.java
+++ b/api/src/main/java/com/ning/billing/invoice/api/InvoiceInternalApi.java
@@ -71,15 +71,4 @@ public interface InvoiceInternalApi {
*/
public void consumeExistingCBAOnAccountWithUnpaidInvoices(final UUID accountId, final InternalCallContext context) throws InvoiceApiException;
-
- /**
- * Insert a new notification with a notificationDate of today to trigger a new invoice on the account.
- *
- * @param accountId account id
- * @param accountTimeZone timezone of the account
- * @param context the context
- *
- * @throws InvoiceApiException
- */
- public void scheduleInvoiceForAccount(UUID accountId, DateTimeZone accountTimeZone, InternalCallContext context) throws InvoiceApiException;
}
diff --git a/beatrix/src/test/resources/beatrix.properties b/beatrix/src/test/resources/beatrix.properties
index ad6d9fb..86d8626 100644
--- a/beatrix/src/test/resources/beatrix.properties
+++ b/beatrix/src/test/resources/beatrix.properties
@@ -26,3 +26,5 @@ killbill.payment.retry.days=8,8,8,8,8,8,8,8
killbill.osgi.bundle.install.dir=/var/tmp/beatrix-bundles
org.slf4j.simpleLogger.showDateTime=true
+killbill.invoice.triggerInvoiceOnBlockingEvent=true
+
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultBlockingTransitionInternalEvent.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultBlockingTransitionInternalEvent.java
new file mode 100644
index 0000000..6d4efb4
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultBlockingTransitionInternalEvent.java
@@ -0,0 +1,153 @@
+/*
+ * 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;
+
+import java.util.UUID;
+
+import com.ning.billing.events.BlockingTransitionInternalEvent;
+import com.ning.billing.events.BusEventBase;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class DefaultBlockingTransitionInternalEvent extends BusEventBase implements BlockingTransitionInternalEvent {
+
+
+ private final UUID blockableId;
+ private final BlockingStateType blockingType;
+ private final Boolean isTransitionedToBlockedBilling;
+ private final Boolean isTransitionedToUnblockedBilling;
+ private final Boolean isTransitionedToBlockedEntitlement;
+ private final Boolean isTransitionedToUnblockedEntitlement;
+
+ @JsonCreator
+ public DefaultBlockingTransitionInternalEvent(@JsonProperty("blockableId") final UUID blockableId,
+ @JsonProperty("blockingType") final BlockingStateType blockingType,
+ @JsonProperty("isTransitionedToBlockedBilling") final Boolean transitionedToBlockedBilling,
+ @JsonProperty("isTransitionedToUnblockedBilling") final Boolean transitionedToUnblockedBilling,
+ @JsonProperty("isTransitionedToBlockedEntitlement") final Boolean transitionedToBlockedEntitlement,
+ @JsonProperty("isTransitionedToUnblockedEntitlement") final Boolean transitionedToUnblockedEntitlement,
+ @JsonProperty("searchKey1") final Long searchKey1,
+ @JsonProperty("searchKey2") final Long searchKey2,
+ @JsonProperty("userToken") final UUID userToken) {
+ super(searchKey1, searchKey2, userToken);
+ this.blockableId = blockableId;
+ this.blockingType = blockingType;
+ isTransitionedToBlockedBilling = transitionedToBlockedBilling;
+ isTransitionedToUnblockedBilling = transitionedToUnblockedBilling;
+ isTransitionedToBlockedEntitlement = transitionedToBlockedEntitlement;
+ isTransitionedToUnblockedEntitlement = transitionedToUnblockedEntitlement;
+ }
+
+ @Override
+ public UUID getBlockableId() {
+ return blockableId;
+ }
+
+ @Override
+ public BlockingStateType getBlockingType() {
+ return blockingType;
+ }
+
+ @JsonProperty("isTransitionedToBlockedBilling")
+ @Override
+ public Boolean isTransitionedToBlockedBilling() {
+ return isTransitionedToBlockedBilling;
+ }
+
+ @JsonProperty("isTransitionedToUnblockedBilling")
+ @Override
+ public Boolean isTransitionedToUnblockedBilling() {
+ return isTransitionedToUnblockedBilling;
+ }
+
+ @JsonProperty("isTransitionedToBlockedEntitlement")
+ @Override
+ public Boolean isTransitionedToBlockedEntitlement() {
+ return isTransitionedToBlockedEntitlement;
+ }
+
+ @JsonProperty("isTransitionedToUnblockedEntitlement")
+ @Override
+ public Boolean isTransitionedToUnblockedEntitlement() {
+ return isTransitionedToUnblockedEntitlement;
+ }
+
+ @JsonIgnore
+ @Override
+ public BusInternalEventType getBusEventType() {
+ return BusInternalEventType.BLOCKING_STATE;
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof DefaultBlockingTransitionInternalEvent)) {
+ return false;
+ }
+
+ final DefaultBlockingTransitionInternalEvent that = (DefaultBlockingTransitionInternalEvent) o;
+
+ if (blockableId != null ? !blockableId.equals(that.blockableId) : that.blockableId != null) {
+ return false;
+ }
+ if (blockingType != that.blockingType) {
+ return false;
+ }
+ if (isTransitionedToBlockedBilling != null ? !isTransitionedToBlockedBilling.equals(that.isTransitionedToBlockedBilling) : that.isTransitionedToBlockedBilling != null) {
+ return false;
+ }
+ if (isTransitionedToBlockedEntitlement != null ? !isTransitionedToBlockedEntitlement.equals(that.isTransitionedToBlockedEntitlement) : that.isTransitionedToBlockedEntitlement != null) {
+ return false;
+ }
+ if (isTransitionedToUnblockedBilling != null ? !isTransitionedToUnblockedBilling.equals(that.isTransitionedToUnblockedBilling) : that.isTransitionedToUnblockedBilling != null) {
+ return false;
+ }
+ if (isTransitionedToUnblockedEntitlement != null ? !isTransitionedToUnblockedEntitlement.equals(that.isTransitionedToUnblockedEntitlement) : that.isTransitionedToUnblockedEntitlement != null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = blockableId != null ? blockableId.hashCode() : 0;
+ result = 31 * result + (blockingType != null ? blockingType.hashCode() : 0);
+ result = 31 * result + (isTransitionedToBlockedBilling != null ? isTransitionedToBlockedBilling.hashCode() : 0);
+ result = 31 * result + (isTransitionedToUnblockedBilling != null ? isTransitionedToUnblockedBilling.hashCode() : 0);
+ result = 31 * result + (isTransitionedToBlockedEntitlement != null ? isTransitionedToBlockedEntitlement.hashCode() : 0);
+ result = 31 * result + (isTransitionedToUnblockedEntitlement != null ? isTransitionedToUnblockedEntitlement.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("DefaultBlockingTransitionInternalEvent{");
+ sb.append("blockableId=").append(blockableId);
+ sb.append(", blockingType=").append(blockingType);
+ sb.append(", isTransitionedToBlockedBilling=").append(isTransitionedToBlockedBilling);
+ sb.append(", isTransitionedToUnblockedBilling=").append(isTransitionedToUnblockedBilling);
+ sb.append(", isTransitionedToBlockedEntitlement=").append(isTransitionedToBlockedEntitlement);
+ sb.append(", isTransitionedToUnblockedEntitlement=").append(isTransitionedToUnblockedEntitlement);
+ sb.append('}');
+ return sb.toString();
+ }
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultInternalBlockingApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultInternalBlockingApi.java
index c8e4f7e..1e5d183 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultInternalBlockingApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultInternalBlockingApi.java
@@ -16,31 +16,50 @@
package com.ning.billing.entitlement.api.svcs;
-import com.google.inject.Inject;
+import java.util.List;
+import java.util.UUID;
+
+import javax.annotation.Nullable;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import com.ning.billing.account.api.Account;
+import com.ning.billing.bus.api.PersistentBus;
+import com.ning.billing.bus.api.PersistentBus.EventBusException;
+import com.ning.billing.callcontext.InternalCallContext;
+import com.ning.billing.callcontext.InternalTenantContext;
import com.ning.billing.clock.Clock;
-import com.ning.billing.entitlement.api.BlockingStateType;
-import com.ning.billing.entitlement.dao.BlockingStateDao;
import com.ning.billing.entitlement.api.Blockable;
+import com.ning.billing.entitlement.api.BlockingApiException;
import com.ning.billing.entitlement.api.BlockingState;
-import com.ning.billing.callcontext.InternalCallContext;
-import com.ning.billing.callcontext.InternalTenantContext;
+import com.ning.billing.entitlement.api.BlockingStateType;
+import com.ning.billing.entitlement.api.DefaultBlockingTransitionInternalEvent;
+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.events.BlockingTransitionInternalEvent;
import com.ning.billing.junction.BlockingInternalApi;
import com.ning.billing.junction.DefaultBlockingState;
-import java.util.List;
-import java.util.UUID;
+import com.google.inject.Inject;
public class DefaultInternalBlockingApi implements BlockingInternalApi {
+
+ private static final Logger log = LoggerFactory.getLogger(DefaultInternalBlockingApi.class);
+
private final BlockingStateDao dao;
+ private final BlockingChecker blockingChecker;
private final Clock clock;
+ private final PersistentBus eventBus;
@Inject
- public DefaultInternalBlockingApi(final BlockingStateDao dao, final Clock clock) {
+ public DefaultInternalBlockingApi(final BlockingStateDao dao, final BlockingChecker blockingChecker, final PersistentBus eventBus, final Clock clock) {
this.dao = dao;
this.clock = clock;
+ this.blockingChecker = blockingChecker;
+ this.eventBus = eventBus;
}
@Override
@@ -89,14 +108,58 @@ public class DefaultInternalBlockingApi implements BlockingInternalApi {
@Override
public void setBlockingState(final BlockingState state, final InternalCallContext context) {
+
+ final BlockingAggregator previousState = getBlockingStateFor(state.getBlockedId(), state.getType(), context);
+
dao.setBlockingState(state, clock, context);
+
+ final BlockingAggregator currentState = getBlockingStateFor(state.getBlockedId(), state.getType(), context);
+ if (previousState != null && currentState != null) {
+ postBlockingTransitionEvent(state.getBlockedId(), state.getType(), previousState, currentState, context);
+ }
}
+ private BlockingAggregator getBlockingStateFor(final UUID blockableId, final BlockingStateType type, final InternalCallContext context) {
+ try {
+ return blockingChecker.getBlockedStatus(blockableId, type, context);
+ } catch (BlockingApiException e) {
+ log.warn("Failed to retrieve blocking state for {} {}", blockableId, type);
+ return null;
+ }
+ }
+
+ private void postBlockingTransitionEvent(final UUID blockableId, final BlockingStateType type,
+ final BlockingAggregator previousState, final BlockingAggregator currentState, final InternalCallContext context) {
+
+ try {
+ final boolean isTransitionToBlockedBilling = !previousState.isBlockBilling() && currentState.isBlockBilling();
+ final boolean isTransitionToUnblockedBilling = previousState.isBlockBilling() && !currentState.isBlockBilling();
+
+ final boolean isTransitionToBlockedEntitlement = !previousState.isBlockEntitlement() && currentState.isBlockEntitlement();
+ final boolean isTransitionToUnblockedEntitlement = previousState.isBlockEntitlement() && !currentState.isBlockEntitlement();
+
+ final BlockingTransitionInternalEvent event = new DefaultBlockingTransitionInternalEvent(blockableId, type,
+ isTransitionToBlockedBilling, isTransitionToUnblockedBilling,
+ isTransitionToBlockedEntitlement, isTransitionToUnblockedEntitlement,
+
+ context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken());
+
+ // TODO
+ // STEPH Ideally we would like to post from transaction when we inserted the new blocking state, but new state would have to be recalculated from transaction which is
+ // difficult without the help of BlockingChecker -- which itself relies on dao. Other alternative is duplicating the logic, or refactoring the DAO to export higher level api.
+ eventBus.post(event);
+ } catch (EventBusException e) {
+ log.warn("Failed to post event {}", e);
+ }
+
+ }
+
+
BlockingStateType getBlockingStateType(final Blockable overdueable) {
if (overdueable instanceof Account) {
return BlockingStateType.ACCOUNT;
}
- // STEPH this is here to ve rify there are no service trying to block on something different than ACCOUNT level
+ // STEPH this is here to verify there are no service trying to block on something different than ACCOUNT level
// All the other entities
throw new RuntimeException("Unexpected blockable type");
}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/block/BlockingChecker.java b/entitlement/src/main/java/com/ning/billing/entitlement/block/BlockingChecker.java
index 64b83c5..5095042 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/block/BlockingChecker.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/block/BlockingChecker.java
@@ -16,9 +16,12 @@
package com.ning.billing.entitlement.block;
+import java.util.UUID;
+
import com.ning.billing.entitlement.api.Blockable;
import com.ning.billing.entitlement.api.BlockingApiException;
import com.ning.billing.callcontext.InternalTenantContext;
+import com.ning.billing.entitlement.api.BlockingStateType;
public interface BlockingChecker {
@@ -40,6 +43,8 @@ public interface BlockingChecker {
// Only throws if we can't find the blockable enties
public BlockingAggregator getBlockedStatus(Blockable blockable, InternalTenantContext context) throws BlockingApiException;
+ public BlockingAggregator getBlockedStatus(final UUID blockableId, final BlockingStateType type, final InternalTenantContext context) throws BlockingApiException;
+
public void checkBlockedChange(Blockable blockable, InternalTenantContext context) throws BlockingApiException;
public void checkBlockedEntitlement(Blockable blockable, InternalTenantContext context) throws BlockingApiException;
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/block/DefaultBlockingChecker.java b/entitlement/src/main/java/com/ning/billing/entitlement/block/DefaultBlockingChecker.java
index 2dc87c8..a1ca6d8 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/block/DefaultBlockingChecker.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/block/DefaultBlockingChecker.java
@@ -24,6 +24,7 @@ import com.ning.billing.account.api.Account;
import com.ning.billing.entitlement.api.Blockable;
import com.ning.billing.entitlement.api.BlockingApiException;
import com.ning.billing.entitlement.api.BlockingState;
+import com.ning.billing.entitlement.api.BlockingStateType;
import com.ning.billing.entitlement.dao.BlockingStateDao;
import com.ning.billing.subscription.api.SubscriptionBase;
import com.ning.billing.subscription.api.user.SubscriptionBaseApiException;
@@ -155,17 +156,28 @@ public class DefaultBlockingChecker implements BlockingChecker {
}
@Override
- public BlockingAggregator getBlockedStatus(final Blockable blockable, final InternalTenantContext context) throws BlockingApiException {
- if (blockable instanceof SubscriptionBase) {
- return getBlockedStateSubscription((SubscriptionBase) blockable, context);
- } else if (blockable instanceof SubscriptionBaseBundle) {
- return getBlockedStateBundle((SubscriptionBaseBundle) blockable, context);
- } else { //(blockable instanceof Account) {
- return getBlockedStateAccount((Account) blockable, context);
+ public BlockingAggregator getBlockedStatus(final UUID blockableId, final BlockingStateType type, final InternalTenantContext context) throws BlockingApiException {
+ if (type == BlockingStateType.SUBSCRIPTION) {
+ return getBlockedStateSubscriptionId(blockableId, context);
+ } else if (type == BlockingStateType.SUBSCRIPTION_BUNDLE) {
+ return getBlockedStateBundleId(blockableId, context);
+ } else { // BlockingStateType.ACCOUNT {
+ return getBlockedStateAccountId(blockableId, context);
}
}
@Override
+ public BlockingAggregator getBlockedStatus(final Blockable blockable, final InternalTenantContext context) throws BlockingApiException {
+ if (blockable instanceof SubscriptionBase) {
+ return getBlockedStateSubscription((SubscriptionBase) blockable, context);
+ } else if (blockable instanceof SubscriptionBaseBundle) {
+ return getBlockedStateBundle((SubscriptionBaseBundle) blockable, context);
+ } else { //(blockable instanceof Account) {
+ return getBlockedStateAccount((Account) blockable, context);
+ }
+ }
+
+ @Override
public void checkBlockedChange(final Blockable blockable, final InternalTenantContext context) throws BlockingApiException {
if (blockable instanceof SubscriptionBase && getBlockedStateSubscription((SubscriptionBase) blockable, context).isBlockChange()) {
throw new BlockingApiException(ErrorCode.BLOCK_BLOCKED_ACTION, ACTION_CHANGE, TYPE_SUBSCRIPTION, blockable.getId().toString());
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestEventJson.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestEventJson.java
new file mode 100644
index 0000000..718dd7b
--- /dev/null
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestEventJson.java
@@ -0,0 +1,43 @@
+/*
+ * 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;
+
+import java.util.UUID;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.entitlement.EntitlementTestSuiteNoDB;
+import com.ning.billing.events.BlockingTransitionInternalEvent;
+import com.ning.billing.util.jackson.ObjectMapper;
+
+public class TestEventJson extends EntitlementTestSuiteNoDB {
+
+ private final ObjectMapper mapper = new ObjectMapper();
+
+ @Test(groups = "fast", description = "Test Blocking event deserialization")
+ public void testDefaultBlockingTransitionInternalEvent() throws Exception {
+ final BlockingTransitionInternalEvent e = new DefaultBlockingTransitionInternalEvent(UUID.randomUUID(), BlockingStateType.ACCOUNT, true, false, false, true, 1L, 2L, null);
+
+ final String json = mapper.writeValueAsString(e);
+
+ final Class<?> claz = Class.forName("com.ning.billing.entitlement.api.DefaultBlockingTransitionInternalEvent");
+ final Object obj = mapper.readValue(json, claz);
+ Assert.assertTrue(obj.equals(e));
+ }
+
+}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/block/MockBlockingChecker.java b/entitlement/src/test/java/com/ning/billing/entitlement/block/MockBlockingChecker.java
new file mode 100644
index 0000000..1a97df2
--- /dev/null
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/block/MockBlockingChecker.java
@@ -0,0 +1,49 @@
+/*
+ * 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.block;
+
+import java.util.UUID;
+
+import com.ning.billing.callcontext.InternalTenantContext;
+import com.ning.billing.entitlement.api.Blockable;
+import com.ning.billing.entitlement.api.BlockingApiException;
+import com.ning.billing.entitlement.api.BlockingStateType;
+
+public class MockBlockingChecker implements BlockingChecker {
+
+ @Override
+ public BlockingAggregator getBlockedStatus(final Blockable blockable, final InternalTenantContext context) throws BlockingApiException {
+ return null;
+ }
+
+ @Override
+ public BlockingAggregator getBlockedStatus(final UUID blockableId, final BlockingStateType type, final InternalTenantContext context) throws BlockingApiException {
+ return null;
+ }
+
+ @Override
+ public void checkBlockedChange(final Blockable blockable, final InternalTenantContext context) throws BlockingApiException {
+ }
+
+ @Override
+ public void checkBlockedEntitlement(final Blockable blockable, final InternalTenantContext context) throws BlockingApiException {
+ }
+
+ @Override
+ public void checkBlockedBilling(final Blockable blockable, final InternalTenantContext context) throws BlockingApiException {
+ }
+}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java b/invoice/src/main/java/com/ning/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java
index 1046548..8eea774 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java
@@ -145,31 +145,4 @@ public class DefaultInvoiceInternalApi implements InvoiceInternalApi {
dao.consumeExstingCBAOnAccountWithUnpaidInvoices(accountId, context);
}
- @Override
- public void scheduleInvoiceForAccount(final UUID accountId, final DateTimeZone accountTimeZone, final InternalCallContext context) throws InvoiceApiException {
- final Map<UUID, List<SubscriptionBase>> subscriptions = subscriptionBaseApi.getSubscriptionsForAccount(context);
- SubscriptionBase targetSubscription = null;
- for (UUID key : subscriptions.keySet()) {
- for (SubscriptionBase cur : subscriptions.get(key)) {
- if (cur.getCategory() == ProductCategory.ADD_ON) {
- continue;
- }
- if (cur.getState() != EntitlementState.ACTIVE) {
- continue;
- }
- if (cur.getCurrentPhase() != null && cur.getCurrentPhase().getBillingPeriod() != BillingPeriod.NO_BILLING_PERIOD) {
- targetSubscription = cur;
- break;
- }
- }
- }
- if (targetSubscription == null) {
- log.info("scheduleInvoiceForAccount : no active subscriptions for account {}", accountId);
- return;
- }
-
- final DateAndTimeZoneContext timeZoneContext = new DateAndTimeZoneContext(targetSubscription.getStartDate(), accountTimeZone, clock);
- final DateTime futureNotificationTime = timeZoneContext.computeUTCDateTimeFromNow();
- nextBillingDatePoster.insertNextBillingNotification(accountId, targetSubscription.getId(), futureNotificationTime, context.getUserToken());
- }
}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/InvoiceListener.java b/invoice/src/main/java/com/ning/billing/invoice/InvoiceListener.java
index 709da03..6efe1e7 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/InvoiceListener.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/InvoiceListener.java
@@ -22,6 +22,10 @@ import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.ning.billing.account.api.AccountApiException;
+import com.ning.billing.account.api.AccountInternalApi;
+import com.ning.billing.clock.Clock;
+import com.ning.billing.events.BlockingTransitionInternalEvent;
import com.ning.billing.subscription.api.SubscriptionBaseTransitionType;
import com.ning.billing.invoice.api.InvoiceApiException;
import com.ning.billing.util.callcontext.CallOrigin;
@@ -31,6 +35,7 @@ import com.ning.billing.util.callcontext.UserType;
import com.ning.billing.events.EffectiveEntitlementInternalEvent;
import com.ning.billing.events.EffectiveSubscriptionInternalEvent;
import com.ning.billing.events.RepairSubscriptionInternalEvent;
+import com.ning.billing.util.config.InvoiceConfig;
import com.google.common.eventbus.Subscribe;
import com.google.inject.Inject;
@@ -38,13 +43,21 @@ import com.google.inject.Inject;
public class InvoiceListener {
private static final Logger log = LoggerFactory.getLogger(InvoiceListener.class);
+
private final InvoiceDispatcher dispatcher;
private final InternalCallContextFactory internalCallContextFactory;
+ private final AccountInternalApi accountApi;
+ private final InvoiceConfig invoiceConfig;
+ private final Clock clock;
@Inject
- public InvoiceListener(final InternalCallContextFactory internalCallContextFactory, final InvoiceDispatcher dispatcher) {
+ public InvoiceListener(final AccountInternalApi accountApi, final Clock clock, final InternalCallContextFactory internalCallContextFactory,
+ final InvoiceConfig invoiceConfig, final InvoiceDispatcher dispatcher) {
+ this.accountApi = accountApi;
this.dispatcher = dispatcher;
+ this.invoiceConfig = invoiceConfig;
this.internalCallContextFactory = internalCallContextFactory;
+ this.clock = clock;
}
@Subscribe
@@ -87,6 +100,26 @@ public class InvoiceListener {
}
}
+ @Subscribe
+ public void handleBlockingStateTransition(final BlockingTransitionInternalEvent event) {
+
+ // We are only interested in unblockBilling transitions or blockBilling transitions when those are configured.
+ if (!event.isTransitionedToUnblockedBilling() &&
+ !(event.isTransitionedToBlockedBilling() && invoiceConfig.isTriggerInvoiceOnBlockingEvent())) {
+ return;
+ }
+
+ try {
+ final InternalCallContext context = internalCallContextFactory.createInternalCallContext(event.getSearchKey2(), event.getSearchKey1(), "SubscriptionBaseTransition", CallOrigin.INTERNAL, UserType.SYSTEM, event.getUserToken());
+ final UUID accountId = accountApi.getByRecordId(event.getSearchKey1(), context);
+ dispatcher.processAccount(accountId, clock.getUTCNow(), false, context);
+ } catch (InvoiceApiException e) {
+ log.error(e.getMessage());
+ } catch (AccountApiException e) {
+ log.error(e.getMessage());
+ }
+ }
+
public void handleNextBillingDateEvent(final UUID subscriptionId, final DateTime eventDateTime, final UUID userToken, final Long accountRecordId, final Long tenantRecordId) {
try {
final InternalCallContext context = internalCallContextFactory.createInternalCallContext(tenantRecordId, accountRecordId, "Next Billing Date", CallOrigin.INTERNAL, UserType.SYSTEM, userToken);
diff --git a/invoice/src/test/java/com/ning/billing/invoice/generator/TestDefaultInvoiceGenerator.java b/invoice/src/test/java/com/ning/billing/invoice/generator/TestDefaultInvoiceGenerator.java
index 9673242..d8db31e 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/generator/TestDefaultInvoiceGenerator.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/generator/TestDefaultInvoiceGenerator.java
@@ -102,6 +102,11 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
public boolean isEmailNotificationsEnabled() {
return false;
}
+
+ @Override
+ public boolean isTriggerInvoiceOnBlockingEvent() {
+ return false;
+ }
};
this.generator = new DefaultInvoiceGenerator(clock, invoiceConfig);
}
diff --git a/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceNotificationQListener.java b/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceNotificationQListener.java
index d426de5..074e5e6 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceNotificationQListener.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceNotificationQListener.java
@@ -22,6 +22,8 @@ import javax.inject.Inject;
import org.joda.time.DateTime;
+import com.ning.billing.account.api.AccountInternalApi;
+import com.ning.billing.clock.Clock;
import com.ning.billing.util.callcontext.InternalCallContextFactory;
public class TestInvoiceNotificationQListener extends InvoiceListener {
@@ -30,8 +32,8 @@ public class TestInvoiceNotificationQListener extends InvoiceListener {
UUID latestSubscriptionId = null;
@Inject
- public TestInvoiceNotificationQListener(final InternalCallContextFactory internalCallContextFactory, final InvoiceDispatcher dispatcher) {
- super(internalCallContextFactory, dispatcher);
+ public TestInvoiceNotificationQListener(final AccountInternalApi accountApi, final Clock clock, final InternalCallContextFactory internalCallContextFactory, final InvoiceDispatcher dispatcher) {
+ super(accountApi, clock, internalCallContextFactory, null, dispatcher);
}
@Override
diff --git a/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModule.java b/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModule.java
index 197341e..0d4307d 100644
--- a/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModule.java
+++ b/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModule.java
@@ -20,6 +20,8 @@ import org.skife.config.ConfigSource;
import com.ning.billing.catalog.MockCatalogModule;
import com.ning.billing.entitlement.api.svcs.DefaultInternalBlockingApi;
+import com.ning.billing.entitlement.block.BlockingChecker;
+import com.ning.billing.entitlement.block.MockBlockingChecker;
import com.ning.billing.entitlement.dao.BlockingStateDao;
import com.ning.billing.entitlement.dao.MockBlockingStateDao;
import com.ning.billing.mock.glue.MockAccountModule;
@@ -60,5 +62,11 @@ public class TestJunctionModule extends DefaultJunctionModule {
public void installBlockingStateDao() {
bind(BlockingStateDao.class).to(MockBlockingStateDao.class).asEagerSingleton();
}
+
+ @Override
+ public void installBlockingChecker() {
+ bind(BlockingChecker.class).to(MockBlockingChecker.class).asEagerSingleton();
+ }
+
}
}
diff --git a/overdue/src/main/java/com/ning/billing/overdue/applicator/DefaultOverdueChangeEvent.java b/overdue/src/main/java/com/ning/billing/overdue/applicator/DefaultOverdueChangeEvent.java
index 1c9288c..c6b4397 100644
--- a/overdue/src/main/java/com/ning/billing/overdue/applicator/DefaultOverdueChangeEvent.java
+++ b/overdue/src/main/java/com/ning/billing/overdue/applicator/DefaultOverdueChangeEvent.java
@@ -83,4 +83,56 @@ public class DefaultOverdueChangeEvent extends BusEventBase implements OverdueCh
public Boolean isUnblockedBilling() {
return isUnblockedBilling;
}
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("DefaultOverdueChangeEvent{");
+ sb.append("overdueObjectId=").append(overdueObjectId);
+ sb.append(", previousOverdueStateName='").append(previousOverdueStateName).append('\'');
+ sb.append(", nextOverdueStateName='").append(nextOverdueStateName).append('\'');
+ sb.append(", isBlockedBilling=").append(isBlockedBilling);
+ sb.append(", isUnblockedBilling=").append(isUnblockedBilling);
+ sb.append('}');
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof DefaultOverdueChangeEvent)) {
+ return false;
+ }
+
+ final DefaultOverdueChangeEvent that = (DefaultOverdueChangeEvent) o;
+
+ if (isBlockedBilling != null ? !isBlockedBilling.equals(that.isBlockedBilling) : that.isBlockedBilling != null) {
+ return false;
+ }
+ if (isUnblockedBilling != null ? !isUnblockedBilling.equals(that.isUnblockedBilling) : that.isUnblockedBilling != null) {
+ return false;
+ }
+ if (nextOverdueStateName != null ? !nextOverdueStateName.equals(that.nextOverdueStateName) : that.nextOverdueStateName != null) {
+ return false;
+ }
+ if (overdueObjectId != null ? !overdueObjectId.equals(that.overdueObjectId) : that.overdueObjectId != null) {
+ return false;
+ }
+ if (previousOverdueStateName != null ? !previousOverdueStateName.equals(that.previousOverdueStateName) : that.previousOverdueStateName != null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = overdueObjectId != null ? overdueObjectId.hashCode() : 0;
+ result = 31 * result + (previousOverdueStateName != null ? previousOverdueStateName.hashCode() : 0);
+ result = 31 * result + (nextOverdueStateName != null ? nextOverdueStateName.hashCode() : 0);
+ result = 31 * result + (isBlockedBilling != null ? isBlockedBilling.hashCode() : 0);
+ result = 31 * result + (isUnblockedBilling != null ? isUnblockedBilling.hashCode() : 0);
+ return result;
+ }
}
diff --git a/overdue/src/main/java/com/ning/billing/overdue/applicator/OverdueStateApplicator.java b/overdue/src/main/java/com/ning/billing/overdue/applicator/OverdueStateApplicator.java
index 477ad68..1567ce5 100644
--- a/overdue/src/main/java/com/ning/billing/overdue/applicator/OverdueStateApplicator.java
+++ b/overdue/src/main/java/com/ning/billing/overdue/applicator/OverdueStateApplicator.java
@@ -49,9 +49,6 @@ import com.ning.billing.invoice.api.InvoiceApiException;
import com.ning.billing.invoice.api.InvoiceInternalApi;
import com.ning.billing.junction.BlockingInternalApi;
import com.ning.billing.junction.DefaultBlockingState;
-import com.ning.billing.overdue.notification.OverdueCheckNotificationKey;
-import com.ning.billing.overdue.notification.OverdueCheckNotifier;
-import com.ning.billing.overdue.notification.OverduePoster;
import com.ning.billing.overdue.OverdueApiException;
import com.ning.billing.overdue.OverdueCancellationPolicy;
import com.ning.billing.overdue.OverdueService;
@@ -60,6 +57,9 @@ import com.ning.billing.overdue.config.api.BillingState;
import com.ning.billing.overdue.config.api.OverdueException;
import com.ning.billing.overdue.config.api.OverdueStateSet;
import com.ning.billing.overdue.glue.DefaultOverdueModule;
+import com.ning.billing.overdue.notification.OverdueCheckNotificationKey;
+import com.ning.billing.overdue.notification.OverdueCheckNotifier;
+import com.ning.billing.overdue.notification.OverduePoster;
import com.ning.billing.tag.TagInternalApi;
import com.ning.billing.util.dao.NonEntityDao;
import com.ning.billing.util.email.DefaultEmailSender;
@@ -83,7 +83,6 @@ public class OverdueStateApplicator {
private final PersistentBus bus;
private final AccountInternalApi accountApi;
private final EntitlementApi entitlementApi;
- private final InvoiceInternalApi invoiceInternalApi;
private final OverdueEmailGenerator overdueEmailGenerator;
private final TagInternalApi tagApi;
private final EmailSender emailSender;
@@ -93,9 +92,8 @@ public class OverdueStateApplicator {
public OverdueStateApplicator(final BlockingInternalApi accessApi,
final AccountInternalApi accountApi,
final EntitlementApi entitlementApi,
- final InvoiceInternalApi invoiceInternalApi,
final Clock clock,
- @Named(DefaultOverdueModule.OVERDUE_NOTIFIER_CHECK_NAMED)final OverduePoster checkPoster,
+ @Named(DefaultOverdueModule.OVERDUE_NOTIFIER_CHECK_NAMED) final OverduePoster checkPoster,
final OverdueEmailGenerator overdueEmailGenerator,
final EmailConfig config,
final PersistentBus bus,
@@ -105,7 +103,6 @@ public class OverdueStateApplicator {
this.blockingApi = accessApi;
this.accountApi = accountApi;
this.entitlementApi = entitlementApi;
- this.invoiceInternalApi = invoiceInternalApi;
this.clock = clock;
this.checkPoster = checkPoster;
this.overdueEmailGenerator = overdueEmailGenerator;
@@ -137,7 +134,7 @@ public class OverdueStateApplicator {
final Period reevaluationInterval = nextOverdueState.isClearState() ? overdueStateSet.getInitialReevaluationInterval() : nextOverdueState.getReevaluationInterval();
// If there is no configuration in the config, we assume this is because the overdue conditions are not time based and so there is nothing to retry
if (reevaluationInterval == null) {
- log.debug("OverdueStateApplicator <notificationQ> : Missing InitialReevaluationInterval from config, NOT inserting notification for account " + account.getId());
+ log.debug("OverdueStateApplicator <notificationQ> : Missing InitialReevaluationInterval from config, NOT inserting notification for account " + account.getId());
} else {
log.debug("OverdueStateApplicator <notificationQ> : inserting notification for account " + account.getId() + ", time = " + clock.getUTCNow().plus(reevaluationInterval));
@@ -156,18 +153,13 @@ public class OverdueStateApplicator {
cancelSubscriptionsIfRequired(account, nextOverdueState, context);
- scheduleInvoiceIfNeeded(account, previousOverdueState, nextOverdueState, context);
-
sendEmailIfRequired(billingState, account, nextOverdueState, context);
} catch (OverdueApiException e) {
if (e.getCode() != ErrorCode.OVERDUE_NO_REEVALUATION_INTERVAL.getCode()) {
throw new OverdueException(e);
}
- } catch (InvoiceApiException e) {
- throw new OverdueException(e);
}
-
try {
bus.post(createOverdueEvent(account, previousOverdueState.getName(), nextOverdueState.getName(), isBlockBillingTransition(previousOverdueState, nextOverdueState),
isUnblockBillingTransition(previousOverdueState, nextOverdueState), context));
@@ -176,19 +168,6 @@ public class OverdueStateApplicator {
}
}
- private void scheduleInvoiceIfNeeded(final Account account, final OverdueState previousOverdueState, final OverdueState nextOverdueState, final InternalCallContext context) throws InvoiceApiException {
- //
- // Invoice will re-enter a notification to schedule a new invoice with a notificationDate equivalent to today. For a given active subscription on this account:
- // - If that notificationDate is less or equals than the chargeThroughDate of the given subscription, it means the invoice was previously invoiced and so blocking/unblocking will have an effect,
- // a new invoice will be generated
- // - If that notificationDate is greater than the chargeThroughDate, then that subscription will be invoiced for the next period.
- //
- // So in both case a new invoice will be generated.
- //
- if (isBlockBillingTransition(previousOverdueState, nextOverdueState) || isUnblockBillingTransition(previousOverdueState, nextOverdueState)) {
- invoiceInternalApi.scheduleInvoiceForAccount(account.getId(), account.getTimeZone(), context);
- }
- }
public void clear(final Account overdueable, final OverdueState previousOverdueState, final OverdueState clearState, final InternalCallContext context) throws OverdueException {
diff --git a/util/src/main/java/com/ning/billing/util/config/InvoiceConfig.java b/util/src/main/java/com/ning/billing/util/config/InvoiceConfig.java
index 0a16915..e4c978d 100644
--- a/util/src/main/java/com/ning/billing/util/config/InvoiceConfig.java
+++ b/util/src/main/java/com/ning/billing/util/config/InvoiceConfig.java
@@ -31,4 +31,10 @@ public interface InvoiceConfig extends KillbillConfig {
@Default("false")
@Description("Whether to send email notifications on invoice creation (for configured accounts)")
public boolean isEmailNotificationsEnabled();
+
+
+ @Config("killbill.invoice.triggerInvoiceOnBlockingEvent")
+ @Default("false")
+ @Description("Whether the invoice code regenerate a new invoice when a blocking event is received")
+ public boolean isTriggerInvoiceOnBlockingEvent();
}
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 54091cf..407e98f 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
@@ -36,6 +36,7 @@ public class MockEntitlementModule extends AbstractModule implements Entitlement
installBlockingStateDao();
installBlockingApi();
installEntitlementApi();
+ installBlockingChecker();
}
@Override