killbill-aplcache
Changes
account/src/main/java/com/ning/billing/account/api/user/DefaultAccountChangeNotification.java 21(+15 -6)
entitlement/src/main/java/com/ning/billing/entitlement/api/migration/DefaultEntitlementMigrationApi.java 20(+11 -9)
entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionApiService.java 81(+44 -37)
entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransitionData.java 51(+45 -6)
entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java 1112(+539 -573)
entitlement/src/test/java/com/ning/billing/entitlement/api/billing/TestDefaultEntitlementBillingApi.java 8(+4 -4)
entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java 2(+1 -1)
invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultEmptyInvoiceNotification.java 56(+56 -0)
invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceCreationNotification.java 22(+14 -8)
invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java 2(+1 -1)
server/pom.xml 844(+425 -419)
server/src/test/resources/log4j.xml 3(+3 -0)
Details
diff --git a/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountChangeNotification.java b/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountChangeNotification.java
index 33e1e3a..203a0f4 100644
--- a/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountChangeNotification.java
+++ b/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountChangeNotification.java
@@ -26,14 +26,28 @@ import java.util.List;
import java.util.UUID;
public class DefaultAccountChangeNotification implements AccountChangeNotification {
+
+ private final UUID userToken;
private final List<ChangedField> changedFields;
private final UUID id;
- public DefaultAccountChangeNotification(UUID id, Account oldData, Account newData) {
+ public DefaultAccountChangeNotification(UUID id, UUID userToken, Account oldData, Account newData) {
this.id = id;
+ this.userToken = userToken;
this.changedFields = calculateChangedFields(oldData, newData);
}
+
+ @Override
+ public BusEventType getBusEventType() {
+ return BusEventType.ACCOUNT_CHANGE;
+ }
+
+ @Override
+ public UUID getUserToken() {
+ return userToken;
+ }
+
@Override
public UUID getAccountId() {
return id;
@@ -102,9 +116,4 @@ public class DefaultAccountChangeNotification implements AccountChangeNotificati
inputList.add(new DefaultChangedField(key, oldData, newData));
}
}
-
- @Override
- public BusEventType getBusEventType() {
- return BusEventType.ACCOUNT_CHANGE;
- }
}
diff --git a/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountCreationEvent.java b/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountCreationEvent.java
index 19b76e5..8ce9342 100644
--- a/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountCreationEvent.java
+++ b/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountCreationEvent.java
@@ -24,14 +24,26 @@ import com.ning.billing.util.bus.BusEvent.BusEventType;
import java.util.UUID;
public class DefaultAccountCreationEvent implements AccountCreationNotification {
+
+ private final UUID userToken;
private final UUID id;
private final AccountData data;
- public DefaultAccountCreationEvent(Account data) {
+ public DefaultAccountCreationEvent(Account data, UUID userToken) {
this.id = data.getId();
this.data = data;
+ this.userToken = userToken;
}
+ @Override
+ public BusEventType getBusEventType() {
+ return BusEventType.ACCOUNT_CREATE;
+ }
+
+ @Override
+ public UUID getUserToken() {
+ return userToken;
+ }
@Override
public UUID getId() {
return id;
@@ -41,10 +53,4 @@ public class DefaultAccountCreationEvent implements AccountCreationNotification
public AccountData getData() {
return data;
}
-
- @Override
- public BusEventType getBusEventType() {
- return BusEventType.ACCOUNT_CREATE;
- }
-
}
diff --git a/account/src/main/java/com/ning/billing/account/dao/AuditedAccountDao.java b/account/src/main/java/com/ning/billing/account/dao/AuditedAccountDao.java
index 2d06666..57a9da4 100644
--- a/account/src/main/java/com/ning/billing/account/dao/AuditedAccountDao.java
+++ b/account/src/main/java/com/ning/billing/account/dao/AuditedAccountDao.java
@@ -134,7 +134,7 @@ public class AuditedAccountDao implements AccountDao {
saveTagsFromWithinTransaction(account, transactionalDao, context);
saveCustomFieldsFromWithinTransaction(account, transactionalDao, context);
- AccountCreationNotification creationEvent = new DefaultAccountCreationEvent(account);
+ AccountCreationNotification creationEvent = new DefaultAccountCreationEvent(account, context.getUserToken());
eventBus.post(creationEvent);
return null;
}
@@ -180,7 +180,7 @@ public class AuditedAccountDao implements AccountDao {
saveTagsFromWithinTransaction(account, accountSqlDao, context);
saveCustomFieldsFromWithinTransaction(account, accountSqlDao, context);
- AccountChangeNotification changeEvent = new DefaultAccountChangeNotification(account.getId(), currentAccount, account);
+ AccountChangeNotification changeEvent = new DefaultAccountChangeNotification(account.getId(), context.getUserToken(), currentAccount, account);
if (changeEvent.hasChanges()) {
eventBus.post(changeEvent);
}
diff --git a/account/src/test/java/com/ning/billing/account/dao/MockAccountDao.java b/account/src/test/java/com/ning/billing/account/dao/MockAccountDao.java
index 645124b..c449a50 100644
--- a/account/src/test/java/com/ning/billing/account/dao/MockAccountDao.java
+++ b/account/src/test/java/com/ning/billing/account/dao/MockAccountDao.java
@@ -46,7 +46,7 @@ public class MockAccountDao implements AccountDao {
accounts.put(account.getId().toString(), account);
try {
- eventBus.post(new DefaultAccountCreationEvent(account));
+ eventBus.post(new DefaultAccountCreationEvent(account, null));
}
catch (EventBusException ex) {
throw new RuntimeException(ex);
@@ -87,7 +87,7 @@ public class MockAccountDao implements AccountDao {
public void update(Account account, CallContext context) {
Account currentAccount = accounts.put(account.getId().toString(), account);
- AccountChangeNotification changeEvent = new DefaultAccountChangeNotification(account.getId(), currentAccount, account);
+ AccountChangeNotification changeEvent = new DefaultAccountChangeNotification(account.getId(), null, currentAccount, account);
if (changeEvent.hasChanges()) {
try {
eventBus.post(changeEvent);
diff --git a/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java b/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java
index 99c68d6..56ce8ff 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java
@@ -226,6 +226,7 @@ public class TestAnalyticsService {
phase,
priceList,
1L,
+ null,
true
);
expectedTransition = new BusinessSubscriptionTransition(
@@ -240,7 +241,7 @@ public class TestAnalyticsService {
}
private void createAccountCreationEvent(final Account account) {
- accountCreationNotification = new DefaultAccountCreationEvent(account);
+ accountCreationNotification = new DefaultAccountCreationEvent(account, null);
}
private void createInvoiceAndPaymentCreationEvents(final Account account) {
@@ -257,7 +258,7 @@ public class TestAnalyticsService {
// It doesn't really matter what the events contain - the listener will go back to the db
invoiceCreationNotification = new DefaultInvoiceCreationNotification(invoice.getId(), account.getId(),
- INVOICE_AMOUNT, ACCOUNT_CURRENCY, clock.getUTCNow());
+ INVOICE_AMOUNT, ACCOUNT_CURRENCY, clock.getUTCNow(), null);
paymentInfoNotification = new PaymentInfo.Builder().setPaymentId(UUID.randomUUID().toString()).setPaymentMethod(PAYMENT_METHOD).setCardCountry(CARD_COUNTRY).build();
final PaymentAttempt paymentAttempt = new PaymentAttempt(UUID.randomUUID(), invoice.getId(), account.getId(), BigDecimal.TEN,
diff --git a/analytics/src/test/java/com/ning/billing/analytics/MockSubscription.java b/analytics/src/test/java/com/ning/billing/analytics/MockSubscription.java
index 99e435f..5edd023 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/MockSubscription.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/MockSubscription.java
@@ -53,13 +53,13 @@ public class MockSubscription implements Subscription
}
@Override
- public void cancel(DateTime requestedDate, boolean eot, CallContext context)
+ public boolean cancel(DateTime requestedDate, boolean eot, CallContext context)
{
throw new UnsupportedOperationException();
}
@Override
- public void changePlan(final String productName, final BillingPeriod term, final String planSet, DateTime requestedDate, CallContext context)
+ public boolean changePlan(final String productName, final BillingPeriod term, final String planSet, DateTime requestedDate, CallContext context)
{
throw new UnsupportedOperationException();
}
@@ -112,7 +112,7 @@ public class MockSubscription implements Subscription
@Override
- public void uncancel(CallContext context) throws EntitlementUserApiException
+ public boolean uncancel(CallContext context) throws EntitlementUserApiException
{
throw new UnsupportedOperationException();
}
@@ -154,7 +154,7 @@ public class MockSubscription implements Subscription
}
@Override
- public void recreate(PlanPhaseSpecifier spec, DateTime requestedDate, CallContext context)
+ public boolean recreate(PlanPhaseSpecifier spec, DateTime requestedDate, CallContext context)
throws EntitlementUserApiException {
throw new UnsupportedOperationException();
}
diff --git a/analytics/src/test/java/com/ning/billing/analytics/TestAnalyticsListener.java b/analytics/src/test/java/com/ning/billing/analytics/TestAnalyticsListener.java
index 7254a5d..be061f9 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/TestAnalyticsListener.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/TestAnalyticsListener.java
@@ -161,6 +161,7 @@ public class TestAnalyticsListener
phase,
priceList,
1L,
+ null,
true
);
}
@@ -187,6 +188,7 @@ public class TestAnalyticsListener
null,
null,
1L,
+ null,
true
);
}
@@ -212,6 +214,7 @@ public class TestAnalyticsListener
phase,
priceList,
1L,
+ null,
true
);
}
@@ -242,6 +245,7 @@ public class TestAnalyticsListener
phase,
priceList,
1L,
+ null,
true
);
}
diff --git a/api/src/main/java/com/ning/billing/account/api/AccountCreationNotification.java b/api/src/main/java/com/ning/billing/account/api/AccountCreationNotification.java
index b30d290..157a96c 100644
--- a/api/src/main/java/com/ning/billing/account/api/AccountCreationNotification.java
+++ b/api/src/main/java/com/ning/billing/account/api/AccountCreationNotification.java
@@ -21,7 +21,7 @@ import com.ning.billing.util.bus.BusEvent;
import java.util.UUID;
public interface AccountCreationNotification extends BusEvent {
+
public UUID getId();
-
public AccountData getData();
}
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java b/api/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java
index 0854e2f..ec94f9d 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java
@@ -31,16 +31,16 @@ import java.util.UUID;
public interface Subscription extends ExtendedEntity {
- public void cancel(DateTime requestedDate, boolean eot, CallContext context)
+ public boolean cancel(DateTime requestedDate, boolean eot, CallContext context)
throws EntitlementUserApiException;
- public void uncancel(CallContext context)
+ public boolean uncancel(CallContext context)
throws EntitlementUserApiException;
- public void changePlan(String productName, BillingPeriod term, String planSet, DateTime requestedDate, CallContext context)
+ public boolean changePlan(String productName, BillingPeriod term, String planSet, DateTime requestedDate, CallContext context)
throws EntitlementUserApiException;
- public void recreate(PlanPhaseSpecifier spec, DateTime requestedDate, CallContext context)
+ public boolean recreate(PlanPhaseSpecifier spec, DateTime requestedDate, CallContext context)
throws EntitlementUserApiException;
public enum SubscriptionState {
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransition.java b/api/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransition.java
index 80b836a..cd976c9 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransition.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransition.java
@@ -64,4 +64,6 @@ public interface SubscriptionTransition extends BusEvent {
SubscriptionState getNextState();
String getNextPriceList();
+
+ Integer getRemainingEventsForUserOperation();
}
diff --git a/api/src/main/java/com/ning/billing/invoice/api/EmptyInvoiceNotification.java b/api/src/main/java/com/ning/billing/invoice/api/EmptyInvoiceNotification.java
new file mode 100644
index 0000000..b62da0d
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/invoice/api/EmptyInvoiceNotification.java
@@ -0,0 +1,22 @@
+/*
+ * 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.invoice.api;
+
+import com.ning.billing.util.bus.BusEvent;
+
+public interface EmptyInvoiceNotification extends BusEvent {
+
+}
diff --git a/api/src/main/java/com/ning/billing/payment/api/PaymentError.java b/api/src/main/java/com/ning/billing/payment/api/PaymentError.java
index d546e3f..31e2594 100644
--- a/api/src/main/java/com/ning/billing/payment/api/PaymentError.java
+++ b/api/src/main/java/com/ning/billing/payment/api/PaymentError.java
@@ -26,32 +26,45 @@ import com.ning.billing.util.bus.BusEvent.BusEventType;
@JsonTypeInfo(use = Id.NAME, property = "error")
public class PaymentError implements BusEvent {
+
private final String type;
private final String message;
private final UUID accountId;
private final UUID invoiceId;
+ private final UUID userToken;
- public PaymentError(PaymentError src, UUID accountId, UUID invoiceId) {
+ public PaymentError(final PaymentError src, final UUID accountId, final UUID invoiceId) {
this.type = src.type;
this.message = src.message;
this.accountId = accountId;
this.invoiceId = invoiceId;
+ this.userToken = src.userToken;
}
- public PaymentError(String type, String message, UUID accountId, UUID invoiceId) {
+ public PaymentError(String type, String message, UUID accountId, UUID invoiceId, UUID userToken) {
this.type = type;
this.message = message;
this.accountId = accountId;
this.invoiceId = invoiceId;
+ this.userToken = userToken;
}
public PaymentError(String type, String message) {
- this.type = type;
- this.message = message;
- this.accountId = null;
- this.invoiceId = null;
+ this(type, message, null, null, null);
+ }
+
+ @JsonIgnore
+ @Override
+ public BusEventType getBusEventType() {
+ return BusEventType.PAYMENT_ERROR;
+ }
+
+ @Override
+ public UUID getUserToken() {
+ return userToken;
}
+
public String getType() {
return type;
}
@@ -119,12 +132,4 @@ public class PaymentError implements BusEvent {
public String toString() {
return "PaymentError [type=" + type + ", message=" + message + ", accountId=" + accountId + ", invoiceId=" + invoiceId + "]";
}
-
- @JsonIgnore
- @Override
- public BusEventType getBusEventType() {
- return BusEventType.PAYMENT_ERROR;
- }
-
-
}
diff --git a/api/src/main/java/com/ning/billing/payment/api/PaymentInfo.java b/api/src/main/java/com/ning/billing/payment/api/PaymentInfo.java
index ad0c516..ad62544 100644
--- a/api/src/main/java/com/ning/billing/payment/api/PaymentInfo.java
+++ b/api/src/main/java/com/ning/billing/payment/api/PaymentInfo.java
@@ -17,6 +17,7 @@
package com.ning.billing.payment.api;
import java.math.BigDecimal;
+import java.util.UUID;
import org.codehaus.jackson.annotate.JsonCreator;
import org.codehaus.jackson.annotate.JsonIgnore;
@@ -29,6 +30,8 @@ import com.ning.billing.util.bus.BusEvent;
import com.ning.billing.util.bus.BusEvent.BusEventType;
public class PaymentInfo implements BusEvent {
+
+
private final String paymentId;
private final BigDecimal amount;
private final BigDecimal refundAmount;
@@ -41,6 +44,7 @@ public class PaymentInfo implements BusEvent {
private final String paymentMethod;
private final String cardType;
private final String cardCountry;
+ private final UUID userToken;
private final DateTime effectiveDate;
private final DateTime createdDate;
private final DateTime updatedDate;
@@ -58,6 +62,7 @@ public class PaymentInfo implements BusEvent {
@JsonProperty("paymentMethod") String paymentMethod,
@JsonProperty("cardType") String cardType,
@JsonProperty("cardCountry") String cardCountry,
+ @JsonProperty("userToken") UUID userToken,
@JsonProperty("effectiveDate") DateTime effectiveDate,
@JsonProperty("createdDate") DateTime createdDate,
@JsonProperty("updatedDate") DateTime updatedDate) {
@@ -73,6 +78,7 @@ public class PaymentInfo implements BusEvent {
this.paymentMethod = paymentMethod;
this.cardType = cardType;
this.cardCountry = cardCountry;
+ this.userToken = userToken;
this.effectiveDate = effectiveDate;
this.createdDate = createdDate == null ? new DateTime(DateTimeZone.UTC) : createdDate;
this.updatedDate = updatedDate == null ? new DateTime(DateTimeZone.UTC) : updatedDate;
@@ -91,10 +97,22 @@ public class PaymentInfo implements BusEvent {
src.paymentMethod,
src.cardType,
src.cardCountry,
+ src.userToken,
src.effectiveDate,
src.createdDate,
src.updatedDate);
}
+
+ @JsonIgnore
+ @Override
+ public BusEventType getBusEventType() {
+ return BusEventType.PAYMENT_INFO;
+ }
+
+ @Override
+ public UUID getUserToken() {
+ return userToken;
+ }
public Builder cloner() {
return new Builder(this);
@@ -173,6 +191,7 @@ public class PaymentInfo implements BusEvent {
private String paymentMethod;
private String cardType;
private String cardCountry;
+ private UUID userToken;
private DateTime effectiveDate;
private DateTime createdDate;
private DateTime updatedDate;
@@ -194,6 +213,7 @@ public class PaymentInfo implements BusEvent {
this.paymentMethod = src.paymentMethod;
this.cardType = src.cardType;
this.cardCountry = src.cardCountry;
+ this.userToken = src.userToken;
this.createdDate = src.createdDate;
this.updatedDate = src.updatedDate;
}
@@ -213,6 +233,11 @@ public class PaymentInfo implements BusEvent {
return this;
}
+ public Builder setUserToken(UUID userToken) {
+ this.userToken = userToken;
+ return this;
+ }
+
public Builder setCreatedDate(DateTime createdDate) {
this.createdDate = createdDate;
return this;
@@ -286,6 +311,7 @@ public class PaymentInfo implements BusEvent {
paymentMethod,
cardType,
cardCountry,
+ userToken,
effectiveDate,
createdDate,
updatedDate);
@@ -305,7 +331,7 @@ public class PaymentInfo implements BusEvent {
paymentMethodId,
paymentMethod,
cardType,
- cardCountry,
+ cardCountry,
effectiveDate,
createdDate,
updatedDate);
@@ -350,11 +376,4 @@ public class PaymentInfo implements BusEvent {
private static long getUnixTimestamp(final DateTime dateTime) {
return dateTime.getMillis() / 1000;
}
-
- @JsonIgnore
- @Override
- public BusEventType getBusEventType() {
- return BusEventType.PAYMENT_INFO;
- }
-
}
diff --git a/api/src/main/java/com/ning/billing/util/bus/BusEvent.java b/api/src/main/java/com/ning/billing/util/bus/BusEvent.java
index ef5bc04..d44e1b5 100644
--- a/api/src/main/java/com/ning/billing/util/bus/BusEvent.java
+++ b/api/src/main/java/com/ning/billing/util/bus/BusEvent.java
@@ -16,17 +16,21 @@
package com.ning.billing.util.bus;
+import java.util.UUID;
+
public interface BusEvent {
public enum BusEventType {
ACCOUNT_CREATE,
ACCOUNT_CHANGE,
SUBSCRIPTION_TRANSITION,
+ INVOICE_EMPTY,
INVOICE_CREATION,
PAYMENT_INFO,
PAYMENT_ERROR
}
public BusEventType getBusEventType();
-
+
+ public UUID getUserToken();
}
diff --git a/api/src/main/java/com/ning/billing/util/callcontext/CallContext.java b/api/src/main/java/com/ning/billing/util/callcontext/CallContext.java
index da36f10..8be4760 100644
--- a/api/src/main/java/com/ning/billing/util/callcontext/CallContext.java
+++ b/api/src/main/java/com/ning/billing/util/callcontext/CallContext.java
@@ -16,9 +16,12 @@
package com.ning.billing.util.callcontext;
+import java.util.UUID;
+
import org.joda.time.DateTime;
public interface CallContext {
+ public UUID getUserToken();
public String getUserName();
public CallOrigin getCallOrigin();
public UserType getUserType();
diff --git a/api/src/main/java/com/ning/billing/util/userrequest/CompletionUserRequestWaiter.java b/api/src/main/java/com/ning/billing/util/userrequest/CompletionUserRequestWaiter.java
index 7bfb6bc..c80034c 100644
--- a/api/src/main/java/com/ning/billing/util/userrequest/CompletionUserRequestWaiter.java
+++ b/api/src/main/java/com/ning/billing/util/userrequest/CompletionUserRequestWaiter.java
@@ -22,6 +22,7 @@ import java.util.concurrent.TimeoutException;
import com.ning.billing.account.api.AccountChangeNotification;
import com.ning.billing.account.api.AccountCreationNotification;
import com.ning.billing.entitlement.api.user.SubscriptionTransition;
+import com.ning.billing.invoice.api.EmptyInvoiceNotification;
import com.ning.billing.invoice.api.InvoiceCreationNotification;
import com.ning.billing.payment.api.PaymentError;
import com.ning.billing.payment.api.PaymentInfo;
@@ -38,6 +39,8 @@ public interface CompletionUserRequestWaiter {
public void onSubscriptionTransition(final SubscriptionTransition curEvent);
public void onInvoiceCreation(final InvoiceCreationNotification curEvent);
+
+ public void onEmptyInvoice(final EmptyInvoiceNotification curEvent);
public void onPaymentInfo(final PaymentInfo curEvent);
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/DefaultEntitlementMigrationApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/DefaultEntitlementMigrationApi.java
index d0fb35b..8390504 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/DefaultEntitlementMigrationApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/DefaultEntitlementMigrationApi.java
@@ -72,11 +72,11 @@ public class DefaultEntitlementMigrationApi implements EntitlementMigrationApi {
@Override
public void migrate(EntitlementAccountMigration toBeMigrated, CallContext context)
throws EntitlementMigrationApiException {
- AccountMigrationData accountMigrationData = createAccountMigrationData(toBeMigrated);
+ AccountMigrationData accountMigrationData = createAccountMigrationData(toBeMigrated, context);
dao.migrate(toBeMigrated.getAccountKey(), accountMigrationData, context);
}
- private AccountMigrationData createAccountMigrationData(EntitlementAccountMigration toBeMigrated)
+ private AccountMigrationData createAccountMigrationData(EntitlementAccountMigration toBeMigrated, CallContext context)
throws EntitlementMigrationApiException {
final UUID accountId = toBeMigrated.getAccountKey();
@@ -118,10 +118,11 @@ public class DefaultEntitlementMigrationApi implements EntitlementMigrationApi {
for (EntitlementSubscriptionMigration curSub : sortedSubscriptions) {
SubscriptionMigrationData data = null;
if (bundleStartDate == null) {
- data = createInitialSubscription(bundleData.getId(), curSub.getCategory(), curSub.getSubscriptionCases(), now, curSub.getChargedThroughDate());
+ data = createInitialSubscription(bundleData.getId(), curSub.getCategory(), curSub.getSubscriptionCases(), now, curSub.getChargedThroughDate(), context);
bundleStartDate = data.getInitialEvents().get(0).getEffectiveDate();
} else {
- data = createSubscriptionMigrationDataWithBundleDate(bundleData.getId(), curSub.getCategory(), curSub.getSubscriptionCases(), now, bundleStartDate, curSub.getChargedThroughDate());
+ data = createSubscriptionMigrationDataWithBundleDate(bundleData.getId(), curSub.getCategory(), curSub.getSubscriptionCases(), now,
+ bundleStartDate, curSub.getChargedThroughDate(), context);
}
if (data != null) {
bundleSubscriptionData.add(data);
@@ -135,7 +136,7 @@ public class DefaultEntitlementMigrationApi implements EntitlementMigrationApi {
}
private SubscriptionMigrationData createInitialSubscription(UUID bundleId, ProductCategory productCategory,
- EntitlementSubscriptionMigrationCase [] input, DateTime now, DateTime ctd)
+ EntitlementSubscriptionMigrationCase [] input, DateTime now, DateTime ctd, CallContext context)
throws EntitlementMigrationApiException {
TimedMigration [] events = migrationAligner.getEventsMigration(input, now);
@@ -148,11 +149,11 @@ public class DefaultEntitlementMigrationApi implements EntitlementMigrationApi {
.setBundleStartDate(migrationStartDate)
.setStartDate(migrationStartDate),
emptyEvents);
- return new SubscriptionMigrationData(subscriptionData, toEvents(subscriptionData, now, ctd, events));
+ return new SubscriptionMigrationData(subscriptionData, toEvents(subscriptionData, now, ctd, events, context));
}
private SubscriptionMigrationData createSubscriptionMigrationDataWithBundleDate(UUID bundleId, ProductCategory productCategory,
- EntitlementSubscriptionMigrationCase [] input, DateTime now, DateTime bundleStartDate, DateTime ctd)
+ EntitlementSubscriptionMigrationCase [] input, DateTime now, DateTime bundleStartDate, DateTime ctd, CallContext context)
throws EntitlementMigrationApiException {
TimedMigration [] events = migrationAligner.getEventsMigration(input, now);
DateTime migrationStartDate= events[0].getEventTime();
@@ -164,10 +165,10 @@ public class DefaultEntitlementMigrationApi implements EntitlementMigrationApi {
.setBundleStartDate(bundleStartDate)
.setStartDate(migrationStartDate),
emptyEvents);
- return new SubscriptionMigrationData(subscriptionData, toEvents(subscriptionData, now, ctd, events));
+ return new SubscriptionMigrationData(subscriptionData, toEvents(subscriptionData, now, ctd, events, context));
}
- private List<EntitlementEvent> toEvents(SubscriptionData subscriptionData, DateTime now, DateTime ctd, TimedMigration [] migrationEvents) {
+ private List<EntitlementEvent> toEvents(SubscriptionData subscriptionData, DateTime now, DateTime ctd, TimedMigration [] migrationEvents, CallContext context) {
ApiEventMigrateEntitlement creationEvent = null;
@@ -189,6 +190,7 @@ public class DefaultEntitlementMigrationApi implements EntitlementMigrationApi {
.setEffectiveDate(cur.getEventTime())
.setProcessedDate(now)
.setRequestedDate(now)
+ .setUserToken(context.getUserToken())
.setFromDisk(true);
switch(cur.getApiEventType()) {
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionApiService.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionApiService.java
index 39efb09..ffeebea 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionApiService.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionApiService.java
@@ -55,15 +55,15 @@ public class SubscriptionApiService {
public SubscriptionData createPlan(SubscriptionBuilder builder, Plan plan, PhaseType initialPhase,
String realPriceList, DateTime requestedDate, DateTime effectiveDate, DateTime processedDate,
CallContext context)
- throws EntitlementUserApiException {
+ throws EntitlementUserApiException {
SubscriptionData subscription = new SubscriptionData(builder, this, clock);
createFromSubscription(subscription, plan, initialPhase, realPriceList, requestedDate, effectiveDate, processedDate, false, context);
return subscription;
}
- public void recreatePlan(SubscriptionData subscription, PlanPhaseSpecifier spec, DateTime requestedDate, CallContext context)
- throws EntitlementUserApiException {
+ public boolean recreatePlan(SubscriptionData subscription, PlanPhaseSpecifier spec, DateTime requestedDate, CallContext context)
+ throws EntitlementUserApiException {
SubscriptionState currentState = subscription.getState();
if (currentState != SubscriptionState.CANCELLED) {
@@ -86,6 +86,7 @@ public class SubscriptionApiService {
DateTime processedDate = now;
createFromSubscription(subscription, plan, spec.getPhaseType(), realPriceList, requestedDate, effectiveDate, processedDate, true, context);
+ return true;
} catch (CatalogApiException e) {
throw new EntitlementUserApiException(e);
}
@@ -94,7 +95,7 @@ public class SubscriptionApiService {
private void createFromSubscription(SubscriptionData subscription, Plan plan, PhaseType initialPhase,
String realPriceList, DateTime requestedDate, DateTime effectiveDate, DateTime processedDate,
boolean reCreate, CallContext context)
- throws EntitlementUserApiException {
+ throws EntitlementUserApiException {
try {
TimedPhase [] curAndNextPhases = planAligner.getCurrentAndNextTimedPhaseOnCreate(subscription, plan, initialPhase, realPriceList, requestedDate, effectiveDate);
@@ -108,6 +109,7 @@ public class SubscriptionApiService {
.setProcessedDate(processedDate)
.setEffectiveDate(effectiveDate)
.setRequestedDate(requestedDate)
+ .setUserToken(context.getUserToken())
.setFromDisk(true);
ApiEvent creationEvent = (reCreate) ? new ApiEventReCreate(createBuilder) : new ApiEventCreate(createBuilder);
@@ -115,24 +117,24 @@ public class SubscriptionApiService {
PhaseEvent nextPhaseEvent = (nextTimedPhase != null) ?
PhaseEventData.createNextPhaseEvent(nextTimedPhase.getPhase().getName(), subscription, processedDate, nextTimedPhase.getStartPhase()) :
null;
- List<EntitlementEvent> events = new ArrayList<EntitlementEvent>();
- events.add(creationEvent);
- if (nextPhaseEvent != null) {
- events.add(nextPhaseEvent);
- }
- if (reCreate) {
- dao.recreateSubscription(subscription.getId(), events, context);
- } else {
- dao.createSubscription(subscription, events, context);
- }
- subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId()), catalogService.getFullCatalog());
+ List<EntitlementEvent> events = new ArrayList<EntitlementEvent>();
+ events.add(creationEvent);
+ if (nextPhaseEvent != null) {
+ events.add(nextPhaseEvent);
+ }
+ if (reCreate) {
+ dao.recreateSubscription(subscription.getId(), events, context);
+ } else {
+ dao.createSubscription(subscription, events, context);
+ }
+ subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId()), catalogService.getFullCatalog());
} catch (CatalogApiException e) {
throw new EntitlementUserApiException(e);
}
}
- public void cancel(SubscriptionData subscription, DateTime requestedDate, boolean eot, CallContext context)
- throws EntitlementUserApiException {
+ public boolean cancel(SubscriptionData subscription, DateTime requestedDate, boolean eot, CallContext context)
+ throws EntitlementUserApiException {
try {
SubscriptionState currentState = subscription.getState();
@@ -151,8 +153,7 @@ public class SubscriptionApiService {
subscription.getCurrentPriceList(),
subscription.getCurrentPhase().getPhaseType());
- ActionPolicy policy = null;
- policy = catalogService.getFullCatalog().planCancelPolicy(planPhase, requestedDate);
+ ActionPolicy policy = catalogService.getFullCatalog().planCancelPolicy(planPhase, requestedDate);
DateTime effectiveDate = subscription.getPlanChangeEffectiveDate(policy, requestedDate);
EntitlementEvent cancelEvent = new ApiEventCancel(new ApiEventBuilder()
@@ -161,18 +162,20 @@ public class SubscriptionApiService {
.setProcessedDate(now)
.setEffectiveDate(effectiveDate)
.setRequestedDate(requestedDate)
+ .setUserToken(context.getUserToken())
.setFromDisk(true));
- dao.cancelSubscription(subscription.getId(), cancelEvent, context);
+ dao.cancelSubscription(subscription.getId(), cancelEvent, context, 0);
subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId()), catalogService.getFullCatalog());
+ return (policy == ActionPolicy.IMMEDIATE);
} catch (CatalogApiException e) {
throw new EntitlementUserApiException(e);
}
}
- public void uncancel(SubscriptionData subscription, CallContext context)
- throws EntitlementUserApiException {
+ public boolean uncancel(SubscriptionData subscription, CallContext context)
+ throws EntitlementUserApiException {
if (!subscription.isSubscriptionFutureCancelled()) {
throw new EntitlementUserApiException(ErrorCode.ENT_UNCANCEL_BAD_STATE, subscription.getId().toString());
@@ -185,6 +188,7 @@ public class SubscriptionApiService {
.setProcessedDate(now)
.setRequestedDate(now)
.setEffectiveDate(now)
+ .setUserToken(context.getUserToken())
.setFromDisk(true));
List<EntitlementEvent> uncancelEvents = new ArrayList<EntitlementEvent>();
@@ -194,16 +198,17 @@ public class SubscriptionApiService {
PhaseEvent nextPhaseEvent = (nextTimedPhase != null) ?
PhaseEventData.createNextPhaseEvent(nextTimedPhase.getPhase().getName(), subscription, now, nextTimedPhase.getStartPhase()) :
null;
- if (nextPhaseEvent != null) {
- uncancelEvents.add(nextPhaseEvent);
- }
- dao.uncancelSubscription(subscription.getId(), uncancelEvents, context);
- subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId()), catalogService.getFullCatalog());
+ if (nextPhaseEvent != null) {
+ uncancelEvents.add(nextPhaseEvent);
+ }
+ dao.uncancelSubscription(subscription.getId(), uncancelEvents, context);
+ subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId()), catalogService.getFullCatalog());
+ return true;
}
- public void changePlan(SubscriptionData subscription, String productName, BillingPeriod term,
+ public boolean changePlan(SubscriptionData subscription, String productName, BillingPeriod term,
String priceList, DateTime requestedDate, CallContext context)
- throws EntitlementUserApiException {
+ throws EntitlementUserApiException {
try {
@@ -257,20 +262,22 @@ public class SubscriptionApiService {
.setProcessedDate(now)
.setEffectiveDate(effectiveDate)
.setRequestedDate(requestedDate)
+ .setUserToken(context.getUserToken())
.setFromDisk(true));
TimedPhase nextTimedPhase = planAligner.getNextTimedPhaseOnChange(subscription, newPlan, newPriceList.getName(), requestedDate, effectiveDate);
PhaseEvent nextPhaseEvent = (nextTimedPhase != null) ?
PhaseEventData.createNextPhaseEvent(nextTimedPhase.getPhase().getName(), subscription, now, nextTimedPhase.getStartPhase()) :
null;
- List<EntitlementEvent> changeEvents = new ArrayList<EntitlementEvent>();
- // Only add the PHASE if it does not coincide with the CHANGE, if not this is 'just' a CHANGE.
- if (nextPhaseEvent != null && ! nextPhaseEvent.getEffectiveDate().equals(changeEvent.getEffectiveDate())) {
- changeEvents.add(nextPhaseEvent);
- }
- changeEvents.add(changeEvent);
- dao.changePlan(subscription.getId(), changeEvents, context);
- subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId()), catalogService.getFullCatalog());
+ List<EntitlementEvent> changeEvents = new ArrayList<EntitlementEvent>();
+ // Only add the PHASE if it does not coincide with the CHANGE, if not this is 'just' a CHANGE.
+ if (nextPhaseEvent != null && ! nextPhaseEvent.getEffectiveDate().equals(changeEvent.getEffectiveDate())) {
+ changeEvents.add(nextPhaseEvent);
+ }
+ changeEvents.add(changeEvent);
+ dao.changePlan(subscription.getId(), changeEvents, context);
+ subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId()), catalogService.getFullCatalog());
+ return (policy == ActionPolicy.IMMEDIATE);
} catch (CatalogApiException e) {
throw new EntitlementUserApiException(e);
}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java
index a67c502..7561cfb 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java
@@ -164,25 +164,25 @@ public class SubscriptionData extends ExtendedEntityBase implements Subscription
@Override
- public void cancel(DateTime requestedDate, boolean eot, CallContext context) throws EntitlementUserApiException {
- apiService.cancel(this, requestedDate, eot, context);
+ public boolean cancel(DateTime requestedDate, boolean eot, CallContext context) throws EntitlementUserApiException {
+ return apiService.cancel(this, requestedDate, eot, context);
}
@Override
- public void uncancel(CallContext context) throws EntitlementUserApiException {
- apiService.uncancel(this, context);
+ public boolean uncancel(CallContext context) throws EntitlementUserApiException {
+ return apiService.uncancel(this, context);
}
@Override
- public void changePlan(String productName, BillingPeriod term,
+ public boolean changePlan(String productName, BillingPeriod term,
String priceList, DateTime requestedDate, CallContext context) throws EntitlementUserApiException {
- apiService.changePlan(this, productName, term, priceList, requestedDate, context);
+ return apiService.changePlan(this, productName, term, priceList, requestedDate, context);
}
@Override
- public void recreate(PlanPhaseSpecifier spec, DateTime requestedDate, CallContext context)
+ public boolean recreate(PlanPhaseSpecifier spec, DateTime requestedDate, CallContext context)
throws EntitlementUserApiException {
- apiService.recreatePlan(this, spec, requestedDate, context);
+ return apiService.recreatePlan(this, spec, requestedDate, context);
}
public List<SubscriptionTransition> getBillingTransitions() {
@@ -220,13 +220,13 @@ public class SubscriptionData extends ExtendedEntityBase implements Subscription
return it.hasNext() ? it.next() : null;
}
- public SubscriptionTransition getTransitionFromEvent(EntitlementEvent event) {
+ public SubscriptionTransition getTransitionFromEvent(final EntitlementEvent event, final int seqId) {
if (transitions == null || event == null) {
return null;
}
for (SubscriptionTransition cur : transitions) {
if (cur.getId().equals(event.getId())) {
- return cur;
+ return new SubscriptionTransitionData((SubscriptionTransitionData) cur, seqId);
}
}
return null;
@@ -337,7 +337,8 @@ public class SubscriptionData extends ExtendedEntityBase implements Subscription
String nextPlanName = null;
String nextPhaseName = null;
String nextPriceList = null;
-
+ UUID nextUserToken = null;
+
SubscriptionState previousState = null;
String previousPriceList = null;
@@ -366,6 +367,8 @@ public class SubscriptionData extends ExtendedEntityBase implements Subscription
ApiEvent userEV = (ApiEvent) cur;
apiEventType = userEV.getEventType();
isFromDisk = userEV.isFromDisk();
+ nextUserToken = userEV.getUserToken();
+
switch(apiEventType) {
case MIGRATE_BILLING:
case MIGRATE_ENTITLEMENT:
@@ -428,7 +431,9 @@ public class SubscriptionData extends ExtendedEntityBase implements Subscription
nextPhase,
nextPriceList,
cur.getTotalOrdering(),
+ nextUserToken,
isFromDisk);
+
transitions.add(transition);
previousState = nextState;
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransitionData.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransitionData.java
index 18a1471..f455f60 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransitionData.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransitionData.java
@@ -48,12 +48,14 @@ public class SubscriptionTransitionData implements SubscriptionTransition {
private final Plan nextPlan;
private final PlanPhase nextPhase;
private final boolean isFromDisk;
+ private final int remainingEventsForUserOperation;
+ private final UUID userToken;
public SubscriptionTransitionData(UUID eventId, UUID subscriptionId, UUID bundleId, EventType eventType,
ApiEventType apiEventType, DateTime requestedTransitionTime, DateTime effectiveTransitionTime,
SubscriptionState previousState, Plan previousPlan, PlanPhase previousPhase, String previousPriceList,
SubscriptionState nextState, Plan nextPlan, PlanPhase nextPhase, String nextPriceList,
- long totalOrdering, boolean isFromDisk) {
+ long totalOrdering, UUID userToken, boolean isFromDisk) {
super();
this.eventId = eventId;
this.subscriptionId = subscriptionId;
@@ -72,8 +74,39 @@ public class SubscriptionTransitionData implements SubscriptionTransition {
this.nextPhase = nextPhase;
this.totalOrdering = totalOrdering;
this.isFromDisk = isFromDisk;
+ this.userToken = userToken;
+ this.remainingEventsForUserOperation = 0;
+ }
+
+ public SubscriptionTransitionData(final SubscriptionTransitionData input, final int remainingEventsForUserOperation) {
+ super();
+ this.eventId = input.getId();
+ this.subscriptionId = input.getSubscriptionId();
+ this.bundleId = input.getBundleId();
+ this.eventType = input.getEventType();
+ this.apiEventType = input.getApiEventType();
+ this.requestedTransitionTime = input.getRequestedTransitionTime();
+ this.effectiveTransitionTime = input.getEffectiveTransitionTime();
+ this.previousState = input.getPreviousState();
+ this.previousPriceList = input.getPreviousPriceList();
+ this.previousPlan = input.getPreviousPlan();
+ this.previousPhase = input.getPreviousPhase();
+ this.nextState = input.getNextState();
+ this.nextPlan = input.getNextPlan();
+ this.nextPriceList = input.getNextPriceList();
+ this.nextPhase = input.getNextPhase();
+ this.totalOrdering = input.getTotalOrdering();
+ this.isFromDisk = input.isFromDisk();
+ this.userToken = input.getUserToken();
+ this.remainingEventsForUserOperation = remainingEventsForUserOperation;
}
+ @Override
+ public BusEventType getBusEventType() {
+ return BusEventType.SUBSCRIPTION_TRANSITION;
+ }
+
+
@Override
public UUID getId() {
return eventId;
@@ -130,6 +163,17 @@ public class SubscriptionTransitionData implements SubscriptionTransition {
public String getNextPriceList() {
return nextPriceList;
}
+
+ @Override
+ public UUID getUserToken() {
+ return userToken;
+ }
+
+ @Override
+ public Integer getRemainingEventsForUserOperation() {
+ return remainingEventsForUserOperation;
+ }
+
@Override
public SubscriptionTransitionType getTransitionType() {
@@ -187,9 +231,4 @@ public class SubscriptionTransitionData implements SubscriptionTransition {
+ ", nextPriceList " + nextPriceList
+ ", nextPhase=" + ((nextPhase != null) ? nextPhase.getName() : null) + "]";
}
-
- @Override
- public BusEventType getBusEventType() {
- return BusEventType.SUBSCRIPTION_TRANSITION;
- }
}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java
index fbecb9b..2984ac3 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java
@@ -18,9 +18,13 @@ package com.ning.billing.entitlement.engine.core;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.UUID;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import com.ning.billing.util.callcontext.CallContext;
@@ -73,6 +77,7 @@ import com.ning.billing.util.notificationq.NotificationQueueService.Notification
public class Engine implements EventListener, EntitlementService {
+
public static final String NOTIFICATION_QUEUE_NAME = "subscription-events";
public static final String ENTITLEMENT_SERVICE_NAME = "entitlement-service";
@@ -129,15 +134,21 @@ public class Engine implements EventListener, EntitlementService {
NOTIFICATION_QUEUE_NAME,
new NotificationQueueHandler() {
@Override
- public void handleReadyNotification(String notificationKey, DateTime eventDateTime) {
- EntitlementEvent event = dao.getEventById(UUID.fromString(notificationKey));
+ public void handleReadyNotification(final String inputKey, final DateTime eventDateTime) {
+
+ EntitlementNotificationKey key = new EntitlementNotificationKey(inputKey);
+ final EntitlementEvent event = dao.getEventById(key.getEventId());
if (event == null) {
- log.warn("Failed to extract event for notification key {}", notificationKey);
- } else {
- final CallContext context = factory.createCallContext("SubscriptionEventQueue", CallOrigin.INTERNAL, UserType.SYSTEM);
- processEventReady(event, context);
+ log.warn("Failed to extract event for notification key {}", inputKey);
+ return;
}
+ final UUID userToken = (event.getType() == EventType.API_USER) ? ((ApiEvent) event).getUserToken() : null;
+ final CallContext context = factory.createCallContext("SubscriptionEventQueue", CallOrigin.INTERNAL, UserType.SYSTEM, userToken);
+ processEventReady(event, key.getSeqId(), context);
}
+
+
+
},
new NotificationConfig() {
@Override
@@ -192,7 +203,7 @@ public class Engine implements EventListener, EntitlementService {
@Override
- public void processEventReady(EntitlementEvent event, CallContext context) {
+ public void processEventReady(final EntitlementEvent event, final int seqId, final CallContext context) {
if (!event.isActive()) {
return;
}
@@ -204,14 +215,16 @@ public class Engine implements EventListener, EntitlementService {
//
// Do any internal processing on that event before we send the event to the bus
//
+
+ int theRealSeqId = seqId;
if (event.getType() == EventType.PHASE) {
onPhaseEvent(subscription, context);
} else if (event.getType() == EventType.API_USER &&
subscription.getCategory() == ProductCategory.BASE) {
- onBasePlanEvent(subscription, (ApiEvent) event, context);
+ theRealSeqId = onBasePlanEvent(subscription, (ApiEvent) event, context);
}
try {
- eventBus.post(subscription.getTransitionFromEvent(event));
+ eventBus.post(subscription.getTransitionFromEvent(event, theRealSeqId));
} catch (EventBusException e) {
log.warn("Failed to post entitlement event " + event, e);
}
@@ -233,7 +246,7 @@ public class Engine implements EventListener, EntitlementService {
}
}
- private void onBasePlanEvent(SubscriptionData baseSubscription, ApiEvent event, CallContext context) {
+ private int onBasePlanEvent(SubscriptionData baseSubscription, ApiEvent event, CallContext context) {
DateTime now = clock.getUTCNow();
@@ -242,6 +255,9 @@ public class Engine implements EventListener, EntitlementService {
List<Subscription> subscriptions = dao.getSubscriptions(subscriptionFactory, baseSubscription.getBundleId());
+
+ Map<UUID, EntitlementEvent> addOnCancellations = new HashMap<UUID, EntitlementEvent>();
+
Iterator<Subscription> it = subscriptions.iterator();
while (it.hasNext()) {
SubscriptionData cur = (SubscriptionData) it.next();
@@ -262,9 +278,18 @@ public class Engine implements EventListener, EntitlementService {
.setProcessedDate(now)
.setEffectiveDate(event.getEffectiveDate())
.setRequestedDate(now)
+ .setUserToken(context.getUserToken())
.setFromDisk(true));
- dao.cancelSubscription(cur.getId(), cancelEvent, context);
+
+ addOnCancellations.put(cur.getId(), cancelEvent);
}
}
+ final int addOnSize = addOnCancellations.size();
+ int cancelSeq = addOnSize - 1;
+ for (final UUID key : addOnCancellations.keySet()) {
+ dao.cancelSubscription(key, addOnCancellations.get(key), context, cancelSeq);
+ cancelSeq--;
+ }
+ return addOnSize;
}
}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/EventListener.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/EventListener.java
index f6b13f9..1272dba 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/EventListener.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/EventListener.java
@@ -22,6 +22,6 @@ import com.ning.billing.util.callcontext.CallContext;
public interface EventListener {
- public void processEventReady(EntitlementEvent event, CallContext context);
+ public void processEventReady(final EntitlementEvent event, final int seqId, final CallContext context);
}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java
index d79444a..4415c1d 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java
@@ -68,8 +68,8 @@ public interface EntitlementDao {
public void recreateSubscription(final UUID subscriptionId, final List<EntitlementEvent> recreateEvents, final CallContext context);
- public void cancelSubscription(final UUID subscriptionId, final EntitlementEvent cancelEvent, final CallContext context);
-
+ public void cancelSubscription(final UUID subscriptionId, final EntitlementEvent cancelEvent, final CallContext context, final int cancelSeq);
+
public void uncancelSubscription(final UUID subscriptionId, final List<EntitlementEvent> uncancelEvents, final CallContext context);
public void changePlan(final UUID subscriptionId, final List<EntitlementEvent> changeEvents, final CallContext context);
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java
index b0f1147..2dd8a8b 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java
@@ -53,6 +53,7 @@ import com.ning.billing.entitlement.api.user.SubscriptionFactory;
import com.ning.billing.entitlement.api.user.SubscriptionFactory.SubscriptionBuilder;
import com.ning.billing.entitlement.engine.addon.AddonUtils;
import com.ning.billing.entitlement.engine.core.Engine;
+import com.ning.billing.entitlement.engine.core.EntitlementNotificationKey;
import com.ning.billing.entitlement.events.EntitlementEvent;
import com.ning.billing.entitlement.events.EntitlementEvent.EventType;
import com.ning.billing.entitlement.events.user.ApiEvent;
@@ -72,577 +73,542 @@ import com.ning.billing.util.notificationq.NotificationQueueService.NoSuchNotifi
import javax.annotation.Nullable;
public class EntitlementSqlDao implements EntitlementDao {
- private final static Logger log = LoggerFactory.getLogger(EntitlementSqlDao.class);
- public static final String ENTITLEMENT_EVENTS_TABLE_NAME = "entitlement_events";
- public static final String BUNDLES_TABLE_NAME = "bundles";
- public static final String SUBSCRIPTIONS_TABLE_NAME = "subscriptions";
-
- private final Clock clock;
- private final SubscriptionSqlDao subscriptionsDao;
- private final BundleSqlDao bundlesDao;
- private final EventSqlDao eventsDao;
- private final NotificationQueueService notificationQueueService;
- private final AddonUtils addonUtils;
- private final CustomFieldDao customFieldDao;
-
- //
- // We are not injecting SubscriptionFactory since that creates circular dependencies--
- // Guice would still work, but this is playing with fire.
- //
- // Instead that factory passed through API top to bottom for the call where is it needed-- where we returned fully rehydrated Subscriptions
- //
- @Inject
- public EntitlementSqlDao(final IDBI dbi, final Clock clock,
- final AddonUtils addonUtils, final NotificationQueueService notificationQueueService,
- final CustomFieldDao customFieldDao) {
- this.clock = clock;
- this.subscriptionsDao = dbi.onDemand(SubscriptionSqlDao.class);
- this.eventsDao = dbi.onDemand(EventSqlDao.class);
- this.bundlesDao = dbi.onDemand(BundleSqlDao.class);
- this.notificationQueueService = notificationQueueService;
- this.addonUtils = addonUtils;
- this.customFieldDao = customFieldDao;
- }
-
- @Override
- public SubscriptionBundle getSubscriptionBundleFromKey(final String bundleKey) {
- return bundlesDao.getBundleFromKey(bundleKey);
- }
-
- @Override
- public List<SubscriptionBundle> getSubscriptionBundleForAccount(
- final UUID accountId) {
- return bundlesDao.getBundleFromAccount(accountId.toString());
- }
-
- @Override
- public SubscriptionBundle getSubscriptionBundleFromId(final UUID bundleId) {
- return bundlesDao.getBundleFromId(bundleId.toString());
- }
-
- @Override
- public SubscriptionBundle createSubscriptionBundle(final SubscriptionBundleData bundle, final CallContext context) {
- return bundlesDao.inTransaction(new Transaction<SubscriptionBundle, BundleSqlDao>() {
- @Override
- public SubscriptionBundle inTransaction(BundleSqlDao bundlesDao, TransactionStatus status) {
- bundlesDao.insertBundle(bundle);
-
- AuditSqlDao auditSqlDao = bundlesDao.become(AuditSqlDao.class);
- String bundleId = bundle.getId().toString();
- auditSqlDao.insertAuditFromTransaction(BUNDLES_TABLE_NAME, bundleId, ChangeType.INSERT, context);
-
- return bundle;
- }
- });
- }
-
- @Override
- public UUID getAccountIdFromSubscriptionId(final UUID subscriptionId) {
- Subscription subscription = subscriptionsDao.getSubscriptionFromId(subscriptionId.toString());
- if (subscription == null) {
- log.error(String.format(ErrorCode.ENT_INVALID_SUBSCRIPTION_ID.getFormat(), subscriptionId.toString()));
- return null;
- }
-
- UUID bundleId = subscription.getBundleId();
- if (bundleId == null) {
- log.error(String.format(ErrorCode.ENT_GET_NO_BUNDLE_FOR_SUBSCRIPTION.getFormat(), subscriptionId.toString()));
- return null;
- }
-
- SubscriptionBundle bundle = bundlesDao.getBundleFromId(bundleId.toString());
- if (bundle == null) {
- log.error(String.format(ErrorCode.ENT_GET_INVALID_BUNDLE_ID.getFormat(), bundleId.toString()));
- return null;
- }
-
- return bundle.getAccountId();
- }
-
- @Override
- public Subscription getBaseSubscription(final SubscriptionFactory factory, final UUID bundleId) {
- return getBaseSubscription(factory, bundleId, true);
- }
-
- @Override
- public Subscription getSubscriptionFromId(final SubscriptionFactory factory, final UUID subscriptionId) {
- return buildSubscription(factory, subscriptionsDao.getSubscriptionFromId(subscriptionId.toString()));
- }
-
- @Override
- public List<Subscription> getSubscriptions(final SubscriptionFactory factory, final UUID bundleId) {
- return buildBundleSubscriptions(factory, subscriptionsDao.getSubscriptionsFromBundleId(bundleId.toString()));
- }
-
- @Override
- public List<Subscription> getSubscriptionsForKey(final SubscriptionFactory factory, final String bundleKey) {
- SubscriptionBundle bundle = bundlesDao.getBundleFromKey(bundleKey);
- if (bundle == null) {
- return Collections.emptyList();
- }
- return getSubscriptions(factory, bundle.getId());
- }
-
- @Override
- public void updateSubscription(final SubscriptionData subscription, final CallContext context) {
-
- final Date ctd = (subscription.getChargedThroughDate() != null) ? subscription.getChargedThroughDate().toDate() : null;
- final Date ptd = (subscription.getPaidThroughDate() != null) ? subscription.getPaidThroughDate().toDate() : null;
-
- subscriptionsDao.inTransaction(new Transaction<Void, SubscriptionSqlDao>() {
- @Override
- public Void inTransaction(SubscriptionSqlDao transactionalDao,
- TransactionStatus status) throws Exception {
- transactionalDao.updateSubscription(subscription.getId().toString(), subscription.getActiveVersion(), ctd, ptd, context);
-
- AuditSqlDao auditSqlDao = transactionalDao.become(AuditSqlDao.class);
- String subscriptionId = subscription.getId().toString();
- auditSqlDao.insertAuditFromTransaction(SUBSCRIPTIONS_TABLE_NAME, subscriptionId, ChangeType.UPDATE, context);
- return null;
- }
- });
- }
-
- @Override
- public void createNextPhaseEvent(final UUID subscriptionId, final EntitlementEvent nextPhase, final CallContext context) {
- eventsDao.inTransaction(new Transaction<Void, EventSqlDao>() {
- @Override
- public Void inTransaction(EventSqlDao dao,
- TransactionStatus status) throws Exception {
- cancelNextPhaseEventFromTransaction(subscriptionId, dao, context);
- dao.insertEvent(nextPhase);
- AuditSqlDao auditSqlDao = dao.become(AuditSqlDao.class);
- auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, nextPhase.getId().toString(), ChangeType.INSERT, context);
-
- recordFutureNotificationFromTransaction(dao,
- nextPhase.getEffectiveDate(),
- new NotificationKey() {
- @Override
- public String toString() {
- return nextPhase.getId().toString();
- }
- });
- return null;
- }
- });
- }
-
- @Override
- public EntitlementEvent getEventById(UUID eventId) {
- return eventsDao.getEventById(eventId.toString());
- }
-
- @Override
- public List<EntitlementEvent> getEventsForSubscription(UUID subscriptionId) {
- return eventsDao.getEventsForSubscription(subscriptionId.toString());
- }
-
- @Override
- public List<EntitlementEvent> getPendingEventsForSubscription(UUID subscriptionId) {
- Date now = clock.getUTCNow().toDate();
- return eventsDao.getFutureActiveEventForSubscription(subscriptionId.toString(), now);
- }
-
- @Override
- public void createSubscription(final SubscriptionData subscription,
- final List<EntitlementEvent> initialEvents, final CallContext context) {
-
- subscriptionsDao.inTransaction(new Transaction<Void, SubscriptionSqlDao>() {
-
- @Override
- public Void inTransaction(SubscriptionSqlDao dao,
- TransactionStatus status) throws Exception {
-
- dao.insertSubscription(subscription, context);
- // STEPH batch as well
- EventSqlDao eventsDaoFromSameTransaction = dao.become(EventSqlDao.class);
- List<String> eventIds = new ArrayList<String>();
-
- for (final EntitlementEvent cur : initialEvents) {
- eventsDaoFromSameTransaction.insertEvent(cur);
- eventIds.add(cur.getId().toString()); // collect ids for batch audit log insert
- recordFutureNotificationFromTransaction(dao,
- cur.getEffectiveDate(),
- new NotificationKey() {
- @Override
- public String toString() {
- return cur.getId().toString();
- }
- });
- }
-
- AuditSqlDao auditSqlDao = dao.become(AuditSqlDao.class);
- auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, eventIds, ChangeType.INSERT, context);
- return null;
- }
- });
- }
-
- @Override
- public void recreateSubscription(final UUID subscriptionId,
- final List<EntitlementEvent> recreateEvents, final CallContext context) {
-
- eventsDao.inTransaction(new Transaction<Void, EventSqlDao>() {
- @Override
- public Void inTransaction(EventSqlDao dao,
- TransactionStatus status) throws Exception {
-
- List<String> eventIds = new ArrayList<String>();
- for (final EntitlementEvent cur : recreateEvents) {
- dao.insertEvent(cur);
- eventIds.add(cur.getId().toString()); // gather event ids for batch audit insert
- recordFutureNotificationFromTransaction(dao,
- cur.getEffectiveDate(),
- new NotificationKey() {
- @Override
- public String toString() {
- return cur.getId().toString();
- }
- });
- }
-
- AuditSqlDao auditSqlDao = dao.become(AuditSqlDao.class);
- auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, eventIds, ChangeType.INSERT, context);
- return null;
- }
- });
- }
-
- @Override
- public void cancelSubscription(final UUID subscriptionId, final EntitlementEvent cancelEvent, final CallContext context) {
-
- eventsDao.inTransaction(new Transaction<Void, EventSqlDao>() {
- @Override
- public Void inTransaction(EventSqlDao dao,
- TransactionStatus status) throws Exception {
- cancelNextCancelEventFromTransaction(subscriptionId, dao, context);
- cancelNextChangeEventFromTransaction(subscriptionId, dao, context);
- cancelNextPhaseEventFromTransaction(subscriptionId, dao, context);
- dao.insertEvent(cancelEvent);
- AuditSqlDao auditSqlDao = dao.become(AuditSqlDao.class);
- String cancelEventId = cancelEvent.getId().toString();
- auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, cancelEventId, ChangeType.INSERT, context);
-
- recordFutureNotificationFromTransaction(dao,
- cancelEvent.getEffectiveDate(),
- new NotificationKey() {
- @Override
- public String toString() {
- return cancelEvent.getId().toString();
- }
- });
- return null;
- }
- });
- }
-
- @Override
- public void uncancelSubscription(final UUID subscriptionId, final List<EntitlementEvent> uncancelEvents, final CallContext context) {
-
- eventsDao.inTransaction(new Transaction<Void, EventSqlDao>() {
-
- @Override
- public Void inTransaction(EventSqlDao dao,
- TransactionStatus status) throws Exception {
-
- UUID existingCancelId = null;
- Date now = clock.getUTCNow().toDate();
- List<EntitlementEvent> events = dao.getFutureActiveEventForSubscription(subscriptionId.toString(), now);
-
- for (EntitlementEvent cur : events) {
- if (cur.getType() == EventType.API_USER && ((ApiEvent) cur).getEventType() == ApiEventType.CANCEL) {
- if (existingCancelId != null) {
- throw new EntitlementError(String.format("Found multiple cancel active events for subscriptions %s", subscriptionId.toString()));
- }
- existingCancelId = cur.getId();
- }
- }
-
- if (existingCancelId != null) {
- dao.unactiveEvent(existingCancelId.toString(), now);
- String deactivatedEventId = existingCancelId.toString();
-
- List<String> eventIds = new ArrayList<String>();
- for (final EntitlementEvent cur : uncancelEvents) {
- dao.insertEvent(cur);
- eventIds.add(cur.getId().toString()); // gather event ids for batch insert into audit log
- recordFutureNotificationFromTransaction(dao,
- cur.getEffectiveDate(),
- new NotificationKey() {
- @Override
- public String toString() {
- return cur.getId().toString();
- }
- });
- }
-
- AuditSqlDao auditSqlDao = dao.become(AuditSqlDao.class);
- auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, deactivatedEventId, ChangeType.UPDATE, context);
- auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, eventIds, ChangeType.INSERT, context);
- }
- return null;
- }
- });
- }
-
- @Override
- public void changePlan(final UUID subscriptionId, final List<EntitlementEvent> changeEvents, final CallContext context) {
- eventsDao.inTransaction(new Transaction<Void, EventSqlDao>() {
- @Override
- public Void inTransaction(EventSqlDao dao, TransactionStatus status) throws Exception {
- cancelNextChangeEventFromTransaction(subscriptionId, dao, context);
- cancelNextPhaseEventFromTransaction(subscriptionId, dao, context);
-
- List<String> eventIds = new ArrayList<String>();
- for (final EntitlementEvent cur : changeEvents) {
- dao.insertEvent(cur);
- eventIds.add(cur.getId().toString()); // gather event ids for batch audit log insert
-
- recordFutureNotificationFromTransaction(dao,
- cur.getEffectiveDate(),
- new NotificationKey() {
- @Override
- public String toString() {
- return cur.getId().toString();
- }
- });
- }
-
- AuditSqlDao auditSqlDao = dao.become(AuditSqlDao.class);
- auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, eventIds, ChangeType.INSERT, context);
- return null;
- }
- });
- }
-
- private void cancelNextPhaseEventFromTransaction(final UUID subscriptionId, final EventSqlDao dao, final CallContext context) {
- cancelFutureEventFromTransaction(subscriptionId, dao, EventType.PHASE, null, context);
- }
-
- private void cancelNextChangeEventFromTransaction(final UUID subscriptionId, final EventSqlDao dao, final CallContext context) {
- cancelFutureEventFromTransaction(subscriptionId, dao, EventType.API_USER, ApiEventType.CHANGE, context);
- }
-
- private void cancelNextCancelEventFromTransaction(final UUID subscriptionId, final EventSqlDao dao, final CallContext context) {
- cancelFutureEventFromTransaction(subscriptionId, dao, EventType.API_USER, ApiEventType.CANCEL, context);
- }
-
- private void cancelFutureEventFromTransaction(final UUID subscriptionId, final EventSqlDao dao,
- final EventType type, @Nullable final ApiEventType apiType,
- final CallContext context) {
-
- UUID futureEventId = null;
- Date now = clock.getUTCNow().toDate();
- List<EntitlementEvent> events = dao.getFutureActiveEventForSubscription(subscriptionId.toString(), now);
- for (EntitlementEvent cur : events) {
- if (cur.getType() == type &&
- (apiType == null || apiType == ((ApiEvent) cur).getEventType() )) {
- if (futureEventId != null) {
- throw new EntitlementError(
- String.format("Found multiple future events for type %s for subscriptions %s",
- type, subscriptionId.toString()));
- }
- futureEventId = cur.getId();
- }
- }
-
- if (futureEventId != null) {
- dao.unactiveEvent(futureEventId.toString(), now);
-
- AuditSqlDao auditSqlDao = dao.become(AuditSqlDao.class);
- auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, futureEventId.toString(), ChangeType.UPDATE, context);
- }
- }
-
- private void updateCustomFieldsFromTransaction(final SubscriptionSqlDao transactionalDao,
- final SubscriptionData subscription,
- final CallContext context) {
- customFieldDao.saveFields(transactionalDao, subscription.getId(), subscription.getObjectName(), subscription.getFieldList(), context);
- }
-
- private Subscription buildSubscription(final SubscriptionFactory factory, final Subscription input) {
- if (input == null) {
- return null;
- }
- List<Subscription> bundleInput = new ArrayList<Subscription>();
- if (input.getCategory() == ProductCategory.ADD_ON) {
- Subscription baseSubscription = getBaseSubscription(factory, input.getBundleId(), false);
- bundleInput.add(baseSubscription);
- bundleInput.add(input);
- } else {
- bundleInput.add(input);
- }
- List<Subscription> reloadedSubscriptions = buildBundleSubscriptions(factory, bundleInput);
- for (Subscription cur : reloadedSubscriptions) {
- if (cur.getId().equals(input.getId())) {
- return cur;
- }
- }
- throw new EntitlementError(String.format("Unexpected code path in buildSubscription"));
- }
-
- private List<Subscription> buildBundleSubscriptions(final SubscriptionFactory factory, final List<Subscription> input) {
- // Make sure BasePlan -- if exists-- is first
- Collections.sort(input, new Comparator<Subscription>() {
- @Override
- public int compare(Subscription o1, Subscription o2) {
- if (o1.getCategory() == ProductCategory.BASE) {
- return -1;
- } else if (o2.getCategory() == ProductCategory.BASE) {
- return 1;
- } else {
- return o1.getStartDate().compareTo(o2.getStartDate());
- }
- }
- });
-
- EntitlementEvent futureBaseEvent = null;
- List<Subscription> result = new ArrayList<Subscription>(input.size());
- for (Subscription cur : input) {
-
- List<EntitlementEvent> events = eventsDao.getEventsForSubscription(cur.getId().toString());
- Subscription reloaded = factory.createSubscription(new SubscriptionBuilder((SubscriptionData) cur), events);
-
- switch (cur.getCategory()) {
- case BASE:
- Collection<EntitlementEvent> futureApiEvents = Collections2.filter(events, new Predicate<EntitlementEvent>() {
- @Override
- public boolean apply(EntitlementEvent input) {
- return (input.getEffectiveDate().isAfter(clock.getUTCNow()) &&
- ((input instanceof ApiEventCancel) || (input instanceof ApiEventChange)));
- }
- });
- futureBaseEvent = (futureApiEvents.size() == 0) ? null : futureApiEvents.iterator().next();
- break;
-
- case ADD_ON:
- Plan targetAddOnPlan = reloaded.getCurrentPlan();
- String baseProductName = (futureBaseEvent instanceof ApiEventChange) ?
- ((ApiEventChange) futureBaseEvent).getEventPlan() : null;
-
- boolean createCancelEvent = (futureBaseEvent != null) &&
- ((futureBaseEvent instanceof ApiEventCancel) ||
- ((! addonUtils.isAddonAvailable(baseProductName, futureBaseEvent.getEffectiveDate(), targetAddOnPlan)) ||
- (addonUtils.isAddonIncluded(baseProductName, futureBaseEvent.getEffectiveDate(), targetAddOnPlan))));
-
- if (createCancelEvent) {
- DateTime now = clock.getUTCNow();
- EntitlementEvent addOnCancelEvent = new ApiEventCancel(new ApiEventBuilder()
- .setSubscriptionId(reloaded.getId())
- .setActiveVersion(((SubscriptionData) reloaded).getActiveVersion())
- .setProcessedDate(now)
- .setEffectiveDate(futureBaseEvent.getEffectiveDate())
- .setRequestedDate(now)
- // This event is only there to indicate the ADD_ON is future canceled, but it is not there
- // on disk until the base plan cancellation becomes effective
- .setFromDisk(false));
-
- events.add(addOnCancelEvent);
- // Finally reload subscription with full set of events
- reloaded = factory.createSubscription(new SubscriptionBuilder((SubscriptionData) cur), events);
- }
- break;
- default:
- break;
- }
- loadCustomFields((SubscriptionData) reloaded);
- result.add(reloaded);
- }
- return result;
- }
-
- @Override
- public void migrate(final UUID accountId, final AccountMigrationData accountData, final CallContext context) {
-
- eventsDao.inTransaction(new Transaction<Void, EventSqlDao>() {
-
- @Override
- public Void inTransaction(EventSqlDao transEventDao,
- TransactionStatus status) throws Exception {
-
- SubscriptionSqlDao transSubDao = transEventDao.become(SubscriptionSqlDao.class);
- BundleSqlDao transBundleDao = transEventDao.become(BundleSqlDao.class);
-
- List<String> bundleIds = new ArrayList<String>();
- List<String> subscriptionIds = new ArrayList<String>();
- List<String> eventIds = new ArrayList<String>();
-
- for (BundleMigrationData curBundle : accountData.getData()) {
- SubscriptionBundleData bundleData = curBundle.getData();
-
- for (SubscriptionMigrationData curSubscription : curBundle.getSubscriptions()) {
-
- SubscriptionData subData = curSubscription.getData();
- for (final EntitlementEvent curEvent : curSubscription.getInitialEvents()) {
- transEventDao.insertEvent(curEvent);
- eventIds.add(curEvent.getId().toString()); // gather event ids for batch audit
-
- recordFutureNotificationFromTransaction(transEventDao,
- curEvent.getEffectiveDate(),
- new NotificationKey() {
- @Override
- public String toString() {
- return curEvent.getId().toString();
- }
- });
- }
- transSubDao.insertSubscription(subData, context);
- subscriptionIds.add(subData.getId().toString()); // gather subscription ids for batch audit
- }
- transBundleDao.insertBundle(bundleData);
- bundleIds.add(bundleData.getId().toString()); // gather bundle ids for batch audit
- }
-
- // add audit records for bundles, subscriptions, and events
- AuditSqlDao auditSqlDao = transBundleDao.become(AuditSqlDao.class);
- auditSqlDao.insertAuditFromTransaction(SUBSCRIPTIONS_TABLE_NAME, subscriptionIds, ChangeType.INSERT, context);
- auditSqlDao.insertAuditFromTransaction(BUNDLES_TABLE_NAME, bundleIds, ChangeType.INSERT, context);
- auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, eventIds, ChangeType.INSERT, context);
-
- return null;
- }
- });
- }
-
-
- private Subscription getBaseSubscription(final SubscriptionFactory factory, final UUID bundleId, boolean rebuildSubscription) {
- List<Subscription> subscriptions = subscriptionsDao.getSubscriptionsFromBundleId(bundleId.toString());
- for (Subscription cur : subscriptions) {
- if (cur.getCategory() == ProductCategory.BASE) {
- return rebuildSubscription ? buildSubscription(factory, cur) : cur;
- }
- }
- return null;
- }
-
- private void recordFutureNotificationFromTransaction(final Transmogrifier transactionalDao, final DateTime effectiveDate, final NotificationKey notificationKey) {
- try {
- NotificationQueue subscriptionEventQueue = notificationQueueService.getNotificationQueue(Engine.ENTITLEMENT_SERVICE_NAME,
- Engine.NOTIFICATION_QUEUE_NAME);
- subscriptionEventQueue.recordFutureNotificationFromTransaction(transactionalDao, effectiveDate, notificationKey);
- } catch (NoSuchNotificationQueue e) {
- throw new RuntimeException(e);
- }
- }
-
- @Override
- public void saveCustomFields(final SubscriptionData subscription, final CallContext context) {
- subscriptionsDao.inTransaction(new Transaction<Void, SubscriptionSqlDao>() {
- @Override
- public Void inTransaction(SubscriptionSqlDao transactionalDao,
- TransactionStatus status) throws Exception {
- updateCustomFieldsFromTransaction(transactionalDao, subscription, context);
- return null;
- }
- });
- }
-
- private void loadCustomFields(final SubscriptionData subscription) {
- CustomFieldSqlDao customFieldSqlDao = subscriptionsDao.become(CustomFieldSqlDao.class);
- List<CustomField> fields = customFieldSqlDao.load(subscription.getId().toString(), subscription.getObjectName());
- subscription.clearFields();
- if (fields != null) {
- subscription.setFields(fields);
- }
- }
+ private final static Logger log = LoggerFactory.getLogger(EntitlementSqlDao.class);
+ public static final String ENTITLEMENT_EVENTS_TABLE_NAME = "entitlement_events";
+ public static final String BUNDLES_TABLE_NAME = "bundles";
+ public static final String SUBSCRIPTIONS_TABLE_NAME = "subscriptions";
+
+ private final Clock clock;
+ private final SubscriptionSqlDao subscriptionsDao;
+ private final BundleSqlDao bundlesDao;
+ private final EventSqlDao eventsDao;
+ private final NotificationQueueService notificationQueueService;
+ private final AddonUtils addonUtils;
+ private final CustomFieldDao customFieldDao;
+
+ //
+ // We are not injecting SubscriptionFactory since that creates circular dependencies--
+ // Guice would still work, but this is playing with fire.
+ //
+ // Instead that factory passed through API top to bottom for the call where is it needed-- where we returned fully rehydrated Subscriptions
+ //
+ @Inject
+ public EntitlementSqlDao(final IDBI dbi, final Clock clock,
+ final AddonUtils addonUtils, final NotificationQueueService notificationQueueService,
+ final CustomFieldDao customFieldDao) {
+ this.clock = clock;
+ this.subscriptionsDao = dbi.onDemand(SubscriptionSqlDao.class);
+ this.eventsDao = dbi.onDemand(EventSqlDao.class);
+ this.bundlesDao = dbi.onDemand(BundleSqlDao.class);
+ this.notificationQueueService = notificationQueueService;
+ this.addonUtils = addonUtils;
+ this.customFieldDao = customFieldDao;
+ }
+
+ @Override
+ public SubscriptionBundle getSubscriptionBundleFromKey(final String bundleKey) {
+ return bundlesDao.getBundleFromKey(bundleKey);
+ }
+
+ @Override
+ public List<SubscriptionBundle> getSubscriptionBundleForAccount(
+ final UUID accountId) {
+ return bundlesDao.getBundleFromAccount(accountId.toString());
+ }
+
+ @Override
+ public SubscriptionBundle getSubscriptionBundleFromId(final UUID bundleId) {
+ return bundlesDao.getBundleFromId(bundleId.toString());
+ }
+
+ @Override
+ public SubscriptionBundle createSubscriptionBundle(final SubscriptionBundleData bundle, final CallContext context) {
+ return bundlesDao.inTransaction(new Transaction<SubscriptionBundle, BundleSqlDao>() {
+ @Override
+ public SubscriptionBundle inTransaction(BundleSqlDao bundlesDao, TransactionStatus status) {
+ bundlesDao.insertBundle(bundle);
+
+ AuditSqlDao auditSqlDao = bundlesDao.become(AuditSqlDao.class);
+ String bundleId = bundle.getId().toString();
+ auditSqlDao.insertAuditFromTransaction(BUNDLES_TABLE_NAME, bundleId, ChangeType.INSERT, context);
+
+ return bundle;
+ }
+ });
+ }
+
+ @Override
+ public UUID getAccountIdFromSubscriptionId(final UUID subscriptionId) {
+ Subscription subscription = subscriptionsDao.getSubscriptionFromId(subscriptionId.toString());
+ if (subscription == null) {
+ log.error(String.format(ErrorCode.ENT_INVALID_SUBSCRIPTION_ID.getFormat(), subscriptionId.toString()));
+ return null;
+ }
+
+ UUID bundleId = subscription.getBundleId();
+ if (bundleId == null) {
+ log.error(String.format(ErrorCode.ENT_GET_NO_BUNDLE_FOR_SUBSCRIPTION.getFormat(), subscriptionId.toString()));
+ return null;
+ }
+
+ SubscriptionBundle bundle = bundlesDao.getBundleFromId(bundleId.toString());
+ if (bundle == null) {
+ log.error(String.format(ErrorCode.ENT_GET_INVALID_BUNDLE_ID.getFormat(), bundleId.toString()));
+ return null;
+ }
+
+ return bundle.getAccountId();
+ }
+
+ @Override
+ public Subscription getBaseSubscription(final SubscriptionFactory factory, final UUID bundleId) {
+ return getBaseSubscription(factory, bundleId, true);
+ }
+
+ @Override
+ public Subscription getSubscriptionFromId(final SubscriptionFactory factory, final UUID subscriptionId) {
+ return buildSubscription(factory, subscriptionsDao.getSubscriptionFromId(subscriptionId.toString()));
+ }
+
+ @Override
+ public List<Subscription> getSubscriptions(final SubscriptionFactory factory, final UUID bundleId) {
+ return buildBundleSubscriptions(factory, subscriptionsDao.getSubscriptionsFromBundleId(bundleId.toString()));
+ }
+
+ @Override
+ public List<Subscription> getSubscriptionsForKey(final SubscriptionFactory factory, final String bundleKey) {
+ SubscriptionBundle bundle = bundlesDao.getBundleFromKey(bundleKey);
+ if (bundle == null) {
+ return Collections.emptyList();
+ }
+ return getSubscriptions(factory, bundle.getId());
+ }
+
+ @Override
+ public void updateSubscription(final SubscriptionData subscription, final CallContext context) {
+
+ final Date ctd = (subscription.getChargedThroughDate() != null) ? subscription.getChargedThroughDate().toDate() : null;
+ final Date ptd = (subscription.getPaidThroughDate() != null) ? subscription.getPaidThroughDate().toDate() : null;
+
+ subscriptionsDao.inTransaction(new Transaction<Void, SubscriptionSqlDao>() {
+ @Override
+ public Void inTransaction(SubscriptionSqlDao transactionalDao,
+ TransactionStatus status) throws Exception {
+ transactionalDao.updateSubscription(subscription.getId().toString(), subscription.getActiveVersion(), ctd, ptd, context);
+
+ AuditSqlDao auditSqlDao = transactionalDao.become(AuditSqlDao.class);
+ String subscriptionId = subscription.getId().toString();
+ auditSqlDao.insertAuditFromTransaction(SUBSCRIPTIONS_TABLE_NAME, subscriptionId, ChangeType.UPDATE, context);
+ return null;
+ }
+ });
+ }
+
+ @Override
+ public void createNextPhaseEvent(final UUID subscriptionId, final EntitlementEvent nextPhase, final CallContext context) {
+ eventsDao.inTransaction(new Transaction<Void, EventSqlDao>() {
+ @Override
+ public Void inTransaction(EventSqlDao dao,
+ TransactionStatus status) throws Exception {
+ cancelNextPhaseEventFromTransaction(subscriptionId, dao, context);
+ dao.insertEvent(nextPhase);
+ AuditSqlDao auditSqlDao = dao.become(AuditSqlDao.class);
+ auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, nextPhase.getId().toString(), ChangeType.INSERT, context);
+
+ recordFutureNotificationFromTransaction(dao,
+ nextPhase.getEffectiveDate(),
+ new EntitlementNotificationKey(nextPhase.getId()));
+ return null;
+ }
+ });
+ }
+
+ @Override
+ public EntitlementEvent getEventById(UUID eventId) {
+ return eventsDao.getEventById(eventId.toString());
+ }
+
+ @Override
+ public List<EntitlementEvent> getEventsForSubscription(UUID subscriptionId) {
+ return eventsDao.getEventsForSubscription(subscriptionId.toString());
+ }
+
+ @Override
+ public List<EntitlementEvent> getPendingEventsForSubscription(UUID subscriptionId) {
+ Date now = clock.getUTCNow().toDate();
+ return eventsDao.getFutureActiveEventForSubscription(subscriptionId.toString(), now);
+ }
+
+ @Override
+ public void createSubscription(final SubscriptionData subscription,
+ final List<EntitlementEvent> initialEvents, final CallContext context) {
+
+ subscriptionsDao.inTransaction(new Transaction<Void, SubscriptionSqlDao>() {
+
+ @Override
+ public Void inTransaction(SubscriptionSqlDao dao,
+ TransactionStatus status) throws Exception {
+
+ dao.insertSubscription(subscription, context);
+ // STEPH batch as well
+ EventSqlDao eventsDaoFromSameTransaction = dao.become(EventSqlDao.class);
+ List<String> eventIds = new ArrayList<String>();
+
+ for (final EntitlementEvent cur : initialEvents) {
+ eventsDaoFromSameTransaction.insertEvent(cur);
+ eventIds.add(cur.getId().toString()); // collect ids for batch audit log insert
+ recordFutureNotificationFromTransaction(dao,
+ cur.getEffectiveDate(),
+ new EntitlementNotificationKey(cur.getId()));
+ }
+
+ AuditSqlDao auditSqlDao = dao.become(AuditSqlDao.class);
+ auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, eventIds, ChangeType.INSERT, context);
+ return null;
+ }
+ });
+ }
+
+ @Override
+ public void recreateSubscription(final UUID subscriptionId,
+ final List<EntitlementEvent> recreateEvents, final CallContext context) {
+
+ eventsDao.inTransaction(new Transaction<Void, EventSqlDao>() {
+ @Override
+ public Void inTransaction(EventSqlDao dao,
+ TransactionStatus status) throws Exception {
+
+ List<String> eventIds = new ArrayList<String>();
+ for (final EntitlementEvent cur : recreateEvents) {
+ dao.insertEvent(cur);
+ eventIds.add(cur.getId().toString()); // gather event ids for batch audit insert
+ recordFutureNotificationFromTransaction(dao,
+ cur.getEffectiveDate(),
+ new EntitlementNotificationKey(cur.getId()));
+ }
+
+ AuditSqlDao auditSqlDao = dao.become(AuditSqlDao.class);
+ auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, eventIds, ChangeType.INSERT, context);
+ return null;
+ }
+ });
+ }
+
+ @Override
+ public void cancelSubscription(final UUID subscriptionId, final EntitlementEvent cancelEvent, final CallContext context, final int seqId) {
+
+ eventsDao.inTransaction(new Transaction<Void, EventSqlDao>() {
+ @Override
+ public Void inTransaction(EventSqlDao dao,
+ TransactionStatus status) throws Exception {
+ cancelNextCancelEventFromTransaction(subscriptionId, dao, context);
+ cancelNextChangeEventFromTransaction(subscriptionId, dao, context);
+ cancelNextPhaseEventFromTransaction(subscriptionId, dao, context);
+ dao.insertEvent(cancelEvent);
+ AuditSqlDao auditSqlDao = dao.become(AuditSqlDao.class);
+ String cancelEventId = cancelEvent.getId().toString();
+ auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, cancelEventId, ChangeType.INSERT, context);
+
+ recordFutureNotificationFromTransaction(dao,
+ cancelEvent.getEffectiveDate(),
+ new EntitlementNotificationKey(cancelEvent.getId(), seqId));
+ return null;
+ }
+ });
+ }
+
+ @Override
+ public void uncancelSubscription(final UUID subscriptionId, final List<EntitlementEvent> uncancelEvents, final CallContext context) {
+
+ eventsDao.inTransaction(new Transaction<Void, EventSqlDao>() {
+
+ @Override
+ public Void inTransaction(EventSqlDao dao,
+ TransactionStatus status) throws Exception {
+
+ UUID existingCancelId = null;
+ Date now = clock.getUTCNow().toDate();
+ List<EntitlementEvent> events = dao.getFutureActiveEventForSubscription(subscriptionId.toString(), now);
+
+ for (EntitlementEvent cur : events) {
+ if (cur.getType() == EventType.API_USER && ((ApiEvent) cur).getEventType() == ApiEventType.CANCEL) {
+ if (existingCancelId != null) {
+ throw new EntitlementError(String.format("Found multiple cancel active events for subscriptions %s", subscriptionId.toString()));
+ }
+ existingCancelId = cur.getId();
+ }
+ }
+
+ if (existingCancelId != null) {
+ dao.unactiveEvent(existingCancelId.toString(), now);
+ String deactivatedEventId = existingCancelId.toString();
+
+ List<String> eventIds = new ArrayList<String>();
+ for (final EntitlementEvent cur : uncancelEvents) {
+ dao.insertEvent(cur);
+ eventIds.add(cur.getId().toString()); // gather event ids for batch insert into audit log
+ recordFutureNotificationFromTransaction(dao,
+ cur.getEffectiveDate(),
+ new EntitlementNotificationKey(cur.getId()));
+ }
+
+ AuditSqlDao auditSqlDao = dao.become(AuditSqlDao.class);
+ auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, deactivatedEventId, ChangeType.UPDATE, context);
+ auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, eventIds, ChangeType.INSERT, context);
+ }
+ return null;
+ }
+ });
+ }
+
+ @Override
+ public void changePlan(final UUID subscriptionId, final List<EntitlementEvent> changeEvents, final CallContext context) {
+ eventsDao.inTransaction(new Transaction<Void, EventSqlDao>() {
+ @Override
+ public Void inTransaction(EventSqlDao dao, TransactionStatus status) throws Exception {
+ cancelNextChangeEventFromTransaction(subscriptionId, dao, context);
+ cancelNextPhaseEventFromTransaction(subscriptionId, dao, context);
+
+ List<String> eventIds = new ArrayList<String>();
+ for (final EntitlementEvent cur : changeEvents) {
+ dao.insertEvent(cur);
+ eventIds.add(cur.getId().toString()); // gather event ids for batch audit log insert
+
+ recordFutureNotificationFromTransaction(dao,
+ cur.getEffectiveDate(),
+ new EntitlementNotificationKey(cur.getId()));
+ }
+
+ AuditSqlDao auditSqlDao = dao.become(AuditSqlDao.class);
+ auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, eventIds, ChangeType.INSERT, context);
+ return null;
+ }
+ });
+ }
+
+ private void cancelNextPhaseEventFromTransaction(final UUID subscriptionId, final EventSqlDao dao, final CallContext context) {
+ cancelFutureEventFromTransaction(subscriptionId, dao, EventType.PHASE, null, context);
+ }
+
+ private void cancelNextChangeEventFromTransaction(final UUID subscriptionId, final EventSqlDao dao, final CallContext context) {
+ cancelFutureEventFromTransaction(subscriptionId, dao, EventType.API_USER, ApiEventType.CHANGE, context);
+ }
+
+ private void cancelNextCancelEventFromTransaction(final UUID subscriptionId, final EventSqlDao dao, final CallContext context) {
+ cancelFutureEventFromTransaction(subscriptionId, dao, EventType.API_USER, ApiEventType.CANCEL, context);
+ }
+
+ private void cancelFutureEventFromTransaction(final UUID subscriptionId, final EventSqlDao dao,
+ final EventType type, @Nullable final ApiEventType apiType,
+ final CallContext context) {
+
+ UUID futureEventId = null;
+ Date now = clock.getUTCNow().toDate();
+ List<EntitlementEvent> events = dao.getFutureActiveEventForSubscription(subscriptionId.toString(), now);
+ for (EntitlementEvent cur : events) {
+ if (cur.getType() == type &&
+ (apiType == null || apiType == ((ApiEvent) cur).getEventType() )) {
+ if (futureEventId != null) {
+ throw new EntitlementError(
+ String.format("Found multiple future events for type %s for subscriptions %s",
+ type, subscriptionId.toString()));
+ }
+ futureEventId = cur.getId();
+ }
+ }
+
+ if (futureEventId != null) {
+ dao.unactiveEvent(futureEventId.toString(), now);
+
+ AuditSqlDao auditSqlDao = dao.become(AuditSqlDao.class);
+ auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, futureEventId.toString(), ChangeType.UPDATE, context);
+ }
+ }
+
+ private void updateCustomFieldsFromTransaction(final SubscriptionSqlDao transactionalDao,
+ final SubscriptionData subscription,
+ final CallContext context) {
+ customFieldDao.saveFields(transactionalDao, subscription.getId(), subscription.getObjectName(), subscription.getFieldList(), context);
+ }
+
+ private Subscription buildSubscription(final SubscriptionFactory factory, final Subscription input) {
+ if (input == null) {
+ return null;
+ }
+ List<Subscription> bundleInput = new ArrayList<Subscription>();
+ if (input.getCategory() == ProductCategory.ADD_ON) {
+ Subscription baseSubscription = getBaseSubscription(factory, input.getBundleId(), false);
+ bundleInput.add(baseSubscription);
+ bundleInput.add(input);
+ } else {
+ bundleInput.add(input);
+ }
+ List<Subscription> reloadedSubscriptions = buildBundleSubscriptions(factory, bundleInput);
+ for (Subscription cur : reloadedSubscriptions) {
+ if (cur.getId().equals(input.getId())) {
+ return cur;
+ }
+ }
+ throw new EntitlementError(String.format("Unexpected code path in buildSubscription"));
+ }
+
+ private List<Subscription> buildBundleSubscriptions(final SubscriptionFactory factory, final List<Subscription> input) {
+ // Make sure BasePlan -- if exists-- is first
+ Collections.sort(input, new Comparator<Subscription>() {
+ @Override
+ public int compare(Subscription o1, Subscription o2) {
+ if (o1.getCategory() == ProductCategory.BASE) {
+ return -1;
+ } else if (o2.getCategory() == ProductCategory.BASE) {
+ return 1;
+ } else {
+ return o1.getStartDate().compareTo(o2.getStartDate());
+ }
+ }
+ });
+
+ EntitlementEvent futureBaseEvent = null;
+ List<Subscription> result = new ArrayList<Subscription>(input.size());
+ for (Subscription cur : input) {
+
+ List<EntitlementEvent> events = eventsDao.getEventsForSubscription(cur.getId().toString());
+ Subscription reloaded = factory.createSubscription(new SubscriptionBuilder((SubscriptionData) cur), events);
+
+ switch (cur.getCategory()) {
+ case BASE:
+ Collection<EntitlementEvent> futureApiEvents = Collections2.filter(events, new Predicate<EntitlementEvent>() {
+ @Override
+ public boolean apply(EntitlementEvent input) {
+ return (input.getEffectiveDate().isAfter(clock.getUTCNow()) &&
+ ((input instanceof ApiEventCancel) || (input instanceof ApiEventChange)));
+ }
+ });
+ futureBaseEvent = (futureApiEvents.size() == 0) ? null : futureApiEvents.iterator().next();
+ break;
+
+ case ADD_ON:
+ Plan targetAddOnPlan = reloaded.getCurrentPlan();
+ String baseProductName = (futureBaseEvent instanceof ApiEventChange) ?
+ ((ApiEventChange) futureBaseEvent).getEventPlan() : null;
+
+ boolean createCancelEvent = (futureBaseEvent != null) &&
+ ((futureBaseEvent instanceof ApiEventCancel) ||
+ ((! addonUtils.isAddonAvailable(baseProductName, futureBaseEvent.getEffectiveDate(), targetAddOnPlan)) ||
+ (addonUtils.isAddonIncluded(baseProductName, futureBaseEvent.getEffectiveDate(), targetAddOnPlan))));
+
+ if (createCancelEvent) {
+ DateTime now = clock.getUTCNow();
+ EntitlementEvent addOnCancelEvent = new ApiEventCancel(new ApiEventBuilder()
+ .setSubscriptionId(reloaded.getId())
+ .setActiveVersion(((SubscriptionData) reloaded).getActiveVersion())
+ .setProcessedDate(now)
+ .setEffectiveDate(futureBaseEvent.getEffectiveDate())
+ .setRequestedDate(now)
+ // This event is only there to indicate the ADD_ON is future canceled, but it is not there
+ // on disk until the base plan cancellation becomes effective
+ .setFromDisk(false));
+
+ events.add(addOnCancelEvent);
+ // Finally reload subscription with full set of events
+ reloaded = factory.createSubscription(new SubscriptionBuilder((SubscriptionData) cur), events);
+ }
+ break;
+ default:
+ break;
+ }
+ loadCustomFields((SubscriptionData) reloaded);
+ result.add(reloaded);
+ }
+ return result;
+ }
+
+ @Override
+ public void migrate(final UUID accountId, final AccountMigrationData accountData, final CallContext context) {
+
+ eventsDao.inTransaction(new Transaction<Void, EventSqlDao>() {
+
+ @Override
+ public Void inTransaction(EventSqlDao transEventDao,
+ TransactionStatus status) throws Exception {
+
+ SubscriptionSqlDao transSubDao = transEventDao.become(SubscriptionSqlDao.class);
+ BundleSqlDao transBundleDao = transEventDao.become(BundleSqlDao.class);
+
+ List<String> bundleIds = new ArrayList<String>();
+ List<String> subscriptionIds = new ArrayList<String>();
+ List<String> eventIds = new ArrayList<String>();
+
+ for (BundleMigrationData curBundle : accountData.getData()) {
+ SubscriptionBundleData bundleData = curBundle.getData();
+
+ for (SubscriptionMigrationData curSubscription : curBundle.getSubscriptions()) {
+
+ SubscriptionData subData = curSubscription.getData();
+ for (final EntitlementEvent curEvent : curSubscription.getInitialEvents()) {
+ transEventDao.insertEvent(curEvent);
+ eventIds.add(curEvent.getId().toString()); // gather event ids for batch audit
+
+ recordFutureNotificationFromTransaction(transEventDao,
+ curEvent.getEffectiveDate(),
+ new EntitlementNotificationKey(curEvent.getId()));
+ }
+ transSubDao.insertSubscription(subData, context);
+ subscriptionIds.add(subData.getId().toString()); // gather subscription ids for batch audit
+ }
+ transBundleDao.insertBundle(bundleData);
+ bundleIds.add(bundleData.getId().toString()); // gather bundle ids for batch audit
+ }
+
+ // add audit records for bundles, subscriptions, and events
+ AuditSqlDao auditSqlDao = transBundleDao.become(AuditSqlDao.class);
+ auditSqlDao.insertAuditFromTransaction(SUBSCRIPTIONS_TABLE_NAME, subscriptionIds, ChangeType.INSERT, context);
+ auditSqlDao.insertAuditFromTransaction(BUNDLES_TABLE_NAME, bundleIds, ChangeType.INSERT, context);
+ auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, eventIds, ChangeType.INSERT, context);
+
+ return null;
+ }
+ });
+ }
+
+
+ private Subscription getBaseSubscription(final SubscriptionFactory factory, final UUID bundleId, boolean rebuildSubscription) {
+ List<Subscription> subscriptions = subscriptionsDao.getSubscriptionsFromBundleId(bundleId.toString());
+ for (Subscription cur : subscriptions) {
+ if (cur.getCategory() == ProductCategory.BASE) {
+ return rebuildSubscription ? buildSubscription(factory, cur) : cur;
+ }
+ }
+ return null;
+ }
+
+ private void recordFutureNotificationFromTransaction(final Transmogrifier transactionalDao, final DateTime effectiveDate, final NotificationKey notificationKey) {
+ try {
+ NotificationQueue subscriptionEventQueue = notificationQueueService.getNotificationQueue(Engine.ENTITLEMENT_SERVICE_NAME,
+ Engine.NOTIFICATION_QUEUE_NAME);
+ subscriptionEventQueue.recordFutureNotificationFromTransaction(transactionalDao, effectiveDate, notificationKey);
+ } catch (NoSuchNotificationQueue e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void saveCustomFields(final SubscriptionData subscription, final CallContext context) {
+ subscriptionsDao.inTransaction(new Transaction<Void, SubscriptionSqlDao>() {
+ @Override
+ public Void inTransaction(SubscriptionSqlDao transactionalDao,
+ TransactionStatus status) throws Exception {
+ updateCustomFieldsFromTransaction(transactionalDao, subscription, context);
+ return null;
+ }
+ });
+ }
+
+ private void loadCustomFields(final SubscriptionData subscription) {
+ CustomFieldSqlDao customFieldSqlDao = subscriptionsDao.become(CustomFieldSqlDao.class);
+ List<CustomField> fields = customFieldSqlDao.load(subscription.getId().toString(), subscription.getObjectName());
+ subscription.clearFields();
+ if (fields != null) {
+ subscription.setFields(fields);
+ }
+ }
}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EventSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EventSqlDao.java
index 610a7f8..5254517 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EventSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EventSqlDao.java
@@ -72,19 +72,36 @@ public interface EventSqlDao extends Transactional<EventSqlDao>, CloseMe, Transm
public static class EventSqlDaoBinder extends BinderBase implements Binder<Bind, EntitlementEvent> {
@Override
public void bind(@SuppressWarnings("rawtypes") SQLStatement stmt, Bind bind, EntitlementEvent evt) {
+
+ String planName = null;
+ String phaseName = null;
+ String priceListName = null;
+ String userType = null;
+ String userToken = null;
+ if (evt.getType() == EventType.API_USER) {
+ ApiEvent userEvent = (ApiEvent) evt;
+ planName = userEvent.getEventPlan();
+ phaseName = userEvent.getEventPlanPhase();
+ priceListName = userEvent.getPriceList();
+ userType = userEvent.getEventType().toString();
+ userToken = (userEvent.getUserToken() != null) ? userEvent.getUserToken().toString() : null;
+ } else {
+ phaseName = ((PhaseEvent) evt).getPhase();
+ }
stmt.bind("event_id", evt.getId().toString());
stmt.bind("event_type", evt.getType().toString());
- stmt.bind("user_type", (evt.getType() == EventType.API_USER) ? ((ApiEvent) evt).getEventType().toString() : null);
+ stmt.bind("user_type", userType);
stmt.bind("created_dt", getDate(evt.getProcessedDate()));
stmt.bind("updated_dt", getDate(evt.getProcessedDate()));
stmt.bind("requested_dt", getDate(evt.getRequestedDate()));
stmt.bind("effective_dt", getDate(evt.getEffectiveDate()));
stmt.bind("subscription_id", evt.getSubscriptionId().toString());
- stmt.bind("plan_name", (evt.getType() == EventType.API_USER) ? ((ApiEvent) evt).getEventPlan() : null);
- stmt.bind("phase_name", (evt.getType() == EventType.API_USER) ? ((ApiEvent) evt).getEventPlanPhase() : ((PhaseEvent) evt).getPhase());
- stmt.bind("plist_name", (evt.getType() == EventType.API_USER) ? ((ApiEvent) evt).getPriceList() : null);
+ stmt.bind("plan_name", planName);
+ stmt.bind("phase_name", phaseName);
+ stmt.bind("plist_name", priceListName);
stmt.bind("current_version", evt.getActiveVersion());
stmt.bind("is_active", evt.isActive());
+ stmt.bind("user_token", userToken);
}
}
@@ -106,7 +123,8 @@ public interface EventSqlDao extends Transactional<EventSqlDao>, CloseMe, Transm
String priceListName = r.getString("plist_name");
long currentVersion = r.getLong("current_version");
boolean isActive = r.getBoolean("is_active");
-
+ UUID userToken = r.getString("user_token") != null ? UUID.fromString(r.getString("user_token")) : null;
+
EventBaseBuilder<?> base = ((eventType == EventType.PHASE) ?
new PhaseEventBuilder() :
new ApiEventBuilder())
@@ -128,6 +146,7 @@ public interface EventSqlDao extends Transactional<EventSqlDao>, CloseMe, Transm
.setEventPlanPhase(phaseName)
.setEventPriceList(priceListName)
.setEventType(userType)
+ .setUserToken(userToken)
.setFromDisk(true);
if (userType == ApiEventType.CREATE) {
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEvent.java b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEvent.java
index c26b168..20a6569 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEvent.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEvent.java
@@ -16,6 +16,8 @@
package com.ning.billing.entitlement.events.user;
+import java.util.UUID;
+
import com.ning.billing.entitlement.events.EntitlementEvent;
@@ -30,5 +32,7 @@ public interface ApiEvent extends EntitlementEvent {
public String getPriceList();
public boolean isFromDisk();
+
+ public UUID getUserToken();
}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventBase.java b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventBase.java
index f67c6b7..c68da2b 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventBase.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventBase.java
@@ -28,6 +28,7 @@ public class ApiEventBase extends EventBase implements ApiEvent {
private final String eventPlan;
private final String eventPlanPhase;
private final String eventPriceList;
+ private final UUID userToken;
private final boolean fromDisk;
public ApiEventBase(ApiEventBuilder builder) {
@@ -37,28 +38,9 @@ public class ApiEventBase extends EventBase implements ApiEvent {
this.eventPlan = builder.getEventPlan();
this.eventPlanPhase = builder.getEventPlanPhase();
this.fromDisk = builder.isFromDisk();
+ this.userToken = builder.getUserToken();
}
-/*
- public ApiEventBase(UUID subscriptionId, DateTime bundleStartDate, DateTime processed, String planName, String phaseName,
- String priceList, DateTime requestedDate, ApiEventType eventType, DateTime effectiveDate, long activeVersion) {
- super(subscriptionId, requestedDate, effectiveDate, processed, activeVersion, true);
- this.eventType = eventType;
- this.eventPriceList = priceList;
- this.eventPlan = planName;
- this.eventPlanPhase = phaseName;
- }
-
- public ApiEventBase(UUID subscriptionId, DateTime bundleStartDate, DateTime processed,
- DateTime requestedDate, ApiEventType eventType, DateTime effectiveDate, long activeVersion) {
- super(subscriptionId, requestedDate, effectiveDate, processed, activeVersion, true);
- this.eventType = eventType;
- this.eventPriceList = null;
- this.eventPlan = null;
- this.eventPlanPhase = null;
- }
-*/
-
@Override
public ApiEventType getEventType() {
return eventType;
@@ -83,6 +65,12 @@ public class ApiEventBase extends EventBase implements ApiEvent {
public String getPriceList() {
return eventPriceList;
}
+
+ @Override
+ public UUID getUserToken() {
+ return userToken;
+ }
+
@Override
public boolean isFromDisk() {
@@ -105,6 +93,7 @@ public class ApiEventBase extends EventBase implements ApiEvent {
+ ", getActiveVersion()=" + getActiveVersion()
+ ", getProcessedDate()=" + getProcessedDate()
+ ", getSubscriptionId()=" + getSubscriptionId()
+ + ", evetnToken()=" + getUserToken()
+ ", isActive()=" + isActive() + "]";
}
}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventBuilder.java b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventBuilder.java
index b7e9764..b6be427 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventBuilder.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventBuilder.java
@@ -16,6 +16,8 @@
package com.ning.billing.entitlement.events.user;
+import java.util.UUID;
+
import com.ning.billing.entitlement.events.EventBaseBuilder;
public class ApiEventBuilder extends EventBaseBuilder<ApiEventBuilder> {
@@ -24,6 +26,7 @@ public class ApiEventBuilder extends EventBaseBuilder<ApiEventBuilder> {
private String eventPlan;
private String eventPlanPhase;
private String eventPriceList;
+ private UUID userToken;
private boolean fromDisk;
@@ -50,11 +53,21 @@ public class ApiEventBuilder extends EventBaseBuilder<ApiEventBuilder> {
public String getEventPriceList() {
return eventPriceList;
}
+
+ public UUID getUserToken() {
+ return userToken;
+ }
public boolean isFromDisk() {
return fromDisk;
}
+ public ApiEventBuilder setUserToken(UUID userToken) {
+ this.userToken = userToken;
+ return this;
+ }
+
+
public ApiEventBuilder setFromDisk(boolean fromDisk) {
this.fromDisk = fromDisk;
return this;
diff --git a/entitlement/src/main/resources/com/ning/billing/entitlement/ddl.sql b/entitlement/src/main/resources/com/ning/billing/entitlement/ddl.sql
index c39d001..0a64176 100644
--- a/entitlement/src/main/resources/com/ning/billing/entitlement/ddl.sql
+++ b/entitlement/src/main/resources/com/ning/billing/entitlement/ddl.sql
@@ -13,6 +13,7 @@ CREATE TABLE entitlement_events (
plan_name varchar(64) DEFAULT NULL,
phase_name varchar(128) DEFAULT NULL,
plist_name varchar(64) DEFAULT NULL,
+ user_token char(36),
current_version int(11) DEFAULT 1,
is_active bool DEFAULT 1,
PRIMARY KEY(id)
diff --git a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/EventSqlDao.sql.stg b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/EventSqlDao.sql.stg
index 86ee538..a1aae54 100644
--- a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/EventSqlDao.sql.stg
+++ b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/EventSqlDao.sql.stg
@@ -15,6 +15,7 @@ getEventById(event_id) ::= <<
, phase_name
, plist_name
, current_version
+ , user_token
, is_active
from entitlement_events
where
@@ -36,6 +37,7 @@ insertEvent() ::= <<
, phase_name
, plist_name
, current_version
+ , user_token
, is_active
) values (
:event_id
@@ -50,6 +52,7 @@ insertEvent() ::= <<
, :phase_name
, :plist_name
, :current_version
+ , :user_token
, :is_active
);
>>
@@ -89,6 +92,7 @@ getFutureActiveEventForSubscription(subscription_id, now) ::= <<
, phase_name
, plist_name
, current_version
+ , user_token
, is_active
from entitlement_events
where
@@ -118,6 +122,7 @@ getEventsForSubscription(subscription_id) ::= <<
, phase_name
, plist_name
, current_version
+ , user_token
, is_active
from entitlement_events
where
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/TestDefaultEntitlementBillingApi.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/TestDefaultEntitlementBillingApi.java
index e940cd8..79c786c 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/TestDefaultEntitlementBillingApi.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/TestDefaultEntitlementBillingApi.java
@@ -152,7 +152,7 @@ public class TestDefaultEntitlementBillingApi {
PlanPhase nextPhase = nextPlan.getAllPhases()[0]; // The trial has no billing period
String nextPriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
SubscriptionTransition t = new SubscriptionTransitionData(
- zeroId, oneId, twoId, EventType.API_USER, ApiEventType.CREATE, then, now, null, null, null, null, SubscriptionState.ACTIVE, nextPlan, nextPhase, nextPriceList, 1, true);
+ zeroId, oneId, twoId, EventType.API_USER, ApiEventType.CREATE, then, now, null, null, null, null, SubscriptionState.ACTIVE, nextPlan, nextPhase, nextPriceList, 1, null, true);
transitions.add(t);
AccountUserApi accountApi = BrainDeadProxyFactory.createBrainDeadProxyFor(AccountUserApi.class);
@@ -174,7 +174,7 @@ public class TestDefaultEntitlementBillingApi {
PlanPhase nextPhase = nextPlan.getAllPhases()[1];
String nextPriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
SubscriptionTransition t = new SubscriptionTransitionData(
- zeroId, oneId, twoId, EventType.API_USER, ApiEventType.CREATE, then, now, null, null, null, null, SubscriptionState.ACTIVE, nextPlan, nextPhase, nextPriceList, 1, true);
+ zeroId, oneId, twoId, EventType.API_USER, ApiEventType.CREATE, then, now, null, null, null, null, SubscriptionState.ACTIVE, nextPlan, nextPhase, nextPriceList, 1, null, true);
transitions.add(t);
Account account = BrainDeadProxyFactory.createBrainDeadProxyFor(Account.class);
@@ -199,7 +199,7 @@ public class TestDefaultEntitlementBillingApi {
PlanPhase nextPhase = nextPlan.getAllPhases()[1];
String nextPriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
SubscriptionTransition t = new SubscriptionTransitionData(
- zeroId, oneId, twoId, EventType.API_USER, ApiEventType.CREATE, then, now, null, null, null, null, SubscriptionState.ACTIVE, nextPlan, nextPhase, nextPriceList, 1, true);
+ zeroId, oneId, twoId, EventType.API_USER, ApiEventType.CREATE, then, now, null, null, null, null, SubscriptionState.ACTIVE, nextPlan, nextPhase, nextPriceList, 1, null, true);
transitions.add(t);
AccountUserApi accountApi = BrainDeadProxyFactory.createBrainDeadProxyFor(AccountUserApi.class);
@@ -221,7 +221,7 @@ public class TestDefaultEntitlementBillingApi {
PlanPhase nextPhase = nextPlan.getAllPhases()[0];
String nextPriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
SubscriptionTransition t = new SubscriptionTransitionData(
- zeroId, oneId, twoId, EventType.API_USER, ApiEventType.CREATE, then, now, null, null, null, null, SubscriptionState.ACTIVE, nextPlan, nextPhase, nextPriceList, 1, true);
+ zeroId, oneId, twoId, EventType.API_USER, ApiEventType.CREATE, then, now, null, null, null, null, SubscriptionState.ACTIVE, nextPlan, nextPhase, nextPriceList, 1, null, true);
transitions.add(t);
Account account = BrainDeadProxyFactory.createBrainDeadProxyFor(Account.class);
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 c7728aa..8b93cd6 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
@@ -128,7 +128,6 @@ public abstract class TestApiBase {
} catch (Exception e) {
log.warn("Failed to tearDown test properly ", e);
}
- //if(helper != null) { helper.stopMysql(); }
}
@BeforeClass(alwaysRun = true)
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java
index 94836a8..1872305 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java
@@ -282,7 +282,7 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
@Override
public void cancelSubscription(final UUID subscriptionId, final EntitlementEvent cancelEvent,
- final CallContext context) {
+ final CallContext context, final int seqId) {
synchronized (cancelEvent) {
cancelNextPhaseEvent(subscriptionId);
insertEvent(cancelEvent);
diff --git a/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultEmptyInvoiceNotification.java b/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultEmptyInvoiceNotification.java
new file mode 100644
index 0000000..2cba6fb
--- /dev/null
+++ b/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultEmptyInvoiceNotification.java
@@ -0,0 +1,56 @@
+/*
+ * 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.invoice.api.user;
+
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.invoice.api.EmptyInvoiceNotification;
+
+public class DefaultEmptyInvoiceNotification implements EmptyInvoiceNotification {
+
+ private final UUID accountId;
+ private final DateTime processingDate;
+ private final UUID userToken;
+
+
+ public DefaultEmptyInvoiceNotification(final UUID accountId,
+ final DateTime processingDate, final UUID userToken) {
+ super();
+ this.accountId = accountId;
+ this.processingDate = processingDate;
+ this.userToken = userToken;
+ }
+
+ @Override
+ public BusEventType getBusEventType() {
+ return BusEventType.INVOICE_EMPTY;
+ }
+
+ @Override
+ public UUID getUserToken() {
+ return userToken;
+ }
+
+ public UUID getAccountId() {
+ return accountId;
+ }
+
+ public DateTime getProcessingDate() {
+ return processingDate;
+ }
+}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceCreationNotification.java b/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceCreationNotification.java
index 8156779..cb8bff3 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceCreationNotification.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceCreationNotification.java
@@ -26,20 +26,33 @@ import com.ning.billing.invoice.api.InvoiceCreationNotification;
import com.ning.billing.util.bus.BusEvent.BusEventType;
public class DefaultInvoiceCreationNotification implements InvoiceCreationNotification {
+
private final UUID invoiceId;
private final UUID accountId;
private final BigDecimal amountOwed;
private final Currency currency;
private final DateTime invoiceCreationDate;
+ private final UUID userToken;
- public DefaultInvoiceCreationNotification(UUID invoiceId, UUID accountId, BigDecimal amountOwed, Currency currency, DateTime invoiceCreationDate) {
+ public DefaultInvoiceCreationNotification(UUID invoiceId, UUID accountId, BigDecimal amountOwed, Currency currency, DateTime invoiceCreationDate, UUID userToken) {
this.invoiceId = invoiceId;
this.accountId = accountId;
this.amountOwed = amountOwed;
this.currency = currency;
this.invoiceCreationDate = invoiceCreationDate;
+ this.userToken = userToken;
}
+ @Override
+ public BusEventType getBusEventType() {
+ return BusEventType.INVOICE_CREATION;
+ }
+
+ @Override
+ public UUID getUserToken() {
+ return userToken;
+ }
+
@Override
public UUID getInvoiceId() {
return invoiceId;
@@ -69,11 +82,4 @@ public class DefaultInvoiceCreationNotification implements InvoiceCreationNotifi
public String toString() {
return "DefaultInvoiceCreationNotification [invoiceId=" + invoiceId + ", accountId=" + accountId + ", amountOwed=" + amountOwed + ", currency=" + currency + ", invoiceCreationDate=" + invoiceCreationDate + "]";
}
-
-
- @Override
- public BusEventType getBusEventType() {
- return BusEventType.INVOICE_CREATION;
- }
-
}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
index cf46d15..4b1c4f8 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
@@ -194,7 +194,8 @@ public class DefaultInvoiceDao implements InvoiceDao {
InvoiceCreationNotification event;
event = new DefaultInvoiceCreationNotification(invoice.getId(), invoice.getAccountId(),
invoice.getBalance(), invoice.getCurrency(),
- invoice.getInvoiceDate());
+ invoice.getInvoiceDate(),
+ context.getUserToken());
try {
eventBus.post(event);
} catch (Bus.EventBusException e) {
diff --git a/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java b/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java
index 32c625c..eecde79 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java
@@ -21,7 +21,12 @@ import java.util.List;
import java.util.SortedSet;
import java.util.UUID;
+import com.ning.billing.util.bus.Bus;
+import com.ning.billing.util.bus.Bus.EventBusException;
+import com.ning.billing.util.bus.BusEvent;
import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.clock.Clock;
+
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -37,6 +42,7 @@ import com.ning.billing.entitlement.api.user.SubscriptionTransition;
import com.ning.billing.invoice.api.Invoice;
import com.ning.billing.invoice.api.InvoiceApiException;
import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.user.DefaultEmptyInvoiceNotification;
import com.ning.billing.invoice.dao.InvoiceDao;
import com.ning.billing.invoice.model.BillingEventSet;
import com.ning.billing.invoice.model.InvoiceGenerator;
@@ -54,6 +60,8 @@ public class InvoiceDispatcher {
private final AccountUserApi accountUserApi;
private final InvoiceDao invoiceDao;
private final GlobalLocker locker;
+ private final Bus eventBus;
+ private final Clock clock;
private final boolean VERBOSE_OUTPUT;
@@ -61,12 +69,16 @@ public class InvoiceDispatcher {
public InvoiceDispatcher(final InvoiceGenerator generator, final AccountUserApi accountUserApi,
final EntitlementBillingApi entitlementBillingApi,
final InvoiceDao invoiceDao,
- final GlobalLocker locker) {
+ final GlobalLocker locker,
+ final Bus eventBus,
+ final Clock clock) {
this.generator = generator;
this.entitlementBillingApi = entitlementBillingApi;
this.accountUserApi = accountUserApi;
this.invoiceDao = invoiceDao;
this.locker = locker;
+ this.eventBus = eventBus;
+ this.clock = clock;
String verboseOutputValue = System.getProperty("VERBOSE_OUTPUT");
VERBOSE_OUTPUT = (verboseOutputValue != null) && Boolean.parseBoolean(verboseOutputValue);
@@ -118,6 +130,14 @@ public class InvoiceDispatcher {
return null;
}
+ private void postEmptyInvoiceEvent(final UUID accountId, final UUID userToken) {
+ try {
+ BusEvent event = new DefaultEmptyInvoiceNotification(accountId, clock.getUTCNow(), userToken);
+ eventBus.post(event);
+ } catch (EventBusException e){
+ log.error("Failed to post DefaultEmptyInvoiceNotification event for account {} ", accountId, e);
+ }
+ }
private Invoice processAccountWithLock(final UUID accountId, final DateTime targetDate,
final boolean dryRun, final CallContext context) throws InvoiceApiException {
@@ -125,7 +145,7 @@ public class InvoiceDispatcher {
if (account == null) {
log.error("Failed handling entitlement change.",
new InvoiceApiException(ErrorCode.INVOICE_ACCOUNT_ID_INVALID, accountId.toString()));
- return null;
+ return null;
}
SortedSet<BillingEvent> events = entitlementBillingApi.getBillingEventsForAccount(accountId);
@@ -139,9 +159,11 @@ public class InvoiceDispatcher {
if (invoice == null) {
log.info("Generated null invoice.");
outputDebugData(events, invoices);
+ if (!dryRun) {
+ postEmptyInvoiceEvent(accountId, context.getUserToken());
+ }
} else {
log.info("Generated invoice {} with {} items.", invoice.getId().toString(), invoice.getNumberOfItems());
-
if (VERBOSE_OUTPUT) {
log.info("New items");
for (InvoiceItem item : invoice.getInvoiceItems()) {
@@ -149,12 +171,10 @@ public class InvoiceDispatcher {
}
}
outputDebugData(events, invoices);
-
- if (invoice.getNumberOfItems() > 0 && !dryRun) {
+ if (!dryRun) {
invoiceDao.create(invoice, context);
}
}
-
return invoice;
}
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 ff23965..cf84664 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/InvoiceListener.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/InvoiceListener.java
@@ -45,7 +45,11 @@ public class InvoiceListener {
@Subscribe
public void handleSubscriptionTransition(final SubscriptionTransition transition) {
try {
- CallContext context = factory.createCallContext("Transition", CallOrigin.INTERNAL, UserType.SYSTEM);
+ if (transition.getRemainingEventsForUserOperation() > 0) {
+ // Skip invoice generation as there is more coming...
+ return;
+ }
+ CallContext context = factory.createCallContext("Transition", CallOrigin.INTERNAL, UserType.SYSTEM, transition.getUserToken());
dispatcher.processSubscription(transition, context);
} catch (InvoiceApiException e) {
log.error(e.getMessage());
diff --git a/invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java b/invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java
index 07c8e54..27126ee 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java
@@ -185,7 +185,7 @@ public class TestDefaultInvoiceMigrationApi {
EntitlementBillingApi entitlementBillingApi = BrainDeadProxyFactory.createBrainDeadProxyFor(EntitlementBillingApi.class);
((ZombieControl)entitlementBillingApi).addResult("getBillingEventsForAccount", events);
- InvoiceDispatcher dispatcher = new InvoiceDispatcher(generator, accountUserApi, entitlementBillingApi, invoiceDao, locker);
+ InvoiceDispatcher dispatcher = new InvoiceDispatcher(generator, accountUserApi, entitlementBillingApi, invoiceDao, locker, busService.getBus(), clock);
CallContext context = new DefaultCallContextFactory(clock).createCallContext("Migration test", CallOrigin.TEST, UserType.TEST);
Invoice invoice = dispatcher.processAccount(accountId, date_regular, true, context);
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/MockInvoiceDao.java b/invoice/src/test/java/com/ning/billing/invoice/dao/MockInvoiceDao.java
index dec7697..c7818d8 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/MockInvoiceDao.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/MockInvoiceDao.java
@@ -52,7 +52,7 @@ public class MockInvoiceDao implements InvoiceDao {
try {
eventBus.post(new DefaultInvoiceCreationNotification(invoice.getId(), invoice.getAccountId(),
invoice.getBalance(), invoice.getCurrency(),
- invoice.getInvoiceDate()));
+ invoice.getInvoiceDate(), null));
}
catch (Bus.EventBusException ex) {
throw new RuntimeException(ex);
diff --git a/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java b/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
index 7008c90..6d9f62f 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
@@ -157,7 +157,7 @@ public class TestInvoiceDispatcher {
DateTime target = new DateTime();
- InvoiceDispatcher dispatcher = new InvoiceDispatcher(generator, accountUserApi, entitlementBillingApi, invoiceDao, locker);
+ InvoiceDispatcher dispatcher = new InvoiceDispatcher(generator, accountUserApi, entitlementBillingApi, invoiceDao, locker, busService.getBus(), clock);
Invoice invoice = dispatcher.processAccount(accountId, target, true, context);
Assert.assertNotNull(invoice);
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java
index c3d0ffa..49203f5 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java
@@ -131,7 +131,7 @@ public class AccountResource implements BaseJaxrsResource {
try {
AccountData data = json.toAccountData();
- final Account account = accountApi.createAccount(data, null, null, context.getContext());
+ final Account account = accountApi.createAccount(data, null, null, context.createContext());
URI uri = UriBuilder.fromPath(account.getId().toString()).build();
return uriBuilder.buildResponse(AccountResource.class, "getAccount", account.getId());
} catch (AccountApiException e) {
@@ -148,7 +148,7 @@ public class AccountResource implements BaseJaxrsResource {
try {
AccountData data = json.toAccountData();
UUID uuid = UUID.fromString(accountId);
- accountApi.updateAccount(uuid, data, context.getContext());
+ accountApi.updateAccount(uuid, data, context.createContext());
return getAccount(accountId);
} catch (AccountApiException e) {
if (e.getCode() == ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_ID.getCode()) {
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BaseJaxrsResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BaseJaxrsResource.java
index 02d9f86..3fb0334 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BaseJaxrsResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BaseJaxrsResource.java
@@ -29,8 +29,9 @@ public interface BaseJaxrsResource {
* Query parameters
*/
public static final String QUERY_EXTERNAL_KEY = "external_key";
- public static final String QUERY_REQUESTED_DT = "requested_date";
-
+ public static final String QUERY_REQUESTED_DT = "requested_date";
+ public static final String QUERY_CALL_COMPLETION = "call_completion";
+ public static final String QUERY_CALL_TIMEOUT = "call_timeout_sec";
public static final String ACCOUNTS = "accounts";
public static final String ACCOUNTS_PATH = API_PREFIX + API_VERSION + "/" + ACCOUNTS;
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java
index 90c9e81..d6a94ab 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java
@@ -92,7 +92,7 @@ public class BundleResource implements BaseJaxrsResource {
public Response createBundle(final BundleJson json) {
try {
UUID accountId = UUID.fromString(json.getAccountId());
- final SubscriptionBundle bundle = entitlementApi.createBundleForAccount(accountId, json.getExternalKey(), context.getContext());
+ final SubscriptionBundle bundle = entitlementApi.createBundleForAccount(accountId, json.getExternalKey(), context.createContext());
return uriBuilder.buildResponse(BundleResource.class, "getBundle", bundle.getId());
} catch (EntitlementUserApiException e) {
log.info(String.format("Failed to create bundle %s", json), e);
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/SubscriptionResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/SubscriptionResource.java
index e4b5893..7ae0513 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/SubscriptionResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/SubscriptionResource.java
@@ -18,10 +18,14 @@ package com.ning.billing.jaxrs.resources;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+import java.math.BigDecimal;
import java.util.UUID;
+import java.util.concurrent.TimeoutException;
+import javax.swing.text.html.HTMLDocument.HTMLReader.IsindexAction;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
@@ -45,129 +49,255 @@ import com.ning.billing.catalog.api.ProductCategory;
import com.ning.billing.entitlement.api.user.EntitlementUserApi;
import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
import com.ning.billing.entitlement.api.user.Subscription;
-import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
+import com.ning.billing.entitlement.api.user.SubscriptionTransition;
+import com.ning.billing.invoice.api.EmptyInvoiceNotification;
+import com.ning.billing.invoice.api.InvoiceCreationNotification;
import com.ning.billing.jaxrs.json.SubscriptionJson;
import com.ning.billing.jaxrs.util.Context;
import com.ning.billing.jaxrs.util.JaxrsUriBuilder;
+import com.ning.billing.jaxrs.util.KillbillEventHandler;
+import com.ning.billing.payment.api.PaymentError;
+import com.ning.billing.payment.api.PaymentInfo;
+import com.ning.billing.util.callcontext.CallContext;
import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.userrequest.CompletionUserRequestBase;
@Path(BaseJaxrsResource.SUBSCRIPTIONS_PATH)
public class SubscriptionResource implements BaseJaxrsResource{
- private static final Logger log = LoggerFactory.getLogger(SubscriptionResource.class);
-
- private final DateTimeFormatter DATE_TIME_FORMATTER = ISODateTimeFormat.dateTime();
-
- private final EntitlementUserApi entitlementApi;
- private final Context context;
- private final JaxrsUriBuilder uriBuilder;
- private final Clock clock;
-
- @Inject
- public SubscriptionResource(final JaxrsUriBuilder uriBuilder, final EntitlementUserApi entitlementApi, final Clock clock, final Context context) {
- this.uriBuilder = uriBuilder;
- this.entitlementApi = entitlementApi;
- this.context = context;
- this.clock = clock;
- }
-
- @GET
- @Path("/{subscriptionId:" + UUID_PATTERN + "}")
- @Produces(APPLICATION_JSON)
- public Response getSubscription(@PathParam("subscriptionId") final String subscriptionId) {
-
- UUID uuid = UUID.fromString(subscriptionId);
- Subscription subscription = entitlementApi.getSubscriptionFromId(uuid);
- if (subscription == null) {
- return Response.status(Status.NO_CONTENT).build();
- }
- SubscriptionJson json = new SubscriptionJson(subscription, null, null, null);
- return Response.status(Status.OK).entity(json).build();
- }
-
- @POST
- @Consumes(APPLICATION_JSON)
- @Produces(APPLICATION_JSON)
- public Response createSubscription(SubscriptionJson subscription,
- @QueryParam(QUERY_REQUESTED_DT) String requestedDate) {
-
- try {
- DateTime inputDate = (requestedDate != null) ? DATE_TIME_FORMATTER.parseDateTime(requestedDate) : null;
- UUID uuid = UUID.fromString(subscription.getBundleId());
-
- PlanPhaseSpecifier spec = new PlanPhaseSpecifier(subscription.getProductName(),
- ProductCategory.valueOf(subscription.getProductCategory()),
- BillingPeriod.valueOf(subscription.getBillingPeriod()), subscription.getPriceList(), null);
- Subscription created = entitlementApi.createSubscription(uuid, spec, inputDate, context.getContext());
- return uriBuilder.buildResponse(SubscriptionResource.class, "getSubscription", created.getId());
-
- } catch (EntitlementUserApiException e) {
- log.info(String.format("Failed to create subscription %s", subscription), e);
- return Response.status(Status.BAD_REQUEST).build();
- }
- }
-
- @PUT
- @Produces(APPLICATION_JSON)
- @Consumes(APPLICATION_JSON)
- @Path("/{subscriptionId:" + UUID_PATTERN + "}")
- public Response changeSubscriptionPlan(SubscriptionJson subscription,
- @PathParam("subscriptionId") String subscriptionId,
- @QueryParam(QUERY_REQUESTED_DT) String requestedDate) {
-
- try {
- UUID uuid = UUID.fromString(subscriptionId);
- Subscription current = entitlementApi.getSubscriptionFromId(uuid);
- if (current == null) {
- return Response.status(Status.NO_CONTENT).build();
- }
- DateTime inputDate = (requestedDate != null) ? DATE_TIME_FORMATTER.parseDateTime(requestedDate) : null;
- current.changePlan(subscription.getProductName(), BillingPeriod.valueOf(subscription.getBillingPeriod()), subscription.getPriceList(), inputDate, context.getContext());
-
- return getSubscription(subscriptionId);
- } catch (EntitlementUserApiException e) {
- log.info(String.format("Failed to change plan for subscription %s", subscription), e);
- return Response.status(Status.BAD_REQUEST).build();
- }
- }
-
- @PUT
- @Path("/{subscriptionId:" + UUID_PATTERN + "}/uncancel")
- @Produces(APPLICATION_JSON)
- public Response uncancelSubscriptionPlan(@PathParam("subscriptionId") String subscriptionId) {
- try {
- UUID uuid = UUID.fromString(subscriptionId);
- Subscription current = entitlementApi.getSubscriptionFromId(uuid);
- if (current == null) {
- return Response.status(Status.NO_CONTENT).build();
- }
- current.uncancel(context.getContext());
- return Response.status(Status.OK).build();
- } catch (EntitlementUserApiException e) {
- log.info(String.format("Failed to uncancel plan for subscription %s", subscriptionId), e);
- return Response.status(Status.BAD_REQUEST).build();
- }
- }
-
- @DELETE
- @Path("/{subscriptionId:" + UUID_PATTERN + "}")
- @Produces(APPLICATION_JSON)
- public Response cancelSubscriptionPlan(@PathParam("subscriptionId") String subscriptionId,
- @QueryParam(QUERY_REQUESTED_DT) String requestedDate) {
-
- try {
- UUID uuid = UUID.fromString(subscriptionId);
- Subscription current = entitlementApi.getSubscriptionFromId(uuid);
- if (current == null) {
- return Response.status(Status.NO_CONTENT).build();
- }
- DateTime inputDate = (requestedDate != null) ? DATE_TIME_FORMATTER.parseDateTime(requestedDate) : null;
- current.cancel(inputDate, false, context.getContext());
- return Response.status(Status.OK).build();
-
- } catch (EntitlementUserApiException e) {
- log.info(String.format("Failed to cancel plan for subscription %s", subscriptionId), e);
- return Response.status(Status.BAD_REQUEST).build();
- }
- }
+ private static final Logger log = LoggerFactory.getLogger(SubscriptionResource.class);
+
+ private final DateTimeFormatter DATE_TIME_FORMATTER = ISODateTimeFormat.dateTime();
+
+ private final EntitlementUserApi entitlementApi;
+ private final Context context;
+ private final JaxrsUriBuilder uriBuilder;
+ private final KillbillEventHandler killbillHandler;
+
+ @Inject
+ public SubscriptionResource(final JaxrsUriBuilder uriBuilder, final EntitlementUserApi entitlementApi,
+ final Clock clock, final Context context, final KillbillEventHandler killbillHandler) {
+ this.uriBuilder = uriBuilder;
+ this.entitlementApi = entitlementApi;
+ this.context = context;
+ this.killbillHandler = killbillHandler;
+ }
+
+ @GET
+ @Path("/{subscriptionId:" + UUID_PATTERN + "}")
+ @Produces(APPLICATION_JSON)
+ public Response getSubscription(@PathParam("subscriptionId") final String subscriptionId) {
+
+
+ UUID uuid = UUID.fromString(subscriptionId);
+ Subscription subscription = entitlementApi.getSubscriptionFromId(uuid);
+ if (subscription == null) {
+ return Response.status(Status.NO_CONTENT).build();
+ }
+ SubscriptionJson json = new SubscriptionJson(subscription, null, null, null);
+ return Response.status(Status.OK).entity(json).build();
+ }
+
+ @POST
+ @Consumes(APPLICATION_JSON)
+ @Produces(APPLICATION_JSON)
+ public Response createSubscription(final SubscriptionJson subscription,
+ final @QueryParam(QUERY_REQUESTED_DT) String requestedDate,
+ final @QueryParam(QUERY_CALL_COMPLETION) @DefaultValue("false") Boolean callCompletion,
+ final @QueryParam(QUERY_CALL_TIMEOUT) @DefaultValue("3") long timeoutSec) {
+
+
+ SubscriptionCallCompletionCallback<Subscription> callback = new SubscriptionCallCompletionCallback<Subscription>() {
+ @Override
+ public Subscription doOperation(final CallContext ctx) throws EntitlementUserApiException, InterruptedException, TimeoutException {
+
+ DateTime inputDate = (requestedDate != null) ? DATE_TIME_FORMATTER.parseDateTime(requestedDate) : null;
+ UUID uuid = UUID.fromString(subscription.getBundleId());
+
+ PlanPhaseSpecifier spec = new PlanPhaseSpecifier(subscription.getProductName(),
+ ProductCategory.valueOf(subscription.getProductCategory()),
+ BillingPeriod.valueOf(subscription.getBillingPeriod()), subscription.getPriceList(), null);
+ return entitlementApi.createSubscription(uuid, spec, inputDate, ctx);
+ }
+ @Override
+ public boolean isImmOperation() {
+ return true;
+ }
+ @Override
+ public Response doResponseOk(final Subscription createdSubscription) {
+ return uriBuilder.buildResponse(SubscriptionResource.class, "getSubscription", createdSubscription.getId());
+ }
+ };
+ SubscriptionCallCompletion<Subscription> callCompletionCreation = new SubscriptionCallCompletion<Subscription>();
+ return callCompletionCreation.withSynchronization(callback, timeoutSec, callCompletion);
+ }
+
+ @PUT
+ @Produces(APPLICATION_JSON)
+ @Consumes(APPLICATION_JSON)
+ @Path("/{subscriptionId:" + UUID_PATTERN + "}")
+ public Response changeSubscriptionPlan(final SubscriptionJson subscription,
+ final @PathParam("subscriptionId") String subscriptionId,
+ final @QueryParam(QUERY_REQUESTED_DT) String requestedDate,
+ final @QueryParam(QUERY_CALL_COMPLETION) @DefaultValue("false") Boolean callCompletion,
+ final @QueryParam(QUERY_CALL_TIMEOUT) @DefaultValue("3") long timeoutSec) {
+
+ SubscriptionCallCompletionCallback<Response> callback = new SubscriptionCallCompletionCallback<Response>() {
+
+ private boolean isImmediateOp = true;
+
+ @Override
+ public Response doOperation(CallContext ctx)
+ throws EntitlementUserApiException, InterruptedException,
+ TimeoutException {
+ UUID uuid = UUID.fromString(subscriptionId);
+ Subscription current = entitlementApi.getSubscriptionFromId(uuid);
+ if (current == null) {
+ return Response.status(Status.NO_CONTENT).build();
+ }
+ DateTime inputDate = (requestedDate != null) ? DATE_TIME_FORMATTER.parseDateTime(requestedDate) : null;
+ isImmediateOp = current.changePlan(subscription.getProductName(), BillingPeriod.valueOf(subscription.getBillingPeriod()), subscription.getPriceList(), inputDate, ctx);
+ return Response.status(Status.OK).build();
+ }
+ @Override
+ public boolean isImmOperation() {
+ return isImmediateOp;
+ }
+ @Override
+ public Response doResponseOk(Response operationResponse) {
+ if (operationResponse.getStatus() != Status.OK.getStatusCode()) {
+ return operationResponse;
+ }
+ return getSubscription(subscriptionId);
+ }
+ };
+ SubscriptionCallCompletion<Response> callCompletionCreation = new SubscriptionCallCompletion<Response>();
+ return callCompletionCreation.withSynchronization(callback, timeoutSec, callCompletion);
+ }
+
+ @PUT
+ @Path("/{subscriptionId:" + UUID_PATTERN + "}/uncancel")
+ @Produces(APPLICATION_JSON)
+ public Response uncancelSubscriptionPlan(@PathParam("subscriptionId") String subscriptionId) {
+ try {
+ UUID uuid = UUID.fromString(subscriptionId);
+ Subscription current = entitlementApi.getSubscriptionFromId(uuid);
+ if (current == null) {
+ return Response.status(Status.NO_CONTENT).build();
+ }
+ current.uncancel(context.createContext());
+ return Response.status(Status.OK).build();
+ } catch (EntitlementUserApiException e) {
+ log.info(String.format("Failed to uncancel plan for subscription %s", subscriptionId), e);
+ return Response.status(Status.BAD_REQUEST).build();
+ }
+ }
+
+ @DELETE
+ @Path("/{subscriptionId:" + UUID_PATTERN + "}")
+ @Produces(APPLICATION_JSON)
+ public Response cancelSubscriptionPlan(final @PathParam("subscriptionId") String subscriptionId,
+ final @QueryParam(QUERY_REQUESTED_DT) String requestedDate,
+ final @QueryParam(QUERY_CALL_COMPLETION) @DefaultValue("false") Boolean callCompletion,
+ final @QueryParam(QUERY_CALL_TIMEOUT) @DefaultValue("3") long timeoutSec) {
+
+ SubscriptionCallCompletionCallback<Response> callback = new SubscriptionCallCompletionCallback<Response>() {
+
+ private boolean isImmediateOp = true;
+
+ @Override
+ public Response doOperation(CallContext ctx)
+ throws EntitlementUserApiException, InterruptedException,
+ TimeoutException {
+ UUID uuid = UUID.fromString(subscriptionId);
+ Subscription current = entitlementApi.getSubscriptionFromId(uuid);
+ if (current == null) {
+ return Response.status(Status.NO_CONTENT).build();
+ }
+ DateTime inputDate = (requestedDate != null) ? DATE_TIME_FORMATTER.parseDateTime(requestedDate) : null;
+ isImmediateOp = current.cancel(inputDate, false, ctx);
+ return Response.status(Status.OK).build();
+ }
+ @Override
+ public boolean isImmOperation() {
+ return isImmediateOp;
+ }
+ @Override
+ public Response doResponseOk(Response operationResponse) {
+ return operationResponse;
+ }
+ };
+ SubscriptionCallCompletion<Response> callCompletionCreation = new SubscriptionCallCompletion<Response>();
+ return callCompletionCreation.withSynchronization(callback, timeoutSec, callCompletion);
+ }
+
+ private final static class CompletionUserRequestSubscription extends CompletionUserRequestBase {
+
+ public CompletionUserRequestSubscription(final UUID userToken) {
+ super(userToken);
+ }
+ @Override
+ public void onSubscriptionTransition(SubscriptionTransition curEvent) {
+ log.info(String.format("Got event SubscriptionTransition token = %s, type = %s, remaining = %d ",
+ curEvent.getUserToken(), curEvent.getTransitionType(), curEvent.getRemainingEventsForUserOperation()));
+ }
+ @Override
+ public void onEmptyInvoice(final EmptyInvoiceNotification curEvent) {
+ log.info(String.format("Got event EmptyInvoiceNotification token = %s ", curEvent.getUserToken()));
+ notifyForCompletion();
+ }
+ @Override
+ public void onInvoiceCreation(InvoiceCreationNotification curEvent) {
+ log.info(String.format("Got event InvoiceCreationNotification token = %s ", curEvent.getUserToken()));
+ if (curEvent.getAmountOwed().compareTo(BigDecimal.ZERO) <= 0) {
+ notifyForCompletion();
+ }
+ }
+ @Override
+ public void onPaymentInfo(PaymentInfo curEvent) {
+ log.info(String.format("Got event PaymentInfo token = %s ", curEvent.getUserToken()));
+ notifyForCompletion();
+ }
+ @Override
+ public void onPaymentError(PaymentError curEvent) {
+ log.info(String.format("Got event PaymentError token = %s ", curEvent.getUserToken()));
+ notifyForCompletion();
+ }
+ }
+
+ private interface SubscriptionCallCompletionCallback<T> {
+ public T doOperation(final CallContext ctx) throws EntitlementUserApiException, InterruptedException, TimeoutException;
+ public boolean isImmOperation();
+ public Response doResponseOk(final T operationResponse);
+ }
+
+ private class SubscriptionCallCompletion<T> {
+
+ public Response withSynchronization(final SubscriptionCallCompletionCallback<T> callback, final long timeoutSec, final boolean callCompletion) {
+
+ CallContext ctx = context.createContext();
+ CompletionUserRequestSubscription waiter = callCompletion ? new CompletionUserRequestSubscription(ctx.getUserToken()) : null;
+ try {
+ if (waiter != null) {
+ killbillHandler.registerCompletionUserRequestWaiter(waiter);
+ }
+ T operationValue = callback.doOperation(ctx);
+ if (waiter != null && callback.isImmOperation()) {
+ waiter.waitForCompletion(timeoutSec * 1000);
+ }
+ return callback.doResponseOk(operationValue);
+ } catch (EntitlementUserApiException e) {
+ log.info(String.format("Failed to complete operation"), e);
+ return Response.status(Status.BAD_REQUEST).build();
+ } catch (InterruptedException e) {
+ return Response.status(Status.INTERNAL_SERVER_ERROR).build();
+ } catch (TimeoutException e) {
+ return Response.status(Status.fromStatusCode(408)).build();
+ } finally {
+ if (waiter != null) {
+ killbillHandler.unregisterCompletionUserRequestWaiter(waiter);
+ }
+ }
+ }
+ }
}
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/util/Context.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/util/Context.java
index 9597e4d..004a468 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/util/Context.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/util/Context.java
@@ -15,6 +15,8 @@
*/
package com.ning.billing.jaxrs.util;
+import java.util.UUID;
+
import com.google.inject.Inject;
import com.ning.billing.util.callcontext.CallContext;
import com.ning.billing.util.callcontext.CallContextFactory;
@@ -36,7 +38,7 @@ public class Context {
}
// Simplistic until we decide how to populate that
- public CallContext getContext() {
- return contextFactory.createCallContext("Unknown", origin, userType);
+ public CallContext createContext() {
+ return contextFactory.createCallContext("Unknown", origin, userType, UUID.randomUUID());
}
}
diff --git a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
index 153c63b..4223ece 100644
--- a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
+++ b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
@@ -153,7 +153,8 @@ public class DefaultPaymentApi implements PaymentApi {
return Either.left(new PaymentError("invoice_balance_0",
"Invoice balance was 0 or less",
paymentAttempt.getAccountId(),
- paymentAttempt.getInvoiceId()));
+ paymentAttempt.getInvoiceId(),
+ context.getUserToken()));
}
else {
PaymentAttempt newPaymentAttempt = new PaymentAttempt.Builder(paymentAttempt)
@@ -169,7 +170,8 @@ public class DefaultPaymentApi implements PaymentApi {
return Either.left(new PaymentError("retry_payment_error",
"Could not load payment attempt, invoice or account for id " + paymentAttemptId,
paymentAttempt.getAccountId(),
- paymentAttempt.getInvoiceId()));
+ paymentAttempt.getInvoiceId(),
+ context.getUserToken()));
}
@Override
@@ -187,7 +189,8 @@ public class DefaultPaymentApi implements PaymentApi {
Either<PaymentError, PaymentInfo> result = Either.left(new PaymentError("invoice_balance_0",
"Invoice balance was 0 or less",
account.getId(),
- UUID.fromString(invoiceId)));
+ UUID.fromString(invoiceId),
+ context.getUserToken()));
processedPaymentsOrErrors.add(result);
}
else if (invoice.isMigrationInvoice()) {
@@ -195,7 +198,8 @@ public class DefaultPaymentApi implements PaymentApi {
Either<PaymentError, PaymentInfo> result = Either.left(new PaymentError("migration invoice",
"Invoice balance was a migration invoice",
account.getId(),
- UUID.fromString(invoiceId)));
+ UUID.fromString(invoiceId),
+ context.getUserToken()));
processedPaymentsOrErrors.add(result);
}
else {
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java b/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java
index 96d7e0b..fed137e 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/PaymentSqlDao.java
@@ -187,11 +187,13 @@ public interface PaymentSqlDao extends Transactional<PaymentSqlDao>, CloseMe, Tr
String paymentMethodId = rs.getString("payment_method_id");
String paymentMethod = rs.getString("payment_method");
String cardType = rs.getString("card_type");
- String cardCountry = rs.getString("card_country");
+ String cardCountry = rs.getString("card_country");
DateTime effectiveDate = getDate(rs, "effective_dt");
DateTime createdDate = getDate(rs, "created_dt");
DateTime updatedDate = getDate(rs, "updated_dt");
+ UUID userToken = null; //rs.getString("user_token") != null ? UUID.fromString(rs.getString("user_token")) : null;
+
return new PaymentInfo(paymentId,
amount,
refundAmount,
@@ -204,6 +206,7 @@ public interface PaymentSqlDao extends Transactional<PaymentSqlDao>, CloseMe, Tr
paymentMethod,
cardType,
cardCountry,
+ userToken,
effectiveDate,
createdDate,
updatedDate);
diff --git a/payment/src/main/java/com/ning/billing/payment/provider/NoOpPaymentProviderPlugin.java b/payment/src/main/java/com/ning/billing/payment/provider/NoOpPaymentProviderPlugin.java
index 280d1e0..c583415 100644
--- a/payment/src/main/java/com/ning/billing/payment/provider/NoOpPaymentProviderPlugin.java
+++ b/payment/src/main/java/com/ning/billing/payment/provider/NoOpPaymentProviderPlugin.java
@@ -56,7 +56,7 @@ public class NoOpPaymentProviderPlugin implements PaymentProviderPlugin {
return Either.left(new PaymentError("unsupported",
"Account creation not supported in this plugin",
account.getId(),
- null));
+ null, null));
}
@Override
diff --git a/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java b/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java
index 47713bb..ff505c1 100644
--- a/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java
+++ b/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java
@@ -60,7 +60,7 @@ public class MockPaymentProviderPlugin implements PaymentProviderPlugin {
@Override
public Either<PaymentError, PaymentInfo> processInvoice(Account account, Invoice invoice) {
if (makeNextInvoiceFail.getAndSet(false)) {
- return Either.left(new PaymentError("unknown", "test error", account.getId(), invoice.getId()));
+ return Either.left(new PaymentError("unknown", "test error", account.getId(), invoice.getId(), null));
}
else {
PaymentInfo payment = new PaymentInfo.Builder().setPaymentId(UUID.randomUUID().toString())
@@ -83,7 +83,7 @@ public class MockPaymentProviderPlugin implements PaymentProviderPlugin {
PaymentInfo payment = payments.get(paymentId);
if (payment == null) {
- return Either.left(new PaymentError("notfound", "No payment found for id " + paymentId, null, null));
+ return Either.left(new PaymentError("notfound", "No payment found for id " + paymentId, null, null, null));
}
else {
return Either.right(payment);
@@ -102,7 +102,7 @@ public class MockPaymentProviderPlugin implements PaymentProviderPlugin {
return Either.right(id);
}
else {
- return Either.left(new PaymentError("unknown", "Did not get account to create payment provider account", null, null));
+ return Either.left(new PaymentError("unknown", "Did not get account to create payment provider account", null, null, null));
}
}
@@ -112,7 +112,7 @@ public class MockPaymentProviderPlugin implements PaymentProviderPlugin {
return Either.right(accounts.get(accountKey));
}
else {
- return Either.left(new PaymentError("unknown", "Did not get account for accountKey " + accountKey, null, null));
+ return Either.left(new PaymentError("unknown", "Did not get account for accountKey " + accountKey, null, null, null));
}
}
@@ -144,7 +144,7 @@ public class MockPaymentProviderPlugin implements PaymentProviderPlugin {
realPaymentMethod = new CreditCardPaymentMethodInfo.Builder(ccPaymentMethod).setId(paymentMethodId).build();
}
if (realPaymentMethod == null) {
- return Either.left(new PaymentError("unsupported", "Payment method " + paymentMethod.getType() + " not supported by the plugin", null, null));
+ return Either.left(new PaymentError("unsupported", "Payment method " + paymentMethod.getType() + " not supported by the plugin", null, null, null));
}
else {
if (shouldBeDefault) {
@@ -155,11 +155,11 @@ public class MockPaymentProviderPlugin implements PaymentProviderPlugin {
}
}
else {
- return Either.left(new PaymentError("noaccount", "Could not retrieve account for accountKey " + accountKey, null, null));
+ return Either.left(new PaymentError("noaccount", "Could not retrieve account for accountKey " + accountKey, null, null, null));
}
}
else {
- return Either.left(new PaymentError("unknown", "Could not create add payment method " + paymentMethod + " for " + accountKey, null, null));
+ return Either.left(new PaymentError("unknown", "Could not create add payment method " + paymentMethod + " for " + accountKey, null, null, null));
}
}
@@ -203,7 +203,7 @@ public class MockPaymentProviderPlugin implements PaymentProviderPlugin {
realPaymentMethod = new CreditCardPaymentMethodInfo.Builder(ccPaymentMethod).build();
}
if (realPaymentMethod == null) {
- return Either.left(new PaymentError("unsupported", "Payment method " + paymentMethod.getType() + " not supported by the plugin", null, null));
+ return Either.left(new PaymentError("unsupported", "Payment method " + paymentMethod.getType() + " not supported by the plugin", null, null, null));
}
else {
paymentMethods.put(paymentMethod.getId(), paymentMethod);
@@ -211,7 +211,7 @@ public class MockPaymentProviderPlugin implements PaymentProviderPlugin {
}
}
else {
- return Either.left(new PaymentError("unknown", "Could not create add payment method " + paymentMethod + " for " + accountKey, null, null));
+ return Either.left(new PaymentError("unknown", "Could not create add payment method " + paymentMethod + " for " + accountKey, null, null, null));
}
}
@@ -221,11 +221,11 @@ public class MockPaymentProviderPlugin implements PaymentProviderPlugin {
if (paymentMethodInfo != null) {
if (Boolean.FALSE.equals(paymentMethodInfo.getDefaultMethod()) || paymentMethodInfo.getDefaultMethod() == null) {
if (paymentMethods.remove(paymentMethodId) == null) {
- return Either.left(new PaymentError("unknown", "Did not get any result back", null, null));
+ return Either.left(new PaymentError("unknown", "Did not get any result back", null, null, null));
}
}
else {
- return Either.left(new PaymentError("error", "Cannot delete default payment method", null, null));
+ return Either.left(new PaymentError("error", "Cannot delete default payment method", null, null, null));
}
}
return Either.right(null);
@@ -234,7 +234,7 @@ public class MockPaymentProviderPlugin implements PaymentProviderPlugin {
@Override
public Either<PaymentError, PaymentMethodInfo> getPaymentMethodInfo(String paymentMethodId) {
if (paymentMethodId == null) {
- return Either.left(new PaymentError("unknown", "Could not retrieve payment method for paymentMethodId " + paymentMethodId, null, null));
+ return Either.left(new PaymentError("unknown", "Could not retrieve payment method for paymentMethodId " + paymentMethodId, null, null, null));
}
return Either.right(paymentMethods.get(paymentMethodId));
diff --git a/payment/src/test/java/com/ning/billing/payment/util/TestSyncWaitOnEventBus.java b/payment/src/test/java/com/ning/billing/payment/util/TestSyncWaitOnEventBus.java
index 4fa635e..963f0b2 100644
--- a/payment/src/test/java/com/ning/billing/payment/util/TestSyncWaitOnEventBus.java
+++ b/payment/src/test/java/com/ning/billing/payment/util/TestSyncWaitOnEventBus.java
@@ -54,6 +54,11 @@ public class TestSyncWaitOnEventBus {
public BusEventType getBusEventType() {
return null;
}
+
+ @Override
+ public UUID getUserToken() {
+ return null;
+ }
}
private static final class TestResponse implements EventBusResponse<UUID> {
@@ -78,6 +83,10 @@ public class TestSyncWaitOnEventBus {
public BusEventType getBusEventType() {
return null;
}
+ @Override
+ public UUID getUserToken() {
+ return null;
+ }
}
private Bus eventBus;
server/pom.xml 844(+425 -419)
diff --git a/server/pom.xml b/server/pom.xml
index 6280399..12d571e 100644
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -1,433 +1,439 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- ~ 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. -->
+ 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. -->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>com.ning.billing</groupId>
- <artifactId>killbill</artifactId>
- <version>0.1.8-SNAPSHOT</version>
- <relativePath>../pom.xml</relativePath>
- </parent>
- <artifactId>killbill-server</artifactId>
- <name>killbill-server</name>
- <packaging>war</packaging>
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>com.ning.billing</groupId>
+ <artifactId>killbill</artifactId>
+ <version>0.1.8-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+ <artifactId>killbill-server</artifactId>
+ <name>killbill-server</name>
+ <packaging>war</packaging>
- <properties>
- <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
- <guice.version>3.0</guice.version>
- <jersey.version>1.12</jersey.version>
- <jetty.version>8.1.2.v20120308</jetty.version>
- <logback.version>1.0.1</logback.version>
- <metrics.version>2.1.1</metrics.version>
- <slf4j.version>1.6.4</slf4j.version>
- <skeleton.version>0.1.1-SNAPSHOT</skeleton.version>
- <async-http-client.version>1.6.5</async-http-client.version>
- </properties>
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <guice.version>3.0</guice.version>
+ <jersey.version>1.12</jersey.version>
+ <jetty.version>8.1.2.v20120308</jetty.version>
+ <logback.version>1.0.1</logback.version>
+ <metrics.version>2.1.2</metrics.version>
+ <slf4j.version>1.6.4</slf4j.version>
+ <skeleton.version>0.1.2</skeleton.version>
+ <async-http-client.version>1.6.5</async-http-client.version>
+ </properties>
- <dependencies>
+ <dependencies>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-http</artifactId>
- <version>${jetty.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-io</artifactId>
- <version>${jetty.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-util</artifactId>
- <version>${jetty.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-server</artifactId>
- <version>${jetty.version}</version>
- <scope>provided</scope>
- </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-http</artifactId>
+ <version>${jetty.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-io</artifactId>
+ <version>${jetty.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-util</artifactId>
+ <version>${jetty.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-server</artifactId>
+ <version>${jetty.version}</version>
+ <scope>provided</scope>
+ </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-deploy</artifactId>
- <version>${jetty.version}</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-jmx</artifactId>
- <version>${jetty.version}</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jetty</groupId>
- <artifactId>jetty-xml</artifactId>
- <version>${jetty.version}</version>
- <scope>test</scope>
- </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-deploy</artifactId>
+ <version>${jetty.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-jmx</artifactId>
+ <version>${jetty.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-xml</artifactId>
+ <version>${jetty.version}</version>
+ <scope>test</scope>
+ </dependency>
- <!-- NOT in master POM; include version as well -->
- <dependency>
- <groupId>org.weakref</groupId>
- <artifactId>jmxutils</artifactId>
- <version>1.12</version>
- </dependency>
- <dependency>
- <groupId>com.google.inject.extensions</groupId>
- <artifactId>guice-servlet</artifactId>
- <version>${guice.version}</version>
- </dependency>
- <dependency>
- <groupId>com.yammer.metrics</groupId>
- <artifactId>metrics-core</artifactId>
- <version>${metrics.version}</version>
- </dependency>
- <dependency>
- <groupId>com.yammer.metrics</groupId>
- <artifactId>metrics-guice</artifactId>
- <version>${metrics.version}</version>
- </dependency>
- <dependency>
- <groupId>com.sun.jersey</groupId>
- <artifactId>jersey-server</artifactId>
- <version>${jersey.version}</version>
- </dependency>
- <dependency>
- <groupId>com.sun.jersey.contribs</groupId>
- <artifactId>jersey-guice</artifactId>
- <version>${jersey.version}</version>
- </dependency>
- <dependency>
- <groupId>javax.servlet</groupId>
- <artifactId>javax.servlet-api</artifactId>
- <version>3.0.1</version>
- </dependency>
- <!-- Do we want to depend on skeleton -->
- <dependency>
- <groupId>com.ning.jetty</groupId>
- <artifactId>ning-service-skeleton-base</artifactId>
- <version>${skeleton.version}</version>
- </dependency>
- <dependency>
- <groupId>com.ning.jetty</groupId>
- <artifactId>ning-service-skeleton-jdbi</artifactId>
- <version>${skeleton.version}</version>
- </dependency>
- <dependency>
- <groupId>com.ning.jetty</groupId>
- <artifactId>ning-service-skeleton-log4j</artifactId>
- <version>${skeleton.version}</version>
- <classifier>selfcontained</classifier>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <version>${slf4j.version}</version>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>jcl-over-slf4j</artifactId>
- <version>${slf4j.version}</version>
- <scope>runtime</scope>
- </dependency>
- <dependency>
- <groupId>ch.qos.logback</groupId>
- <artifactId>logback-core</artifactId>
- <version>${logback.version}</version>
- </dependency>
- <dependency>
- <groupId>ch.qos.logback</groupId>
- <artifactId>logback-classic</artifactId>
- <version>${logback.version}</version>
- </dependency>
+ <!-- NOT in master POM; include version as well -->
+ <dependency>
+ <groupId>org.weakref</groupId>
+ <artifactId>jmxutils</artifactId>
+ <version>1.12</version>
+ </dependency>
+ <dependency>
+ <groupId>com.google.inject.extensions</groupId>
+ <artifactId>guice-servlet</artifactId>
+ <version>${guice.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.yammer.metrics</groupId>
+ <artifactId>metrics-core</artifactId>
+ <version>${metrics.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.yammer.metrics</groupId>
+ <artifactId>metrics-guice</artifactId>
+ <version>${metrics.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.sun.jersey</groupId>
+ <artifactId>jersey-server</artifactId>
+ <version>${jersey.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.sun.jersey.contribs</groupId>
+ <artifactId>jersey-guice</artifactId>
+ <version>${jersey.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>javax.servlet-api</artifactId>
+ <version>3.0.1</version>
+ </dependency>
+ <!-- Do we want to depend on skeleton -->
+ <dependency>
+ <groupId>com.ning.jetty</groupId>
+ <artifactId>ning-service-skeleton-base</artifactId>
+ <version>${skeleton.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.ning.jetty</groupId>
+ <artifactId>ning-service-skeleton-jdbi</artifactId>
+ <version>${skeleton.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.ning.jetty</groupId>
+ <artifactId>ning-service-skeleton-log4j</artifactId>
+ <version>${skeleton.version}</version>
+ <classifier>selfcontained</classifier>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>${slf4j.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>jcl-over-slf4j</artifactId>
+ <version>${slf4j.version}</version>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-core</artifactId>
+ <version>${logback.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ <version>${logback.version}</version>
+ </dependency>
- <!-- FROM MASTER POM / LIBRARY -->
- <dependency>
- <groupId>com.ning.billing</groupId>
- <artifactId>killbill-api</artifactId>
- </dependency>
- <dependency>
- <groupId>com.ning.billing</groupId>
- <artifactId>killbill-jaxrs</artifactId>
- </dependency>
- <dependency>
- <groupId>com.ning.billing</groupId>
- <artifactId>killbill-beatrix</artifactId>
- </dependency>
- <dependency>
- <groupId>com.ning.billing</groupId>
- <artifactId>killbill-util</artifactId>
- </dependency>
- <dependency>
- <groupId>com.ning.billing</groupId>
- <artifactId>killbill-entitlement</artifactId>
- </dependency>
- <dependency>
- <groupId>com.ning.billing</groupId>
- <artifactId>killbill-invoice</artifactId>
- </dependency>
- <dependency>
- <groupId>com.ning.billing</groupId>
- <artifactId>killbill-payment</artifactId>
- </dependency>
- <dependency>
- <groupId>com.ning.billing</groupId>
- <artifactId>killbill-catalog</artifactId>
- </dependency>
- <dependency>
- <groupId>com.ning.billing</groupId>
- <artifactId>killbill-analytics</artifactId>
- </dependency>
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- <version>11.0.2</version>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>com.google.inject</groupId>
- <artifactId>guice</artifactId>
- <scope>compile</scope>
- </dependency>
- <dependency><!-- Needed by jmxutils -->
- <groupId>com.google.inject.extensions</groupId>
- <artifactId>guice-multibindings</artifactId>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>mysql</groupId>
- <artifactId>mysql-connector-java</artifactId>
- <scope>runtime</scope>
- </dependency>
- <dependency>
- <groupId>org.antlr</groupId>
- <artifactId>stringtemplate</artifactId>
- <scope>runtime</scope>
- </dependency>
- <dependency>
- <groupId>org.skife.config</groupId>
- <artifactId>config-magic</artifactId>
- </dependency>
- <dependency>
- <groupId>javax.ws.rs</groupId>
- <artifactId>jsr311-api</artifactId>
- </dependency>
- <dependency>
- <groupId>joda-time</groupId>
- <artifactId>joda-time</artifactId>
- </dependency>
- <dependency>
- <groupId>com.ning</groupId>
- <artifactId>async-http-client</artifactId>
- <version>${async-http-client.version}</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.testng</groupId>
- <artifactId>testng</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>com.ning.billing</groupId>
- <artifactId>killbill-util</artifactId>
- <type>test-jar</type>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>com.ning.billing</groupId>
- <artifactId>killbill-beatrix</artifactId>
- <type>test-jar</type>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>mysql</groupId>
- <artifactId>mysql-connector-mxj</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>mysql</groupId>
- <artifactId>mysql-connector-mxj-db-files</artifactId>
- <scope>test</scope>
- </dependency>
- </dependencies>
- <build>
- <resources>
- <resource>
- <directory>${basedir}/src/main/resources</directory>
- </resource>
- </resources>
- <plugins>
- <plugin>
- <groupId>com.ning.maven.plugins</groupId>
- <artifactId>maven-dependency-versions-check-plugin</artifactId>
- <version>2.0.2</version>
- <configuration>
- <failBuildInCaseOfConflict>true</failBuildInCaseOfConflict>
- </configuration>
- <executions>
- <execution>
- <phase>verify</phase>
- <goals>
- <goal>check</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
- <plugin>
- <!-- To make eclipse happy -->
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-resources-plugin</artifactId>
- <version>2.5</version>
- </plugin>
- <plugin>
- <groupId>com.ning.maven.plugins</groupId>
- <artifactId>maven-duplicate-finder-plugin</artifactId>
- <version>1.0.2</version>
- <configuration>
- <failBuildInCaseOfConflict>false</failBuildInCaseOfConflict>
- <!-- That's for Jetty -->
- <ignoredResources>
- <ignoredResource>about.html</ignoredResource>
- </ignoredResources>
- </configuration>
- <executions>
- <execution>
- <phase>verify</phase>
- <goals>
- <goal>check</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- <version>2.3.2</version>
- <configuration>
- <source>1.6</source>
- <target>1.6</target>
- </configuration>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-dependency-plugin</artifactId>
- <version>2.3</version>
- <executions>
- <execution>
- <id>analyze</id>
- <goals>
- <goal>analyze-only</goal>
- </goals>
- <configuration>
- <ignoreNonCompile>true</ignoreNonCompile>
- <failOnWarning>false</failOnWarning>
- </configuration>
- </execution>
- </executions>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-release-plugin</artifactId>
- <version>2.2.1</version>
- <configuration>
- <mavenExecutorId>forked-path</mavenExecutorId>
- </configuration>
- <dependencies>
- <dependency>
- <groupId>org.apache.maven.scm</groupId>
- <artifactId>maven-scm-provider-gitexe</artifactId>
- <version>1.4</version>
- </dependency>
- <dependency>
- <groupId>org.codehaus.plexus</groupId>
- <artifactId>plexus-utils</artifactId>
- <version>1.5.9</version>
- </dependency>
- </dependencies>
- </plugin>
- <plugin>
- <!-- TODO: fix for http://jira.codehaus.org/browse/MSITE-286? -->
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-site-plugin</artifactId>
- <version>3.0</version>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-surefire-plugin</artifactId>
- <version>2.6</version>
- <configuration>
- <useManifestOnlyJar>false</useManifestOnlyJar>
- <systemPropertyVariables>
- <log4j.configuration>file:${project.basedir}/src/test/resources/log4j.xml</log4j.configuration>
- <xn.billing.zuora.useSluggedRatePlans>false</xn.billing.zuora.useSluggedRatePlans>
- </systemPropertyVariables>
- <groups>fast,slow</groups>
- </configuration>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-war-plugin</artifactId>
- <version>2.1.1</version>
- <configuration>
- <attachClasses>true</attachClasses>
- </configuration>
- </plugin>
- <plugin>
- <groupId>org.mortbay.jetty</groupId>
- <artifactId>jetty-maven-plugin</artifactId>
- <version>${jetty.version}</version>
- <dependencies>
- <dependency><!-- For LogLevelCounterAppender -->
- <groupId>com.ning.jetty</groupId>
- <artifactId>ning-service-skeleton-log4j</artifactId>
- <version>${skeleton.version}</version>
- <classifier>selfcontained</classifier>
- <scope>runtime</scope>
- </dependency>
- <!-- Needed to redirect Jetty logs to slf4j -->
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <version>${slf4j.version}</version>
- </dependency>
- <dependency>
- <groupId>ch.qos.logback</groupId>
- <artifactId>logback-core</artifactId>
- <version>${logback.version}</version>
- </dependency>
- <dependency>
- <groupId>ch.qos.logback</groupId>
- <artifactId>logback-classic</artifactId>
- <version>${logback.version}</version>
- </dependency>
- </dependencies>
- <configuration>
- <scanIntervalSeconds>60</scanIntervalSeconds>
- <systemProperties>
- <systemProperty>
- <name>logback.configurationFile</name>
- <value>file:${basedir}/src/main/resources/logback.xml</value>
- </systemProperty>
- </systemProperties>
- </configuration>
- </plugin>
- </plugins>
- </build>
+ <!-- FROM MASTER POM / LIBRARY -->
+ <dependency>
+ <groupId>com.ning.billing</groupId>
+ <artifactId>killbill-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.ning.billing</groupId>
+ <artifactId>killbill-jaxrs</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.ning.billing</groupId>
+ <artifactId>killbill-beatrix</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.ning.billing</groupId>
+ <artifactId>killbill-util</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.ning.billing</groupId>
+ <artifactId>killbill-entitlement</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.ning.billing</groupId>
+ <artifactId>killbill-invoice</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.ning.billing</groupId>
+ <artifactId>killbill-payment</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.ning.billing</groupId>
+ <artifactId>killbill-catalog</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.ning.billing</groupId>
+ <artifactId>killbill-analytics</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ <version>11.0.2</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.google.inject</groupId>
+ <artifactId>guice</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ <dependency><!-- Needed by jmxutils -->
+ <groupId>com.google.inject.extensions</groupId>
+ <artifactId>guice-multibindings</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>mysql</groupId>
+ <artifactId>mysql-connector-java</artifactId>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.antlr</groupId>
+ <artifactId>stringtemplate</artifactId>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.skife.config</groupId>
+ <artifactId>config-magic</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>javax.ws.rs</groupId>
+ <artifactId>jsr311-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>joda-time</groupId>
+ <artifactId>joda-time</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.ning</groupId>
+ <artifactId>async-http-client</artifactId>
+ <version>${async-http-client.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.testng</groupId>
+ <artifactId>testng</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.ning.billing</groupId>
+ <artifactId>killbill-util</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.ning.billing</groupId>
+ <artifactId>killbill-beatrix</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.ning.billing</groupId>
+ <artifactId>killbill-payment</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>mysql</groupId>
+ <artifactId>mysql-connector-mxj</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>mysql</groupId>
+ <artifactId>mysql-connector-mxj-db-files</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <resources>
+ <resource>
+ <directory>${basedir}/src/main/resources</directory>
+ </resource>
+ </resources>
+ <plugins>
+ <plugin>
+ <groupId>com.ning.maven.plugins</groupId>
+ <artifactId>maven-dependency-versions-check-plugin</artifactId>
+ <version>2.0.2</version>
+ <configuration>
+ <failBuildInCaseOfConflict>true</failBuildInCaseOfConflict>
+ </configuration>
+ <executions>
+ <execution>
+ <phase>verify</phase>
+ <goals>
+ <goal>check</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <!-- To make eclipse happy -->
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-resources-plugin</artifactId>
+ <version>2.5</version>
+ </plugin>
+ <plugin>
+ <groupId>com.ning.maven.plugins</groupId>
+ <artifactId>maven-duplicate-finder-plugin</artifactId>
+ <version>1.0.2</version>
+ <configuration>
+ <failBuildInCaseOfConflict>false</failBuildInCaseOfConflict>
+ <!-- That's for Jetty -->
+ <ignoredResources>
+ <ignoredResource>about.html</ignoredResource>
+ </ignoredResources>
+ </configuration>
+ <executions>
+ <execution>
+ <phase>verify</phase>
+ <goals>
+ <goal>check</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>2.3.2</version>
+ <configuration>
+ <source>1.6</source>
+ <target>1.6</target>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <version>2.3</version>
+ <executions>
+ <execution>
+ <id>analyze</id>
+ <goals>
+ <goal>analyze-only</goal>
+ </goals>
+ <configuration>
+ <ignoreNonCompile>true</ignoreNonCompile>
+ <failOnWarning>false</failOnWarning>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-release-plugin</artifactId>
+ <version>2.2.1</version>
+ <configuration>
+ <mavenExecutorId>forked-path</mavenExecutorId>
+ </configuration>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.maven.scm</groupId>
+ <artifactId>maven-scm-provider-gitexe</artifactId>
+ <version>1.4</version>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.plexus</groupId>
+ <artifactId>plexus-utils</artifactId>
+ <version>1.5.9</version>
+ </dependency>
+ </dependencies>
+ </plugin>
+ <plugin>
+ <!-- TODO: fix for http://jira.codehaus.org/browse/MSITE-286? -->
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-site-plugin</artifactId>
+ <version>3.0</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>2.6</version>
+ <configuration>
+ <useManifestOnlyJar>false</useManifestOnlyJar>
+ <systemPropertyVariables>
+ <log4j.configuration>file:${project.basedir}/src/test/resources/log4j.xml</log4j.configuration>
+ <xn.billing.zuora.useSluggedRatePlans>false</xn.billing.zuora.useSluggedRatePlans>
+ </systemPropertyVariables>
+ <groups>fast,slow</groups>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-war-plugin</artifactId>
+ <version>2.1.1</version>
+ <configuration>
+ <attachClasses>true</attachClasses>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.mortbay.jetty</groupId>
+ <artifactId>jetty-maven-plugin</artifactId>
+ <version>${jetty.version}</version>
+ <dependencies>
+ <dependency><!-- For LogLevelCounterAppender -->
+ <groupId>com.ning.jetty</groupId>
+ <artifactId>ning-service-skeleton-log4j</artifactId>
+ <version>${skeleton.version}</version>
+ <classifier>selfcontained</classifier>
+ <scope>runtime</scope>
+ </dependency>
+ <!-- Needed to redirect Jetty logs to slf4j -->
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>${slf4j.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-core</artifactId>
+ <version>${logback.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ <version>${logback.version}</version>
+ </dependency>
+ </dependencies>
+ <configuration>
+ <scanIntervalSeconds>60</scanIntervalSeconds>
+ <systemProperties>
+ <systemProperty>
+ <name>logback.configurationFile</name>
+ <value>file:${basedir}/src/main/resources/logback.xml</value>
+ </systemProperty>
+ </systemProperties>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
</project>
diff --git a/server/src/main/java/com/ning/billing/server/listeners/KillbillGuiceListener.java b/server/src/main/java/com/ning/billing/server/listeners/KillbillGuiceListener.java
index 424c10f..11de6ec 100644
--- a/server/src/main/java/com/ning/billing/server/listeners/KillbillGuiceListener.java
+++ b/server/src/main/java/com/ning/billing/server/listeners/KillbillGuiceListener.java
@@ -17,17 +17,15 @@ package com.ning.billing.server.listeners;
import com.ning.billing.beatrix.lifecycle.DefaultLifecycle;
-import com.ning.billing.entitlement.api.user.SubscriptionTransition;
+import com.ning.billing.jaxrs.util.KillbillEventHandler;
import com.ning.billing.server.config.KillbillServerConfig;
import com.ning.billing.server.healthchecks.KillbillHealthcheck;
import com.ning.billing.server.modules.KillbillServerModule;
import com.ning.billing.util.bus.Bus;
-import com.ning.billing.util.bus.BusEvent;
import com.ning.billing.util.bus.BusService;
import com.ning.jetty.base.modules.ServerModuleBuilder;
import com.ning.jetty.core.listeners.SetupServer;
-import com.google.common.eventbus.Subscribe;
import com.google.inject.Injector;
import com.google.inject.Module;
@@ -71,8 +69,7 @@ public class KillbillGuiceListener extends SetupServer
theInjector = injector(event);
killbillLifecycle = theInjector.getInstance(DefaultLifecycle.class);
killbillBusService = theInjector.getInstance(BusService.class);
-
- killbilleventHandler = new KillbillEventHandler();
+ killbilleventHandler = theInjector.getInstance(KillbillEventHandler.class);
//
// Fire all Startup levels up to service start
@@ -116,21 +113,4 @@ public class KillbillGuiceListener extends SetupServer
// Complete shutdown sequence
killbillLifecycle.fireShutdownSequencePostEventUnRegistration();
}
-
-
- //
- // At this point we have one generic handler in IRS that could dispatch notifications to the various pieces
- // interested but we could the various pieces register their own handler directly
- //
- public static class KillbillEventHandler
- {
- /*
- * IRS event handler for killbill entitlement events
- */
- @Subscribe
- public void handleEntitlementevents(BusEvent event)
- {
- logger.info("Killbill entitlement event {}", event.toString());
- }
- }
}
diff --git a/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java b/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java
index f1fd9a8..a33ac8a 100644
--- a/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java
+++ b/server/src/main/java/com/ning/billing/server/modules/KillbillServerModule.java
@@ -28,6 +28,7 @@ import com.ning.billing.jaxrs.resources.BundleTimelineResource;
import com.ning.billing.jaxrs.resources.InvoiceResource;
import com.ning.billing.jaxrs.resources.PaymentResource;
import com.ning.billing.jaxrs.resources.SubscriptionResource;
+import com.ning.billing.jaxrs.util.KillbillEventHandler;
import com.ning.billing.payment.setup.PaymentModule;
import com.ning.billing.util.glue.BusModule;
import com.ning.billing.util.glue.CallContextModule;
@@ -62,6 +63,7 @@ public class KillbillServerModule extends AbstractModule
bind(BundleTimelineResource.class).asEagerSingleton();
bind(InvoiceResource.class).asEagerSingleton();
bind(PaymentResource.class).asEagerSingleton();
+ bind(KillbillEventHandler.class).asEagerSingleton();
}
protected void installClock() {
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java b/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
index ec2b3ea..96e1ed6 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
@@ -37,25 +37,41 @@ import org.skife.jdbi.v2.IDBI;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.Assert;
-import org.testng.annotations.AfterClass;
+import org.testng.annotations.AfterSuite;
+import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.BeforeSuite;
import com.google.inject.Injector;
import com.google.inject.Module;
+import com.google.inject.util.Modules;
+import com.ning.billing.account.glue.AccountModule;
+import com.ning.billing.analytics.setup.AnalyticsModule;
+import com.ning.billing.beatrix.glue.BeatrixModule;
import com.ning.billing.beatrix.integration.TestBusHandler;
import com.ning.billing.catalog.api.PriceListSet;
+import com.ning.billing.catalog.glue.CatalogModule;
+import com.ning.billing.config.PaymentConfig;
import com.ning.billing.dbi.DBIProvider;
import com.ning.billing.dbi.DbiConfig;
import com.ning.billing.dbi.MysqlTestingHelper;
+import com.ning.billing.entitlement.glue.EntitlementModule;
+import com.ning.billing.invoice.glue.InvoiceModule;
import com.ning.billing.jaxrs.json.AccountJson;
import com.ning.billing.jaxrs.json.BundleJson;
import com.ning.billing.jaxrs.json.SubscriptionJson;
import com.ning.billing.jaxrs.resources.BaseJaxrsResource;
+import com.ning.billing.payment.provider.MockPaymentProviderPluginModule;
+import com.ning.billing.payment.setup.PaymentModule;
import com.ning.billing.server.listeners.KillbillGuiceListener;
import com.ning.billing.server.modules.KillbillServerModule;
import com.ning.billing.util.clock.Clock;
import com.ning.billing.util.clock.ClockMock;
+import com.ning.billing.util.glue.BusModule;
+import com.ning.billing.util.glue.CallContextModule;
+import com.ning.billing.util.glue.FieldStoreModule;
+import com.ning.billing.util.glue.NotificationQueueModule;
+import com.ning.billing.util.glue.TagStoreModule;
import com.ning.http.client.AsyncCompletionHandler;
import com.ning.http.client.AsyncHttpClient;
import com.ning.http.client.AsyncHttpClient.BoundRequestBuilder;
@@ -65,314 +81,327 @@ import com.ning.jetty.core.CoreConfig;
import com.ning.jetty.core.server.HttpServer;
public class TestJaxrsBase {
-
- protected static final int DEFAULT_HTTP_TIMEOUT_SEC = 5;
-
- protected static final Map<String, String> DEFAULT_EMPTY_QUERY = new HashMap<String, String>();
-
- private static final Logger log = LoggerFactory.getLogger(TestJaxrsBase.class);
-
- public static final String HEADER_CONTENT_TYPE = "Content-type";
- public static final String CONTENT_TYPE = "application/json";
-
- private MysqlTestingHelper helper;
- private HttpServer server;
-
- protected CoreConfig config;
- protected AsyncHttpClient httpClient;
- protected ObjectMapper mapper;
- protected ClockMock clock;
- protected TestBusHandler busHandler;
-
- public static void loadSystemPropertiesFromClasspath(final String resource) {
- final URL url = TestJaxrsBase.class.getResource(resource);
- assertNotNull(url);
- try {
- System.getProperties().load( url.openStream() );
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
-
- public static class TestKillbillGuiceListener extends KillbillGuiceListener {
- public TestKillbillGuiceListener() {
- super();
- }
- @Override
- protected Module getModule() {
- return new TestKillbillServerModule();
- }
- public Injector getTheInjector() {
- return theInjector;
- }
- }
-
- public static class TestKillbillServerModule extends KillbillServerModule {
-
- @Override
- protected void installClock() {
- bind(Clock.class).to(ClockMock.class).asEagerSingleton();
- }
-
- @Override
- protected void configureDao() {
- final MysqlTestingHelper helper = new MysqlTestingHelper();
- bind(MysqlTestingHelper.class).toInstance(helper);
- if (helper.isUsingLocalInstance()) {
- bind(IDBI.class).toProvider(DBIProvider.class).asEagerSingleton();
- final DbiConfig config = new ConfigurationObjectFactory(System.getProperties()).build(DbiConfig.class);
- bind(DbiConfig.class).toInstance(config);
- } else {
- final IDBI dbi = helper.getDBI();
- bind(IDBI.class).toInstance(dbi);
- }
- }
- }
-
- @BeforeMethod(groups="slow")
- public void cleanupTables() {
- helper.cleanupAllTables();
- busHandler.reset();
- }
-
- @BeforeSuite(groups="slow")
- public void setup() throws Exception {
-
- loadSystemPropertiesFromClasspath("/killbill.properties");
-
- final EventListener eventListener = new TestKillbillGuiceListener();
- httpClient = new AsyncHttpClient();
- server = new HttpServer();
- config = getConfig();
- mapper = new ObjectMapper();
- final Iterable<EventListener> eventListeners = new Iterable<EventListener>() {
- @Override
- public Iterator<EventListener> iterator() {
- ArrayList<EventListener> array = new ArrayList<EventListener>();
- array.add(eventListener);
- return array.iterator();
- }
- };
- server.configure(config, eventListeners, new HashMap<FilterHolder, String>());
- server.start();
-
- Injector injector = ((TestKillbillGuiceListener) eventListener).getTheInjector();
- helper = injector.getInstance(MysqlTestingHelper.class);
- clock = (ClockMock) injector.getInstance(Clock.class);
-
- busHandler = new TestBusHandler();
- }
-
- @AfterClass(groups="slow")
- public void tearDown() {
- if (helper != null) {
- try {
- helper.startMysql();
- } catch (IOException ignore) {
- }
- }
- }
-
-
- protected AccountJson createAccount(String name, String key, String email) throws Exception {
- AccountJson input = getAccountJson(name, key, email);
- String baseJson = mapper.writeValueAsString(input);
- Response response = doPost(BaseJaxrsResource.ACCOUNTS_PATH, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
- Assert.assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
-
- String location = response.getHeader("Location");
- Assert.assertNotNull(location);
-
- // Retrieves by Id based on Location returned
- response = doGetWithUrl(location, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
- Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
-
- baseJson = response.getResponseBody();
- AccountJson objFromJson = mapper.readValue(baseJson, AccountJson.class);
- Assert.assertNotNull(objFromJson);
- return objFromJson;
- }
-
-
-
- protected BundleJson createBundle(String accountId, String key) throws Exception {
- BundleJson input = new BundleJson(null, accountId, key, null);
- String baseJson = mapper.writeValueAsString(input);
- Response response = doPost(BaseJaxrsResource.BUNDLES_PATH, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
- Assert.assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
-
- String location = response.getHeader("Location");
- Assert.assertNotNull(location);
-
- // Retrieves by Id based on Location returned
- response = doGetWithUrl(location, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
- Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
-
- baseJson = response.getResponseBody();
- BundleJson objFromJson = mapper.readValue(baseJson, BundleJson.class);
- Assert.assertTrue(objFromJson.equalsNoId(input));
- return objFromJson;
- }
-
- protected SubscriptionJson createSubscription(final String bundleId, final String productName, final String productCategory, final String billingPeriod) throws Exception {
-
- SubscriptionJson input = new SubscriptionJson(null, bundleId, productName, productCategory, billingPeriod, PriceListSet.DEFAULT_PRICELIST_NAME, null, null, null);
- String baseJson = mapper.writeValueAsString(input);
- Response response = doPost(BaseJaxrsResource.SUBSCRIPTIONS_PATH, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
- Assert.assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
-
- String location = response.getHeader("Location");
- Assert.assertNotNull(location);
-
- // Retrieves by Id based on Location returned
- response = doGetWithUrl(location, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
- Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
-
- baseJson = response.getResponseBody();
- SubscriptionJson objFromJson = mapper.readValue(baseJson, SubscriptionJson.class);
- Assert.assertTrue(objFromJson.equalsNoId(input));
- return objFromJson;
- }
-
-
-
- //
- // HTTP CLIENT HELPERS
- //
- protected Response doPost(final String uri, final String body, final Map<String, String> queryParams, final int timeoutSec) {
- BoundRequestBuilder builder = getBuilderWithHeaderAndQuery("POST", getUrlFromUri(uri), queryParams);
- if (body != null) {
- builder.setBody(body);
- }
- return executeAndWait(builder, timeoutSec);
- }
-
- protected Response doPut(final String uri, final String body, final Map<String, String> queryParams, final int timeoutSec) {
- final String url = String.format("http://%s:%d%s", config.getServerHost(), config.getServerPort(), uri);
- BoundRequestBuilder builder = getBuilderWithHeaderAndQuery("PUT", url, queryParams);
- if (body != null) {
- builder.setBody(body);
- }
- return executeAndWait(builder, timeoutSec);
- }
-
- protected Response doDelete(final String uri, final Map<String, String> queryParams, final int timeoutSec) {
- final String url = String.format("http://%s:%d%s", config.getServerHost(), config.getServerPort(), uri);
- BoundRequestBuilder builder = getBuilderWithHeaderAndQuery("DELETE", url, queryParams);
- return executeAndWait(builder, timeoutSec);
- }
-
- protected Response doGet(final String uri, final Map<String, String> queryParams, final int timeoutSec) {
- final String url = String.format("http://%s:%d%s", config.getServerHost(), config.getServerPort(), uri);
- return doGetWithUrl(url, queryParams, timeoutSec);
- }
-
- protected Response doGetWithUrl(final String url, final Map<String, String> queryParams, final int timeoutSec) {
- BoundRequestBuilder builder = getBuilderWithHeaderAndQuery("GET", url, queryParams);
- return executeAndWait(builder, timeoutSec);
- }
-
- private Response executeAndWait(final BoundRequestBuilder builder, final int timeoutSec) {
- Response response = null;
- try {
- ListenableFuture<Response> futureStatus =
- builder.execute(new AsyncCompletionHandler<Response>() {
- @Override
- public Response onCompleted(Response response) throws Exception {
- return response;
- }
- });
- response = futureStatus.get(timeoutSec, TimeUnit.SECONDS);
- } catch (Exception e) {
- Assert.fail(e.getMessage());
- }
- Assert.assertNotNull(response);
- return response;
- }
-
- private String getUrlFromUri(final String uri) {
- return String.format("http://%s:%d%s", config.getServerHost(), config.getServerPort(), uri);
- }
-
- private BoundRequestBuilder getBuilderWithHeaderAndQuery(final String verb, final String url, final Map<String, String> queryParams) {
- BoundRequestBuilder builder = null;
- if (verb.equals("GET")) {
- builder = httpClient.prepareGet(url);
- } else if (verb.equals("POST")) {
- builder = httpClient.preparePost(url);
- } else if (verb.equals("PUT")) {
- builder = httpClient.preparePut(url);
- } else if (verb.equals("DELETE")) {
- builder = httpClient.prepareDelete(url);
- }
- builder.addHeader(HEADER_CONTENT_TYPE, CONTENT_TYPE);
- for (Entry<String, String> q : queryParams.entrySet()) {
- builder.addQueryParameter(q.getKey(), q.getValue());
- }
- return builder;
- }
-
- public AccountJson getAccountJson(final String name, final String externalKey, final String email) {
- String accountId = UUID.randomUUID().toString();
- int length = 4;
- int billCycleDay = 12;
- String currency = "USD";
- String paymentProvider = "paypal";
- String timeZone = "UTC";
- String address1 = "12 rue des ecoles";
- String address2 = "Poitier";
- String company = "Renault";
- String state = "Poitou";
- String country = "France";
- String phone = "81 53 26 56";
-
- AccountJson accountJson = new AccountJson(accountId, name, length, externalKey, email, billCycleDay, currency, paymentProvider, timeZone, address1, address2, company, state, country, phone);
- return accountJson;
- }
-
-
- private CoreConfig getConfig() {
- return new CoreConfig() {
- @Override
- public boolean isSSLEnabled() {
- return false;
- }
- @Override
- public boolean isJettyStatsOn() {
- return false;
- }
- @Override
- public int getServerSslPort() {
- return 0;
- }
- @Override
- public int getServerPort() {
- return 8080;
- }
- @Override
- public String getServerHost() {
- return "127.0.0.1";
- }
- @Override
- public String getSSLkeystorePassword() {
- return null;
- }
- @Override
- public String getSSLkeystoreLocation() {
- return null;
- }
- @Override
- public int getMinThreads() {
- return 2;
- }
- @Override
- public int getMaxThreads() {
- return 100;
- }
- @Override
- public String getLogPath() {
- return "/var/tmp/.logs";
- }
- };
- }
+
+ private final static String PLUGIN_NAME = "noop";
+
+ protected static final int DEFAULT_HTTP_TIMEOUT_SEC = 5;
+
+ protected static final Map<String, String> DEFAULT_EMPTY_QUERY = new HashMap<String, String>();
+
+ private static final Logger log = LoggerFactory.getLogger(TestJaxrsBase.class);
+
+ public static final String HEADER_CONTENT_TYPE = "Content-type";
+ public static final String CONTENT_TYPE = "application/json";
+
+ private MysqlTestingHelper helper;
+ private HttpServer server;
+
+
+ // YAck...
+ private static Injector injector;
+
+ protected CoreConfig config;
+ protected AsyncHttpClient httpClient;
+ protected ObjectMapper mapper;
+ protected ClockMock clock;
+ protected TestBusHandler busHandler;
+
+ public static void loadSystemPropertiesFromClasspath(final String resource) {
+ final URL url = TestJaxrsBase.class.getResource(resource);
+ assertNotNull(url);
+ try {
+ System.getProperties().load(url.openStream());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static class TestKillbillGuiceListener extends KillbillGuiceListener {
+ public TestKillbillGuiceListener() {
+ super();
+ }
+ @Override
+ protected Module getModule() {
+ return new TestKillbillServerModule();
+ }
+ public Injector getTheInjector() {
+ return theInjector;
+ }
+ }
+
+ public static class TestKillbillServerModule extends KillbillServerModule {
+
+ @Override
+ protected void installClock() {
+ bind(Clock.class).to(ClockMock.class).asEagerSingleton();
+ }
+
+
+ private static final class PaymentMockModule extends PaymentModule {
+ @Override
+ protected void installPaymentProviderPlugins(PaymentConfig config) {
+ install(new MockPaymentProviderPluginModule(PLUGIN_NAME));
+ }
+ }
+
+ protected void installKillbillModules(){
+
+ /*
+ * For a lack of getting module override working, copy all install modules from parent class...
+ *
+ super.installKillbillModules();
+ Modules.override(new com.ning.billing.payment.setup.PaymentModule()).with(new PaymentMockModule());
+ */
+ install(new FieldStoreModule());
+ install(new TagStoreModule());
+ install(new CatalogModule());
+ install(new BusModule());
+ install(new NotificationQueueModule());
+ install(new CallContextModule());
+ install(new AccountModule());
+ install(new InvoiceModule());
+ install(new EntitlementModule());
+ install(new AnalyticsModule());
+ install(new PaymentMockModule());
+ install(new BeatrixModule());
+ installClock();
+ }
+
+ @Override
+ protected void configureDao() {
+ final MysqlTestingHelper helper = new MysqlTestingHelper();
+ bind(MysqlTestingHelper.class).toInstance(helper);
+ if (helper.isUsingLocalInstance()) {
+ bind(IDBI.class).toProvider(DBIProvider.class).asEagerSingleton();
+ final DbiConfig config = new ConfigurationObjectFactory(System.getProperties()).build(DbiConfig.class);
+ bind(DbiConfig.class).toInstance(config);
+ } else {
+ final IDBI dbi = helper.getDBI();
+ bind(IDBI.class).toInstance(dbi);
+ }
+ }
+ }
+
+ @BeforeMethod(groups="slow")
+ public void cleanupTables() {
+ helper.cleanupAllTables();
+ busHandler.reset();
+ }
+
+ @BeforeClass(groups="slow")
+ public void setupClass() {
+
+ loadConfig();
+ httpClient = new AsyncHttpClient();
+ mapper = new ObjectMapper();
+ helper = injector.getInstance(MysqlTestingHelper.class);
+ clock = (ClockMock) injector.getInstance(Clock.class);
+ busHandler = new TestBusHandler();
+ }
+
+
+ private void loadConfig() {
+ if (config == null) {
+ config = new ConfigurationObjectFactory(System.getProperties()).build(CoreConfig.class);
+ }
+ }
+
+ @BeforeSuite(groups="slow")
+ public void setup() throws Exception {
+
+ loadSystemPropertiesFromClasspath("/killbill.properties");
+
+ final EventListener eventListener = new TestKillbillGuiceListener();
+ server = new HttpServer();
+ loadConfig();
+ final Iterable<EventListener> eventListeners = new Iterable<EventListener>() {
+ @Override
+ public Iterator<EventListener> iterator() {
+ ArrayList<EventListener> array = new ArrayList<EventListener>();
+ array.add(eventListener);
+ return array.iterator();
+ }
+ };
+ server.configure(config, eventListeners, new HashMap<FilterHolder, String>());
+ server.start();
+ injector = ((TestKillbillGuiceListener) eventListener).getTheInjector();
+ }
+
+ @AfterSuite(groups="slow")
+ public void tearDown() {
+ if (helper != null) {
+ helper.stopMysql();
+ }
+ try {
+ server.stop();
+ } catch (Exception e) {
+
+ }
+ }
+
+
+ protected AccountJson createAccount(String name, String key, String email) throws Exception {
+ AccountJson input = getAccountJson(name, key, email);
+ String baseJson = mapper.writeValueAsString(input);
+ Response response = doPost(BaseJaxrsResource.ACCOUNTS_PATH, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+ Assert.assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
+
+ String location = response.getHeader("Location");
+ Assert.assertNotNull(location);
+
+ // Retrieves by Id based on Location returned
+ response = doGetWithUrl(location, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+ Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
+
+ baseJson = response.getResponseBody();
+ AccountJson objFromJson = mapper.readValue(baseJson, AccountJson.class);
+ Assert.assertNotNull(objFromJson);
+ return objFromJson;
+ }
+
+
+
+ protected BundleJson createBundle(String accountId, String key) throws Exception {
+ BundleJson input = new BundleJson(null, accountId, key, null);
+ String baseJson = mapper.writeValueAsString(input);
+ Response response = doPost(BaseJaxrsResource.BUNDLES_PATH, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+ Assert.assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
+
+ String location = response.getHeader("Location");
+ Assert.assertNotNull(location);
+
+ // Retrieves by Id based on Location returned
+ response = doGetWithUrl(location, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+ Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
+
+ baseJson = response.getResponseBody();
+ BundleJson objFromJson = mapper.readValue(baseJson, BundleJson.class);
+ Assert.assertTrue(objFromJson.equalsNoId(input));
+ return objFromJson;
+ }
+
+ protected SubscriptionJson createSubscription(final String bundleId, final String productName, final String productCategory, final String billingPeriod, final boolean waitCompletion) throws Exception {
+
+ SubscriptionJson input = new SubscriptionJson(null, bundleId, productName, productCategory, billingPeriod, PriceListSet.DEFAULT_PRICELIST_NAME, null, null, null);
+ String baseJson = mapper.writeValueAsString(input);
+
+
+ Map<String, String> queryParams = waitCompletion ? getQueryParamsForCallCompletion("5") : DEFAULT_EMPTY_QUERY;
+ Response response = doPost(BaseJaxrsResource.SUBSCRIPTIONS_PATH, baseJson, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
+ Assert.assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
+
+ String location = response.getHeader("Location");
+ Assert.assertNotNull(location);
+
+ // Retrieves by Id based on Location returned
+
+ response = doGetWithUrl(location, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+ Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
+
+ baseJson = response.getResponseBody();
+ SubscriptionJson objFromJson = mapper.readValue(baseJson, SubscriptionJson.class);
+ Assert.assertTrue(objFromJson.equalsNoId(input));
+ return objFromJson;
+ }
+
+ protected Map<String, String> getQueryParamsForCallCompletion(final String timeoutSec) {
+ Map<String, String> queryParams = new HashMap<String, String>();
+ queryParams.put(BaseJaxrsResource.QUERY_CALL_COMPLETION, "true");
+ queryParams.put(BaseJaxrsResource.QUERY_CALL_TIMEOUT, timeoutSec);
+ return queryParams;
+ }
+
+ //
+ // HTTP CLIENT HELPERS
+ //
+ protected Response doPost(final String uri, final String body, final Map<String, String> queryParams, final int timeoutSec) {
+ BoundRequestBuilder builder = getBuilderWithHeaderAndQuery("POST", getUrlFromUri(uri), queryParams);
+ if (body != null) {
+ builder.setBody(body);
+ }
+ return executeAndWait(builder, timeoutSec);
+ }
+
+ protected Response doPut(final String uri, final String body, final Map<String, String> queryParams, final int timeoutSec) {
+ final String url = String.format("http://%s:%d%s", config.getServerHost(), config.getServerPort(), uri);
+ BoundRequestBuilder builder = getBuilderWithHeaderAndQuery("PUT", url, queryParams);
+ if (body != null) {
+ builder.setBody(body);
+ }
+ return executeAndWait(builder, timeoutSec);
+ }
+
+ protected Response doDelete(final String uri, final Map<String, String> queryParams, final int timeoutSec) {
+ final String url = String.format("http://%s:%d%s", config.getServerHost(), config.getServerPort(), uri);
+ BoundRequestBuilder builder = getBuilderWithHeaderAndQuery("DELETE", url, queryParams);
+ return executeAndWait(builder, timeoutSec);
+ }
+
+ protected Response doGet(final String uri, final Map<String, String> queryParams, final int timeoutSec) {
+ final String url = String.format("http://%s:%d%s", config.getServerHost(), config.getServerPort(), uri);
+ return doGetWithUrl(url, queryParams, timeoutSec);
+ }
+
+ protected Response doGetWithUrl(final String url, final Map<String, String> queryParams, final int timeoutSec) {
+ BoundRequestBuilder builder = getBuilderWithHeaderAndQuery("GET", url, queryParams);
+ return executeAndWait(builder, timeoutSec);
+ }
+
+ private Response executeAndWait(final BoundRequestBuilder builder, final int timeoutSec) {
+ Response response = null;
+ try {
+ ListenableFuture<Response> futureStatus =
+ builder.execute(new AsyncCompletionHandler<Response>() {
+ @Override
+ public Response onCompleted(Response response) throws Exception {
+ return response;
+ }
+ });
+ response = futureStatus.get(timeoutSec, TimeUnit.SECONDS);
+ } catch (Exception e) {
+ Assert.fail(e.getMessage());
+ }
+ Assert.assertNotNull(response);
+ return response;
+ }
+
+ private String getUrlFromUri(final String uri) {
+ return String.format("http://%s:%d%s", config.getServerHost(), config.getServerPort(), uri);
+ }
+
+ private BoundRequestBuilder getBuilderWithHeaderAndQuery(final String verb, final String url, final Map<String, String> queryParams) {
+ BoundRequestBuilder builder = null;
+ if (verb.equals("GET")) {
+ builder = httpClient.prepareGet(url);
+ } else if (verb.equals("POST")) {
+ builder = httpClient.preparePost(url);
+ } else if (verb.equals("PUT")) {
+ builder = httpClient.preparePut(url);
+ } else if (verb.equals("DELETE")) {
+ builder = httpClient.prepareDelete(url);
+ }
+ builder.addHeader(HEADER_CONTENT_TYPE, CONTENT_TYPE);
+ for (Entry<String, String> q : queryParams.entrySet()) {
+ builder.addQueryParameter(q.getKey(), q.getValue());
+ }
+ return builder;
+ }
+
+ public AccountJson getAccountJson(final String name, final String externalKey, final String email) {
+ String accountId = UUID.randomUUID().toString();
+ int length = 4;
+ int billCycleDay = 12;
+ String currency = "USD";
+ String paymentProvider = "noop";
+ String timeZone = "UTC";
+ String address1 = "12 rue des ecoles";
+ String address2 = "Poitier";
+ String company = "Renault";
+ String state = "Poitou";
+ String country = "France";
+ String phone = "81 53 26 56";
+
+ AccountJson accountJson = new AccountJson(accountId, name, length, externalKey, email, billCycleDay, currency, paymentProvider, timeZone, address1, address2, company, state, country, phone);
+ return accountJson;
+ }
}
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestSubscription.java b/server/src/test/java/com/ning/billing/jaxrs/TestSubscription.java
index 8b2a055..3bd2524 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestSubscription.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestSubscription.java
@@ -15,7 +15,7 @@
*/
package com.ning.billing.jaxrs;
-import static org.testng.Assert.assertTrue;
+import java.util.Map;
import javax.ws.rs.core.Response.Status;
@@ -25,7 +25,6 @@ import org.slf4j.LoggerFactory;
import org.testng.Assert;
import org.testng.annotations.Test;
-import com.ning.billing.beatrix.integration.TestBusHandler.NextEvent;
import com.ning.billing.catalog.api.BillingPeriod;
import com.ning.billing.catalog.api.Duration;
import com.ning.billing.catalog.api.ProductCategory;
@@ -37,76 +36,80 @@ import com.ning.billing.jaxrs.resources.BaseJaxrsResource;
import com.ning.http.client.Response;
public class TestSubscription extends TestJaxrsBase {
-
- private static final Logger log = LoggerFactory.getLogger(TestSubscription.class);
- private static final long DELAY = 5000;
+ private static final Logger log = LoggerFactory.getLogger(TestSubscription.class);
- @Test(groups="slow", enabled=true)
- public void testSubscriptionOk() throws Exception {
+ private static final long DELAY = 5000;
+ private static final String CALL_COMPLETION_TIMEOUT_SEC = "5";
+
+
+ @Test(groups="slow", enabled=true)
+ public void testSubscriptionInTrialOk() throws Exception {
+
+ AccountJson accountJson = createAccount("xil", "shdxilhkkl", "xil@yahoo.com");
+ BundleJson bundleJson = createBundle(accountJson.getAcountId(), "99999");
- AccountJson accountJson = createAccount("xil", "shdxilhkkl", "xil@yahoo.com");
- BundleJson bundleJson = createBundle(accountJson.getAcountId(), "99999");
-
String productName = "Shotgun";
BillingPeriod term = BillingPeriod.MONTHLY;
- //busHandler.pushExpectedEvent(NextEvent.CREATE);
- //busHandler.pushExpectedEvent(NextEvent.INVOICE);
- SubscriptionJson subscriptionJson = createSubscription(bundleJson.getBundleId(), productName, ProductCategory.BASE.toString(), term.toString());
- //assertTrue(busHandler.isCompleted(DELAY));
-
- // Change plan IMM
- String newProductName = "Assault-Rifle";
-
- SubscriptionJson newInput = new SubscriptionJson(subscriptionJson.getSubscriptionId(),
- subscriptionJson.getBundleId(),
- newProductName,
- subscriptionJson.getProductCategory(),
- subscriptionJson.getBillingPeriod(),
- subscriptionJson.getPriceList(), null, null, null);
- String baseJson = mapper.writeValueAsString(newInput);
-
- String uri = BaseJaxrsResource.SUBSCRIPTIONS_PATH + "/" + subscriptionJson.getSubscriptionId().toString();
- Response response = doPut(uri, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
- Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
- baseJson = response.getResponseBody();
- SubscriptionJson objFromJson = mapper.readValue(baseJson, SubscriptionJson.class);
- Assert.assertTrue(objFromJson.equals(newInput));
-
- // MOVE after TRIAL
- //busHandler.pushExpectedEvent(NextEvent.PHASE);
- //busHandler.pushExpectedEvent(NextEvent.INVOICE);
- //busHandler.pushExpectedEvent(NextEvent.PAYMENT);
- clock.setDeltaFromReality(new Duration() {
- @Override
- public TimeUnit getUnit() {
- return TimeUnit.MONTHS;
- }
- @Override
- public int getNumber() {
- return 1;
- }
- @Override
- public DateTime addToDateTime(DateTime dateTime) {
- return null;
- }
- }, 1000);
- //assertTrue(busHandler.isCompleted(DELAY));
-
-
- Thread.sleep(5000);
-
- // Cancel EOT
- uri = BaseJaxrsResource.SUBSCRIPTIONS_PATH + "/" + subscriptionJson.getSubscriptionId().toString();
- response = doDelete(uri, DEFAULT_EMPTY_QUERY, 10000 /* DEFAULT_HTTP_TIMEOUT_SEC */);
- Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
-
-
- // Uncancel
- uri = BaseJaxrsResource.SUBSCRIPTIONS_PATH + "/" + subscriptionJson.getSubscriptionId().toString() + "/uncancel";
- response = doPut(uri, baseJson, DEFAULT_EMPTY_QUERY, 10000 /* DEFAULT_HTTP_TIMEOUT_SEC */);
- Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
- }
+ SubscriptionJson subscriptionJson = createSubscription(bundleJson.getBundleId(), productName, ProductCategory.BASE.toString(), term.toString(), true);
+
+ // Change plan IMM
+ String newProductName = "Assault-Rifle";
+
+ SubscriptionJson newInput = new SubscriptionJson(subscriptionJson.getSubscriptionId(),
+ subscriptionJson.getBundleId(),
+ newProductName,
+ subscriptionJson.getProductCategory(),
+ subscriptionJson.getBillingPeriod(),
+ subscriptionJson.getPriceList(), null, null, null);
+ String baseJson = mapper.writeValueAsString(newInput);
+
+ String uri = BaseJaxrsResource.SUBSCRIPTIONS_PATH + "/" + subscriptionJson.getSubscriptionId().toString();
+ Map<String, String> queryParams = getQueryParamsForCallCompletion(CALL_COMPLETION_TIMEOUT_SEC);
+ Response response = doPut(uri, baseJson, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
+ Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
+ baseJson = response.getResponseBody();
+ SubscriptionJson objFromJson = mapper.readValue(baseJson, SubscriptionJson.class);
+ Assert.assertTrue(objFromJson.equals(newInput));
+
+ clock.setDeltaFromReality(new Duration() {
+ @Override
+ public TimeUnit getUnit() {
+ return TimeUnit.MONTHS;
+ }
+ @Override
+ public int getNumber() {
+ return 1;
+ }
+ @Override
+ public DateTime addToDateTime(DateTime dateTime) {
+ return null;
+ }
+ }, 1000);
+
+ crappyWaitForLackOfProperSynchonization();
+
+ //
+ // Cancel EOT
+ uri = BaseJaxrsResource.SUBSCRIPTIONS_PATH + "/" + subscriptionJson.getSubscriptionId().toString();
+ response = doDelete(uri, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
+ Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
+
+ // Uncancel
+ uri = BaseJaxrsResource.SUBSCRIPTIONS_PATH + "/" + subscriptionJson.getSubscriptionId().toString() + "/uncancel";
+ response = doPut(uri, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+ Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
+ }
+
+
+ /**
+ *
+ * We could implement a ClockResource in jaxrs with the ability to sync on user token
+ * but until we have a strong need for it, this is in the TODO list...
+ */
+ private void crappyWaitForLackOfProperSynchonization() throws Exception {
+ Thread.sleep(DELAY);
+ }
}
diff --git a/server/src/test/resources/killbill.properties b/server/src/test/resources/killbill.properties
index c3fe203..2ff0814 100644
--- a/server/src/test/resources/killbill.properties
+++ b/server/src/test/resources/killbill.properties
@@ -3,7 +3,6 @@ com.ning.billing.dbi.jdbc.url=jdbc:mysql://127.0.0.1:3306/test_killbill
killbill.catalog.uri=file:src/test/resources/catalog-weapons.xml
-killbill.payment.provider.default=zuora-shadow
killbill.payment.engine.events.off=false
killbill.payment.retry.days=8,8,8
@@ -11,5 +10,7 @@ user.timezone=UTC
com.ning.billing.dbi.test.useLocalDb=true
+com.ning.core.server.jetty.logPath=/var/tmp/.logs
+
server/src/test/resources/log4j.xml 3(+3 -0)
diff --git a/server/src/test/resources/log4j.xml b/server/src/test/resources/log4j.xml
index 1a946d3..0c84cc0 100644
--- a/server/src/test/resources/log4j.xml
+++ b/server/src/test/resources/log4j.xml
@@ -25,6 +25,9 @@
</appender>
+ <logger name="com.ning.billing.jaxrs">
+ <level value="debug"/>
+ </logger>
<logger name="com.ning.billing.server">
<level value="info"/>
</logger>
diff --git a/util/src/main/java/com/ning/billing/util/callcontext/CallContextBase.java b/util/src/main/java/com/ning/billing/util/callcontext/CallContextBase.java
index 8164f76..35dc92f 100644
--- a/util/src/main/java/com/ning/billing/util/callcontext/CallContextBase.java
+++ b/util/src/main/java/com/ning/billing/util/callcontext/CallContextBase.java
@@ -16,15 +16,25 @@
package com.ning.billing.util.callcontext;
+import java.util.UUID;
+
public abstract class CallContextBase implements CallContext {
+
+ private final UUID userToken;
private final String userName;
private final CallOrigin callOrigin;
private final UserType userType;
+
public CallContextBase(String userName, CallOrigin callOrigin, UserType userType) {
+ this(userName, callOrigin, userType, null);
+ }
+
+ public CallContextBase(String userName, CallOrigin callOrigin, UserType userType, UUID userToken) {
this.userName = userName;
this.callOrigin = callOrigin;
this.userType = userType;
+ this.userToken = userToken;
}
@Override
@@ -41,4 +51,9 @@ public abstract class CallContextBase implements CallContext {
public UserType getUserType() {
return userType;
}
+
+ @Override
+ public UUID getUserToken() {
+ return userToken;
+ }
}
diff --git a/util/src/main/java/com/ning/billing/util/callcontext/CallContextBinder.java b/util/src/main/java/com/ning/billing/util/callcontext/CallContextBinder.java
index 3ac8a98..41dd68a 100644
--- a/util/src/main/java/com/ning/billing/util/callcontext/CallContextBinder.java
+++ b/util/src/main/java/com/ning/billing/util/callcontext/CallContextBinder.java
@@ -37,9 +37,10 @@ public @interface CallContextBinder {
return new Binder<CallContextBinder, CallContext>() {
@Override
public void bind(SQLStatement q, CallContextBinder bind, CallContext callContext) {
- q.bind("userName", callContext.getUserName());
+ q.bind("userName", callContext.getUserName());
q.bind("createdDate", callContext.getCreatedDate().toDate());
q.bind("updatedDate", callContext.getUpdatedDate().toDate());
+ q.bind("userToken", (callContext.getUserToken() != null) ? callContext.getUserToken().toString() : null);
}
};
}
diff --git a/util/src/main/java/com/ning/billing/util/callcontext/CallContextFactory.java b/util/src/main/java/com/ning/billing/util/callcontext/CallContextFactory.java
index 6ffc554..d9f18c9 100644
--- a/util/src/main/java/com/ning/billing/util/callcontext/CallContextFactory.java
+++ b/util/src/main/java/com/ning/billing/util/callcontext/CallContextFactory.java
@@ -16,10 +16,14 @@
package com.ning.billing.util.callcontext;
+import java.util.UUID;
+
import org.joda.time.DateTime;
public interface CallContextFactory {
- CallContext createCallContext(String userName, CallOrigin callOrigin, UserType userType);
+ CallContext createCallContext(String userName, CallOrigin callOrigin, UserType userType, UUID userToken);
+
+ CallContext createCallContext(String userName, CallOrigin callOrigin, UserType userType);
CallContext createMigrationCallContext(String userName, CallOrigin callOrigin, UserType userType, DateTime createdDate, DateTime updatedDate);
diff --git a/util/src/main/java/com/ning/billing/util/callcontext/DefaultCallContext.java b/util/src/main/java/com/ning/billing/util/callcontext/DefaultCallContext.java
index 57dc682..4cde143 100644
--- a/util/src/main/java/com/ning/billing/util/callcontext/DefaultCallContext.java
+++ b/util/src/main/java/com/ning/billing/util/callcontext/DefaultCallContext.java
@@ -16,17 +16,24 @@
package com.ning.billing.util.callcontext;
+import java.util.UUID;
+
import com.ning.billing.util.clock.Clock;
import org.joda.time.DateTime;
public class DefaultCallContext extends CallContextBase {
+
private final Clock clock;
- public DefaultCallContext(String userName, CallOrigin callOrigin, UserType userType, Clock clock) {
- super(userName, callOrigin, userType);
+ public DefaultCallContext(final String userName, final CallOrigin callOrigin, final UserType userType, final UUID userToken, final Clock clock) {
+ super(userName, callOrigin, userType, userToken);
this.clock = clock;
}
+ public DefaultCallContext(String userName, CallOrigin callOrigin, UserType userType, Clock clock) {
+ this(userName, callOrigin, userType, null, clock);
+ }
+
@Override
public DateTime getCreatedDate() {
return clock.getUTCNow();
diff --git a/util/src/main/java/com/ning/billing/util/callcontext/DefaultCallContextFactory.java b/util/src/main/java/com/ning/billing/util/callcontext/DefaultCallContextFactory.java
index f75574c..52ac313 100644
--- a/util/src/main/java/com/ning/billing/util/callcontext/DefaultCallContextFactory.java
+++ b/util/src/main/java/com/ning/billing/util/callcontext/DefaultCallContextFactory.java
@@ -16,6 +16,8 @@
package com.ning.billing.util.callcontext;
+import java.util.UUID;
+
import com.google.inject.Inject;
import com.ning.billing.util.clock.Clock;
import org.joda.time.DateTime;
@@ -29,8 +31,13 @@ public class DefaultCallContextFactory implements CallContextFactory {
}
@Override
+ public CallContext createCallContext(String userName, CallOrigin callOrigin, UserType userType, UUID userToken) {
+ return new DefaultCallContext(userName, callOrigin, userType, userToken, clock);
+ }
+
+ @Override
public CallContext createCallContext(String userName, CallOrigin callOrigin, UserType userType) {
- return new DefaultCallContext(userName, callOrigin, userType, clock);
+ return createCallContext(userName, callOrigin, userType, null);
}
@Override
diff --git a/util/src/main/java/com/ning/billing/util/userrequest/CompletionUserRequestBase.java b/util/src/main/java/com/ning/billing/util/userrequest/CompletionUserRequestBase.java
index 26c5184..d1e5efa 100644
--- a/util/src/main/java/com/ning/billing/util/userrequest/CompletionUserRequestBase.java
+++ b/util/src/main/java/com/ning/billing/util/userrequest/CompletionUserRequestBase.java
@@ -23,6 +23,7 @@ import java.util.concurrent.TimeoutException;
import com.ning.billing.account.api.AccountChangeNotification;
import com.ning.billing.account.api.AccountCreationNotification;
import com.ning.billing.entitlement.api.user.SubscriptionTransition;
+import com.ning.billing.invoice.api.EmptyInvoiceNotification;
import com.ning.billing.invoice.api.InvoiceCreationNotification;
import com.ning.billing.payment.api.PaymentError;
import com.ning.billing.payment.api.PaymentInfo;
@@ -104,6 +105,9 @@ public abstract class CompletionUserRequestBase implements CompletionUserRequest
case SUBSCRIPTION_TRANSITION:
onSubscriptionTransition((SubscriptionTransition) curEvent);
break;
+ case INVOICE_EMPTY:
+ onEmptyInvoice((EmptyInvoiceNotification) curEvent);
+ break;
case INVOICE_CREATION:
onInvoiceCreation((InvoiceCreationNotification) curEvent);
break;
@@ -118,21 +122,35 @@ public abstract class CompletionUserRequestBase implements CompletionUserRequest
}
}
+ /*
+ *
+ * Default no-op implementation so as to not have to implement all callbacks
+ */
@Override
- public abstract void onAccountCreation(final AccountCreationNotification curEvent);
+ public void onAccountCreation(final AccountCreationNotification curEvent) {
+ }
@Override
- public abstract void onAccountChange(final AccountChangeNotification curEvent);
+ public void onAccountChange(final AccountChangeNotification curEvent) {
+ }
@Override
- public abstract void onSubscriptionTransition(final SubscriptionTransition curEvent);
+ public void onSubscriptionTransition(final SubscriptionTransition curEvent) {
+ }
@Override
- public abstract void onInvoiceCreation(final InvoiceCreationNotification curEvent);
+ public void onEmptyInvoice(final EmptyInvoiceNotification curEvent) {
+ }
+
+ @Override
+ public void onInvoiceCreation(final InvoiceCreationNotification curEvent) {
+ }
@Override
- public abstract void onPaymentInfo(final PaymentInfo curEvent);
+ public void onPaymentInfo(final PaymentInfo curEvent) {
+ }
@Override
- public abstract void onPaymentError(final PaymentError curEvent);
+ public void onPaymentError(final PaymentError curEvent) {
+ }
}
diff --git a/util/src/main/resources/com/ning/billing/util/audit/dao/AuditSqlDao.sql.stg b/util/src/main/resources/com/ning/billing/util/audit/dao/AuditSqlDao.sql.stg
index 25cc9d4..f949ba8 100644
--- a/util/src/main/resources/com/ning/billing/util/audit/dao/AuditSqlDao.sql.stg
+++ b/util/src/main/resources/com/ning/billing/util/audit/dao/AuditSqlDao.sql.stg
@@ -7,10 +7,11 @@ fields(prefix) ::= <<
<prefix>change_date,
<prefix>changed_by,
<prefix>reason_code,
- <prefix>comments
+ <prefix>comments,
+ <prefix>user_token
>>
insertAuditFromTransaction() ::= <<
INSERT INTO audit_log(<fields()>)
- VALUES(:tableName, :recordId, :changeType, :createdDate, :userName, NULL, NULL);
+ VALUES(:tableName, :recordId, :changeType, :createdDate, :userName, NULL, NULL, :userToken);
>>
\ No newline at end of file
diff --git a/util/src/main/resources/com/ning/billing/util/ddl.sql b/util/src/main/resources/com/ning/billing/util/ddl.sql
index 77b66c9..486a031 100644
--- a/util/src/main/resources/com/ning/billing/util/ddl.sql
+++ b/util/src/main/resources/com/ning/billing/util/ddl.sql
@@ -116,5 +116,6 @@ CREATE TABLE audit_log (
changed_by varchar(50) NOT NULL,
reason_code varchar(20) DEFAULT NULL,
comments varchar(255) DEFAULT NULL,
+ user_token char(36),
PRIMARY KEY(id)
) ENGINE=innodb;
diff --git a/util/src/test/java/com/ning/billing/util/bus/TestEventBus.java b/util/src/test/java/com/ning/billing/util/bus/TestEventBus.java
index 2078d0e..a4b493f 100644
--- a/util/src/test/java/com/ning/billing/util/bus/TestEventBus.java
+++ b/util/src/test/java/com/ning/billing/util/bus/TestEventBus.java
@@ -16,6 +16,8 @@
package com.ning.billing.util.bus;
+import java.util.UUID;
+
import com.google.common.eventbus.Subscribe;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -55,6 +57,11 @@ public class TestEventBus {
public BusEventType getBusEventType() {
return null;
}
+
+ @Override
+ public UUID getUserToken() {
+ return null;
+ }
}
public static final class MyOtherEvent implements BusEvent {
@@ -70,6 +77,11 @@ public class TestEventBus {
public BusEventType getBusEventType() {
return null;
}
+
+ @Override
+ public UUID getUserToken() {
+ return null;
+ }
}
public static class MyEventHandler {
diff --git a/util/src/test/java/com/ning/billing/util/callcontext/TestCallContext.java b/util/src/test/java/com/ning/billing/util/callcontext/TestCallContext.java
index b632f2e..0185a69 100644
--- a/util/src/test/java/com/ning/billing/util/callcontext/TestCallContext.java
+++ b/util/src/test/java/com/ning/billing/util/callcontext/TestCallContext.java
@@ -16,25 +16,27 @@
package com.ning.billing.util.callcontext;
+import java.util.UUID;
+
import com.ning.billing.util.clock.DefaultClock;
import org.joda.time.DateTime;
public class TestCallContext implements CallContext {
+
private final String userName;
private final DateTime updatedDate;
private final DateTime createdDate;
-
+ private final UUID userToken;
+
public TestCallContext(String userName) {
- this.userName = userName;
- DateTime now = new DefaultClock().getUTCNow();
- this.updatedDate = now;
- this.createdDate = now;
+ this(userName, new DefaultClock().getUTCNow(), new DefaultClock().getUTCNow());
}
public TestCallContext(String userName, DateTime createdDate, DateTime updatedDate) {
this.userName = userName;
this.createdDate = createdDate;
this.updatedDate = updatedDate;
+ this.userToken = UUID.randomUUID();
}
@Override
@@ -61,4 +63,9 @@ public class TestCallContext implements CallContext {
public DateTime getUpdatedDate() {
return updatedDate;
}
+
+ @Override
+ public UUID getUserToken() {
+ return userToken;
+ }
}