killbill-memoizeit
Changes
account/pom.xml 2(+1 -1)
analytics/pom.xml 2(+1 -1)
api/pom.xml 6(+5 -1)
beatrix/pom.xml 2(+1 -1)
catalog/pom.xml 2(+1 -1)
entitlement/pom.xml 2(+1 -1)
entitlement/src/main/java/com/ning/billing/entitlement/api/billing/EntitlementBillingApi.java 7(+3 -4)
entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransition.java 14(+13 -1)
entitlement/src/main/java/com/ning/billing/entitlement/engine/core/ApiEventProcessorBase.java 4(+2 -2)
entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlan.java 23(+19 -4)
entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanMemory.java 2(+1 -1)
entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanSql.java 2(+1 -1)
entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/EntitlementDaoMemoryMock.java 6(+3 -3)
invoice/pom.xml 2(+1 -1)
payment/pom.xml 2(+1 -1)
pom.xml 3(+2 -1)
util/pom.xml 11(+5 -6)
util/src/test/resources/log4j.xml 2(+1 -1)
Details
account/pom.xml 2(+1 -1)
diff --git a/account/pom.xml b/account/pom.xml
index f92d35b..8d87ed2 100644
--- a/account/pom.xml
+++ b/account/pom.xml
@@ -13,7 +13,7 @@
<parent>
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
- <version>0.0.13-SNAPSHOT</version>
+ <version>0.0.14-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-account</artifactId>
analytics/pom.xml 2(+1 -1)
diff --git a/analytics/pom.xml b/analytics/pom.xml
index cb22965..3755178 100644
--- a/analytics/pom.xml
+++ b/analytics/pom.xml
@@ -13,7 +13,7 @@
<parent>
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
- <version>0.0.13-SNAPSHOT</version>
+ <version>0.0.14-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-analytics</artifactId>
diff --git a/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java b/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
index 63c7a06..fbbe2b8 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
@@ -16,22 +16,23 @@
package com.ning.billing.analytics;
+import com.google.common.eventbus.Subscribe;
import com.google.inject.Inject;
import com.ning.billing.account.api.IAccount;
import com.ning.billing.account.api.IAccountUserApi;
import com.ning.billing.analytics.dao.BusinessSubscriptionTransitionDao;
import com.ning.billing.catalog.api.Currency;
-import com.ning.billing.entitlement.api.user.IApiListener;
import com.ning.billing.entitlement.api.user.IEntitlementUserApi;
import com.ning.billing.entitlement.api.user.ISubscriptionBundle;
import com.ning.billing.entitlement.api.user.ISubscriptionTransition;
+
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
-public class AnalyticsListener implements IApiListener
+public class AnalyticsListener
{
private static final Logger log = LoggerFactory.getLogger(AnalyticsListener.class);
@@ -47,42 +48,67 @@ public class AnalyticsListener implements IApiListener
this.accountApi = accountApi;
}
- @Override
+ /*
+ * Disable until we fix IRS to allow for two instances (One for bilr proxy, or for killbill)
+ * @Subscribe
+ */
+ public void handleNotificationChange(ISubscriptionTransition event) {
+ switch (event.getTransitionType()) {
+ case CREATE:
+ subscriptionCreated(event);
+ break;
+ case CANCEL:
+ subscriptionCancelled(event);
+ break;
+ case CHANGE:
+ subscriptionChanged(event);
+ break;
+ case PAUSE:
+ subscriptionPaused(event);
+ break;
+ case RESUME:
+ subscriptionResumed(event);
+ break;
+ case UNCANCEL:
+ break;
+ case PHASE:
+ subscriptionPhaseChanged(event);
+ break;
+ default:
+ throw new RuntimeException("Unexpected event type " + event.getRequestedTransitionTime());
+ }
+ }
+
public void subscriptionCreated(final ISubscriptionTransition created)
{
final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionCreated(created.getNextPlan());
recordTransition(event, created);
}
- @Override
public void subscriptionCancelled(final ISubscriptionTransition cancelled)
{
final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionCancelled(cancelled.getNextPlan());
recordTransition(event, cancelled);
}
- @Override
public void subscriptionChanged(final ISubscriptionTransition changed)
{
final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionChanged(changed.getNextPlan());
recordTransition(event, changed);
}
- @Override
public void subscriptionPaused(final ISubscriptionTransition paused)
{
final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionPaused(paused.getNextPlan());
recordTransition(event, paused);
}
- @Override
public void subscriptionResumed(final ISubscriptionTransition resumed)
{
final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionResumed(resumed.getNextPlan());
recordTransition(event, resumed);
}
- @Override
public void subscriptionPhaseChanged(final ISubscriptionTransition phaseChanged)
{
final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionPhaseChanged(phaseChanged.getNextPlan(), phaseChanged.getNextState());
api/pom.xml 6(+5 -1)
diff --git a/api/pom.xml b/api/pom.xml
index ad67258..5385aad 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -13,7 +13,7 @@
<parent>
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
- <version>0.0.13-SNAPSHOT</version>
+ <version>0.0.14-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-api</artifactId>
@@ -26,6 +26,10 @@
<scope>provided</scope>
</dependency>
<dependency>
+ <groupId>org.jdbi</groupId>
+ <artifactId>jdbi</artifactId>
+ </dependency>
+ <dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<scope>provided</scope>
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/IEntitlementService.java b/api/src/main/java/com/ning/billing/entitlement/api/IEntitlementService.java
index ddcdf0c..4547119 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/IEntitlementService.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/IEntitlementService.java
@@ -16,11 +16,8 @@
package com.ning.billing.entitlement.api;
-import java.util.List;
-
import com.ning.billing.entitlement.api.billing.IEntitlementBillingApi;
import com.ning.billing.entitlement.api.test.IEntitlementTestApi;
-import com.ning.billing.entitlement.api.user.IApiListener;
import com.ning.billing.entitlement.api.user.IEntitlementUserApi;
import com.ning.billing.lifecycle.IService;
@@ -30,7 +27,7 @@ public interface IEntitlementService extends IService {
@Override
public String getName();
- public IEntitlementUserApi getUserApi(List<IApiListener> listeners);
+ public IEntitlementUserApi getUserApi();
public IEntitlementBillingApi getBillingApi();
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/user/IEntitlementUserApi.java b/api/src/main/java/com/ning/billing/entitlement/api/user/IEntitlementUserApi.java
index c3f79dc..f45f450 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/user/IEntitlementUserApi.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/user/IEntitlementUserApi.java
@@ -27,8 +27,6 @@ import com.ning.billing.catalog.api.BillingPeriod;
public interface IEntitlementUserApi {
- public void initialize(List<IApiListener> listeners);
-
public ISubscriptionBundle getBundleFromId(UUID id);
public ISubscription getSubscriptionFromId(UUID id);
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/user/ISubscriptionTransition.java b/api/src/main/java/com/ning/billing/entitlement/api/user/ISubscriptionTransition.java
index d4e1a5a..718c28a 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/user/ISubscriptionTransition.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/user/ISubscriptionTransition.java
@@ -23,9 +23,21 @@ import org.joda.time.DateTime;
import com.ning.billing.catalog.api.IPlan;
import com.ning.billing.catalog.api.IPlanPhase;
import com.ning.billing.entitlement.api.user.ISubscription.SubscriptionState;
+import com.ning.billing.util.eventbus.IEventBusType;
-public interface ISubscriptionTransition {
+public interface ISubscriptionTransition extends IEventBusType {
+ public enum SubscriptionTransitionType {
+ CREATE,
+ CHANGE,
+ PAUSE,
+ RESUME,
+ CANCEL,
+ UNCANCEL,
+ PHASE
+ }
+
+ SubscriptionTransitionType getTransitionType();
UUID getBundleId();
diff --git a/api/src/main/java/com/ning/billing/lifecycle/LyfecycleHandlerType.java b/api/src/main/java/com/ning/billing/lifecycle/LyfecycleHandlerType.java
index 520fca4..af15dea 100644
--- a/api/src/main/java/com/ning/billing/lifecycle/LyfecycleHandlerType.java
+++ b/api/src/main/java/com/ning/billing/lifecycle/LyfecycleHandlerType.java
@@ -20,6 +20,10 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+import java.util.Collections;
+import java.util.List;
+
+import com.google.common.collect.Lists;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@@ -29,51 +33,55 @@ public @interface LyfecycleHandlerType {
//
// The level themselves are still work in progress depending on what we really need
//
+ // Ordering is important in that enum
+ //
public enum LyfecycleLevel {
/**
* Load and validate catalog (only for catalog subsytem)
*/
- LOAD_CATALOG(Sequence.STARTUP),
+ LOAD_CATALOG(Sequence.STARTUP_PRE_EVENT_REGISTRATION),
/**
* Initialize event bus (only for the event bus)
*/
- INIT_BUS(Sequence.STARTUP),
+ INIT_BUS(Sequence.STARTUP_PRE_EVENT_REGISTRATION),
/**
- * Service specific initalization
+ * Service specific initalization-- service does not start yet
*/
- INIT_SERVICE(Sequence.STARTUP),
+ INIT_SERVICE(Sequence.STARTUP_PRE_EVENT_REGISTRATION),
/**
* Service register their interest in events
*/
- REGISTER_EVENTS(Sequence.STARTUP),
+ REGISTER_EVENTS(Sequence.STARTUP_PRE_EVENT_REGISTRATION),
/**
* Service start
* - API call should not work
* - Events might be triggered
* - Batch processing jobs started
*/
- START_SERVICE(Sequence.STARTUP),
+ START_SERVICE(Sequence.STARTUP_POST_EVENT_REGISTRATION),
/**
* Stop service
*/
- STOP_SERVICE(Sequence.SHUTOWN),
+ STOP_SERVICE(Sequence.SHUTOWN_PRE_EVENT_UNREGISTRATION),
/**
* Unregister interest in events
*/
- UNREGISTER_EVENTS(Sequence.SHUTOWN),
+ UNREGISTER_EVENTS(Sequence.SHUTOWN_PRE_EVENT_UNREGISTRATION),
/**
* Stop bus
*/
- STOP_BUS(Sequence.SHUTOWN),
+ STOP_BUS(Sequence.SHUTOWN_POST_EVENT_UNREGISTRATION),
/**
* Any service specific shutdown action before the end
*/
- SHUTDOWN(Sequence.SHUTOWN);
+ SHUTDOWN(Sequence.SHUTOWN_POST_EVENT_UNREGISTRATION);
public enum Sequence {
- STARTUP,
- SHUTOWN
+ STARTUP_PRE_EVENT_REGISTRATION,
+ STARTUP_POST_EVENT_REGISTRATION,
+ SHUTOWN_PRE_EVENT_UNREGISTRATION,
+ SHUTOWN_POST_EVENT_UNREGISTRATION
};
private Sequence seq;
@@ -85,6 +93,19 @@ public @interface LyfecycleHandlerType {
public Sequence getSequence() {
return seq;
}
+
+ //
+ // Returns an ordered list of level for a particular sequence
+ //
+ public static List<LyfecycleLevel> getLevelsForSequence(Sequence seq) {
+ List<LyfecycleLevel> result = Lists.newLinkedList();
+ for (LyfecycleLevel level : LyfecycleLevel.values()) {
+ if (level.getSequence() == seq) {
+ result.add(level);
+ }
+ }
+ return result;
+ }
}
public LyfecycleLevel value();
diff --git a/api/src/main/java/com/ning/billing/util/eventbus/IEventBus.java b/api/src/main/java/com/ning/billing/util/eventbus/IEventBus.java
new file mode 100644
index 0000000..90ab0bc
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/util/eventbus/IEventBus.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.util.eventbus;
+
+import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
+import com.google.common.eventbus.Subscribe;
+
+
+/**
+ *
+ * EventBus API based on the guava EventBus API
+ *
+ * The API also provides an API to send events from within a transaction
+ * with the guarantee that the event will be delivered if and only if
+ * the transaction completes. If the implementation is not based on a
+ * DB, this API is behaves the same as the regular post() call.
+ *
+ */
+public interface IEventBus {
+
+
+ public class EventBusException extends Exception {
+
+ private static final long serialVersionUID = 12355236L;
+
+ public EventBusException() {
+ super();
+ }
+ public EventBusException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public EventBusException(String message) {
+ super(message);
+ }
+ }
+
+ /**
+ * Start accepting events and dispatching them
+ *
+ */
+ public void start();
+
+ /**
+ * Stop accepting events and flush event queue before it returns.
+ *
+ */
+ public void stop();
+
+ /**
+ *
+ * Registers all handler methods on {@code object} to receive events.
+ * Handler methods need to be Annotated with {@link Subscribe}
+ *
+ * @param handlerInstance
+ *
+ * @throws EventBusException if bus not been started yet
+ */
+ public void register(Object handlerInstance) throws EventBusException;
+
+
+ /**
+ * Unregister the handler for a particular type of event
+ *
+ * @param handlerInstance
+ * @throws EventBusException
+ */
+ public void unregister(Object handlerInstance) throws EventBusException;
+
+
+ /**
+ * Post an event asynchronously
+ *
+ * @param event to be posted
+ *
+ * @throws EventBusException if bus not been started yet
+ */
+ public void post(IEventBusType event) throws EventBusException;
+
+ /**
+ *
+ * Post an event from within a transaction.
+ * Guarantees that the event is persisted on disk from within the same transaction
+ *
+ *
+ * @param event to be posted
+ * @param dao a valid DAO object obtained through the DBI.onDeamand() API.
+ *
+ * @throws EventBusException if bus not been started yet
+ */
+ public void postFromTransaction(IEventBusType event, Transmogrifier dao) throws EventBusException;
+
+
+}
beatrix/pom.xml 2(+1 -1)
diff --git a/beatrix/pom.xml b/beatrix/pom.xml
index a8c5515..3571e7d 100644
--- a/beatrix/pom.xml
+++ b/beatrix/pom.xml
@@ -13,7 +13,7 @@
<parent>
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
- <version>0.0.13-SNAPSHOT</version>
+ <version>0.0.14-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-beatrix</artifactId>
diff --git a/beatrix/src/main/java/com/ning/billing/beatrix/lifecycle/Lifecycle.java b/beatrix/src/main/java/com/ning/billing/beatrix/lifecycle/Lifecycle.java
index 6abdb75..f091a81 100644
--- a/beatrix/src/main/java/com/ning/billing/beatrix/lifecycle/Lifecycle.java
+++ b/beatrix/src/main/java/com/ning/billing/beatrix/lifecycle/Lifecycle.java
@@ -20,6 +20,7 @@ import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
@@ -74,21 +75,27 @@ public class Lifecycle {
}
}
- public void fireStartupSequence() {
- for (LyfecycleLevel level : LyfecycleLevel.values()) {
- if (level.getSequence() == Sequence.SHUTOWN) {
- break;
- }
- doFireStage(level);
- }
+
+ public void fireStartupSequencePriorEventRegistration() {
+ fireSequence(Sequence.STARTUP_PRE_EVENT_REGISTRATION);
}
- public void fireShutdownSequence() {
- for (LyfecycleLevel level : LyfecycleLevel.values()) {
- if (level.getSequence() == Sequence.STARTUP) {
- continue;
- }
- doFireStage(level);
+ public void fireStartupSequencePostEventRegistration() {
+ fireSequence(Sequence.STARTUP_POST_EVENT_REGISTRATION);
+ }
+
+ public void fireShutdownSequencePriorEventUnRegistration() {
+ fireSequence(Sequence.SHUTOWN_PRE_EVENT_UNREGISTRATION);
+ }
+
+ public void fireShutdownSequencePostEventUnRegistration() {
+ fireSequence(Sequence.SHUTOWN_POST_EVENT_UNREGISTRATION);
+ }
+
+ private void fireSequence(Sequence seq) {
+ List<LyfecycleLevel> levels = LyfecycleLevel.getLevelsForSequence(seq);
+ for (LyfecycleLevel cur : levels) {
+ doFireStage(cur);
}
}
@@ -103,12 +110,13 @@ public class Lifecycle {
log.info("Killbill lifecycle calling handler {} for service {}", cur.getMethod().getName(), target.getName());
method.invoke(target);
} catch (Exception e) {
- log.warn("Killbill lifecycle failed to invoke lifecycle handler", e);
+ logWarn("Killbill lifecycle failed to invoke lifecycle handler", e);
}
}
}
+
private Set<? extends IService> findServices() {
Set<IService> result = new HashSet<IService>();
@@ -120,7 +128,7 @@ public class Lifecycle {
log.debug("got instance {}", instance.getName());
result.add(instance);
} catch (Exception e) {
- log.warn("Failed to inject " + cur.getName(), e);
+ logWarn("Failed to inject " + cur.getName(), e);
}
}
@@ -128,9 +136,13 @@ public class Lifecycle {
}
+ // Used to disable valid injection failure from unit tests
+ protected void logWarn(String msg, Exception e) {
+ log.warn(msg, e);
+ }
+
public Multimap<LyfecycleLevel, LifecycleHandler<? extends IService>> findAllHandlers(IService service) {
- Multimap<LyfecycleLevel, LifecycleHandler<? extends IService>> methodsInService =
- HashMultimap.create();
+ Multimap<LyfecycleLevel, LifecycleHandler<? extends IService>> methodsInService = HashMultimap.create();
Class<? extends IService> clazz = service.getClass();
for (Method method : clazz.getMethods()) {
LyfecycleHandlerType annotation = method.getAnnotation(LyfecycleHandlerType.class);
diff --git a/beatrix/src/main/java/com/ning/billing/beatrix/lifecycle/ServiceFinder.java b/beatrix/src/main/java/com/ning/billing/beatrix/lifecycle/ServiceFinder.java
index fe3f0c5..6916cf1 100644
--- a/beatrix/src/main/java/com/ning/billing/beatrix/lifecycle/ServiceFinder.java
+++ b/beatrix/src/main/java/com/ning/billing/beatrix/lifecycle/ServiceFinder.java
@@ -99,10 +99,16 @@ public class ServiceFinder {
File classPath = new File( (URL.class).isInstance(classPaths[h]) ?
((URL)classPaths[h]).getFile() : classPaths[h].toString());
if (classPath.isDirectory()) {
+
+ log.debug("DIR : " + classPath);
+
List<String> dirListing = new ArrayList<String>();
recursivelyListDir(dirListing, classPath, new StringBuffer() );
files = Collections.enumeration( dirListing );
} else if (classPath.getName().endsWith(".jar")) {
+
+ log.debug("JAR : " + classPath);
+
String [] jarParts = classPath.getName().split("/");
String jarName = jarParts[jarParts.length - 1];
if (jarFilter != null && jarName != null && ! jarName.startsWith(jarFilter)) {
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/lifecycle/TestLifecycle.java b/beatrix/src/test/java/com/ning/billing/beatrix/lifecycle/TestLifecycle.java
index f85232f..791b2fe 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/lifecycle/TestLifecycle.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/lifecycle/TestLifecycle.java
@@ -18,15 +18,16 @@ package com.ning.billing.beatrix.lifecycle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
+import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Stage;
import com.ning.billing.lifecycle.IService;
-import com.ning.billing.lifecycle.Lifecycled;
import com.ning.billing.lifecycle.LyfecycleHandlerType;
import com.ning.billing.lifecycle.LyfecycleHandlerType.LyfecycleLevel;
@@ -40,16 +41,45 @@ public class TestLifecycle {
private Lifecycle lifecycle;
- public static class Service1 implements IService {
+ public static class ServiceBase {
+
+ private int count = 0;
+
+ public ServiceBase() {
+ reset();
+ }
+
+ public synchronized void reset() {
+ this.count = 0;
+ }
+
+ public synchronized int getCount() {
+ return count;
+ }
+
+ public synchronized void incrementCount() {
+ count++;
+ }
+ }
+
+ public static class Service1 extends ServiceBase implements IService {
@LyfecycleHandlerType(LyfecycleLevel.INIT_BUS)
public void initBus() {
log.info("Service1 : got INIT_BUS");
+ incrementCount();
}
@LyfecycleHandlerType(LyfecycleLevel.START_SERVICE)
public void startService() {
log.info("Service1 : got START_SERVICE");
+ incrementCount();
+ }
+
+ @LyfecycleHandlerType(LyfecycleLevel.SHUTDOWN)
+ public void shutdownService() {
+ log.info("Service1 : got SHUTDOWN");
+ incrementCount();
}
@Override
@@ -58,24 +88,41 @@ public class TestLifecycle {
}
}
- @Lifecycled
- public static class Service2 implements IService {
+ public static class Service2 extends ServiceBase implements IService {
@LyfecycleHandlerType(LyfecycleLevel.LOAD_CATALOG)
public void loadCatalog() {
- log.info("Service1 : got LOAD_CATALOG");
+ log.info("Service2 : got LOAD_CATALOG");
+ incrementCount();
+ }
+
+ @LyfecycleHandlerType(LyfecycleLevel.REGISTER_EVENTS)
+ public void registerEvents() {
+ log.info("Service2 : got REGISTER_EVENTS");
+ incrementCount();
+ }
+
+ @LyfecycleHandlerType(LyfecycleLevel.UNREGISTER_EVENTS)
+ public void unregisterEvents() {
+ log.info("Service2 : got UNREGISTER_EVENTS");
+ incrementCount();
+ }
+
+ @LyfecycleHandlerType(LyfecycleLevel.START_SERVICE)
+ public void startService() {
+ log.info("Service2 : got START_SERVICE");
+ incrementCount();
}
@Override
public String getName() {
return null;
}
-
}
- @BeforeClass(groups={"fast"})
+ @BeforeClass
public void setup() {
final Injector g = Guice.createInjector(Stage.DEVELOPMENT, new TestLifecycleModule());
s1 = g.getInstance(Service1.class);
@@ -83,17 +130,45 @@ public class TestLifecycle {
lifecycle = g.getInstance(Lifecycle.class);
}
- @Test
+ @Test(enabled=true, groups={"fast"})
public void testLifecycle() {
- lifecycle.fireStartupSequence();
+ s1.reset();
+ s2.reset();
+ lifecycle.fireStartupSequencePriorEventRegistration();
+ Assert.assertEquals(s1.getCount() + s2.getCount(), 3);
+
+ s1.reset();
+ s2.reset();
+ lifecycle.fireStartupSequencePostEventRegistration();
+ Assert.assertEquals(s1.getCount() + s2.getCount(), 2);
+
+ s1.reset();
+ s2.reset();
+ lifecycle.fireShutdownSequencePriorEventUnRegistration();
+ Assert.assertEquals(s1.getCount() + s2.getCount(), 1);
+
+ s1.reset();
+ s2.reset();
+ lifecycle.fireShutdownSequencePostEventUnRegistration();
+ Assert.assertEquals(s1.getCount() + s2.getCount(), 1);
}
+ public static class LifecycleNoWarn extends Lifecycle {
+
+ @Inject
+ public LifecycleNoWarn(Injector injector) {
+ super(injector);
+ }
+ @Override
+ protected void logWarn(String msg, Exception e) {
+ }
+ }
public static class TestLifecycleModule extends AbstractModule {
@Override
protected void configure() {
- bind(Lifecycle.class).asEagerSingleton();
+ bind(Lifecycle.class).to(LifecycleNoWarn.class).asEagerSingleton();
bind(Service1.class).asEagerSingleton();
bind(Service2.class).asEagerSingleton();
}
catalog/pom.xml 2(+1 -1)
diff --git a/catalog/pom.xml b/catalog/pom.xml
index db24ca9..5643272 100644
--- a/catalog/pom.xml
+++ b/catalog/pom.xml
@@ -13,7 +13,7 @@
<parent>
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
- <version>0.0.13-SNAPSHOT</version>
+ <version>0.0.14-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-catalog</artifactId>
entitlement/pom.xml 2(+1 -1)
diff --git a/entitlement/pom.xml b/entitlement/pom.xml
index 5f4d4ef..7483255 100644
--- a/entitlement/pom.xml
+++ b/entitlement/pom.xml
@@ -13,7 +13,7 @@
<parent>
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
- <version>0.0.13-SNAPSHOT</version>
+ <version>0.0.14-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-entitlement</artifactId>
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/alignment/PlanAligner.java b/entitlement/src/main/java/com/ning/billing/entitlement/alignment/PlanAligner.java
index 0d9ac8d..01ec2c0 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/alignment/PlanAligner.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/alignment/PlanAligner.java
@@ -139,13 +139,9 @@ public class PlanAligner implements IPlanAligner {
planStartDate = subscription.getBundleStartDate();
break;
case CHANGE_OF_PLAN:
- // STEPH
throw new EntitlementError(String.format("Not implemented yet %s", alignment));
- //break;
case CHANGE_OF_PRICELIST:
- // STEPH
throw new EntitlementError(String.format("Not implemented yet %s", alignment));
- //break;
default:
throw new EntitlementError(String.format("Unknwon PlanAlignmentChange %s", alignment));
}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/test/EntitlementTestApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/test/EntitlementTestApi.java
index 8ba459e..61cee03 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/test/EntitlementTestApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/test/EntitlementTestApi.java
@@ -19,6 +19,7 @@ package com.ning.billing.entitlement.api.test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.inject.Inject;
import com.ning.billing.config.IEntitlementConfig;
import com.ning.billing.entitlement.engine.core.IApiEventProcessor;
@@ -29,6 +30,7 @@ public class EntitlementTestApi implements IEntitlementTestApi {
private final IApiEventProcessor apiEventProcessor;
private final IEntitlementConfig config;
+ @Inject
public EntitlementTestApi(IApiEventProcessor apiEventProcessor, IEntitlementConfig config) {
this.apiEventProcessor = apiEventProcessor;
this.config = config;
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApi.java
index 19f2ec8..93fe561 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApi.java
@@ -22,53 +22,42 @@ import java.util.UUID;
import org.joda.time.DateTime;
+import com.google.inject.Inject;
import com.ning.billing.ErrorCode;
import com.ning.billing.account.api.IAccount;
import com.ning.billing.catalog.api.BillingPeriod;
-import com.ning.billing.catalog.api.ICatalog;
+import com.ning.billing.catalog.api.ICatalogService;
import com.ning.billing.catalog.api.IPlan;
import com.ning.billing.catalog.api.IPlanPhase;
import com.ning.billing.catalog.api.IPriceListSet;
import com.ning.billing.entitlement.alignment.IPlanAligner;
import com.ning.billing.entitlement.alignment.IPlanAligner.TimedPhase;
-import com.ning.billing.entitlement.engine.core.Engine;
+import com.ning.billing.entitlement.api.user.ISubscription;
+import com.ning.billing.entitlement.api.user.ISubscriptionBundle;
+import com.ning.billing.entitlement.api.user.IEntitlementUserApi;
import com.ning.billing.entitlement.engine.dao.IEntitlementDao;
import com.ning.billing.entitlement.events.IEvent;
import com.ning.billing.entitlement.events.phase.IPhaseEvent;
import com.ning.billing.entitlement.events.phase.PhaseEvent;
import com.ning.billing.entitlement.events.user.ApiEventCreate;
import com.ning.billing.entitlement.exceptions.EntitlementError;
-import com.ning.billing.entitlement.glue.InjectorMagic;
import com.ning.billing.util.clock.IClock;
public class EntitlementUserApi implements IEntitlementUserApi {
- private final Engine engine;
private final IClock clock;
private final IEntitlementDao dao;
private final IPlanAligner planAligner;
+ private final ICatalogService catalogService;
- private boolean initialized;
-
- private ICatalog catalog;
-
- public EntitlementUserApi(Engine engine, IClock clock, IPlanAligner planAligner, IEntitlementDao dao) {
+ @Inject
+ public EntitlementUserApi(IClock clock, IPlanAligner planAligner,
+ IEntitlementDao dao, ICatalogService catalogService) {
super();
- this.engine = engine;
this.clock = clock;
this.dao = dao;
this.planAligner = planAligner;
- this.initialized = false;
-
- }
-
- @Override
- public synchronized void initialize(List<IApiListener> listeners) {
- if (!initialized) {
- this.catalog = InjectorMagic.getCatlog();
- engine.registerApiObservers(listeners);
- initialized = true;
- }
+ this.catalogService = catalogService;
}
@Override
@@ -102,7 +91,6 @@ public class EntitlementUserApi implements IEntitlementUserApi {
public ISubscription createSubscription(UUID bundleId, String productName,
BillingPeriod term, String priceList, DateTime requestedDate) throws EntitlementUserApiException {
- // STEPH Should really get 'standard' from catalog
String realPriceList = (priceList == null) ? IPriceListSet.DEFAULT_PRICELIST_NAME : priceList;
DateTime now = clock.getUTCNow();
@@ -112,7 +100,7 @@ public class EntitlementUserApi implements IEntitlementUserApi {
requestedDate = (requestedDate == null) ? now : requestedDate;
- IPlan plan = catalog.getPlan(productName, term, realPriceList);
+ IPlan plan = catalogService.getCatalog().getPlan(productName, term, realPriceList);
if (plan == null) {
throw new EntitlementUserApiException(ErrorCode.ENT_CREATE_BAD_CATALOG, productName, term, realPriceList);
}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java
index 2c04998..afcf053 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java
@@ -47,7 +47,7 @@ import com.ning.billing.entitlement.events.user.ApiEventCancel;
import com.ning.billing.entitlement.events.user.ApiEventChange;
import com.ning.billing.entitlement.events.user.ApiEventType;
import com.ning.billing.entitlement.events.user.ApiEventUncancel;
-import com.ning.billing.entitlement.events.user.IUserEvent;
+import com.ning.billing.entitlement.events.user.IApiEvent;
import com.ning.billing.entitlement.exceptions.EntitlementError;
import com.ning.billing.entitlement.glue.InjectorMagic;
import com.ning.billing.util.clock.IClock;
@@ -295,8 +295,8 @@ public class Subscription extends PrivateFields implements ISubscription {
TimedPhase nextTimedPhase = planAligner.getNextTimedPhaseOnChange(this, newPlan, realPriceList, effectiveDate);
IPhaseEvent nextPhaseEvent = PhaseEvent.getNextPhaseEvent(nextTimedPhase, this, now);
List<IEvent> changeEvents = new ArrayList<IEvent>();
- // Add phase event first so we expect to see PHASE event first-- mostly for test expectation
- if (nextPhaseEvent != null) {
+ // 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);
@@ -479,7 +479,7 @@ public class Subscription extends PrivateFields implements ISubscription {
break;
case API_USER:
- IUserEvent userEV = (IUserEvent) cur;
+ IApiEvent userEV = (IApiEvent) cur;
apiEventType = userEV.getEventType();
switch(apiEventType) {
case CREATE:
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransition.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransition.java
index 63f42f6..616e871 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransition.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransition.java
@@ -25,6 +25,7 @@ import com.ning.billing.catalog.api.IPlanPhase;
import com.ning.billing.entitlement.api.user.ISubscription.SubscriptionState;
import com.ning.billing.entitlement.events.IEvent.EventType;
import com.ning.billing.entitlement.events.user.ApiEventType;
+import com.ning.billing.entitlement.exceptions.EntitlementError;
public class SubscriptionTransition implements ISubscriptionTransition {
@@ -117,6 +118,18 @@ public class SubscriptionTransition implements ISubscriptionTransition {
return nextPriceList;
}
+ @Override
+ public SubscriptionTransitionType getTransitionType() {
+ switch(eventType) {
+ case API_USER:
+ return apiEventType.getSubscriptionTransitionType();
+ case PHASE:
+ return SubscriptionTransitionType.PHASE;
+ default:
+ throw new EntitlementError("Unexpected event type " + eventType);
+ }
+ }
+
public ApiEventType getApiEventType() {
return apiEventType;
}
@@ -151,5 +164,4 @@ public class SubscriptionTransition implements ISubscriptionTransition {
+ ", nextPriceList " + nextPriceList
+ ", nextPhase=" + ((nextPhase != null) ? nextPhase.getName() : null) + "]";
}
-
}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/ApiEventProcessorBase.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/ApiEventProcessorBase.java
index c5b9bd4..f3faf8e 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/ApiEventProcessorBase.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/ApiEventProcessorBase.java
@@ -141,8 +141,8 @@ public abstract class ApiEventProcessorBase implements IApiEventProcessor {
Thread.currentThread().getId()));
} catch (Throwable e) {
log.error(API_EVENT_THREAD_NAME + " got an exception exiting...", e);
- // STEPH let's review that later...
- System.exit(1);
+ // Just to make it really obvious in the log
+ e.printStackTrace();
}
}
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 fee014e..622fa12 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
@@ -16,27 +16,21 @@
package com.ning.billing.entitlement.engine.core;
-import java.util.List;
-
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.inject.Inject;
-import com.ning.billing.catalog.api.ICatalog;
-import com.ning.billing.catalog.api.ICatalogService;
import com.ning.billing.config.IEntitlementConfig;
import com.ning.billing.entitlement.alignment.IPlanAligner;
import com.ning.billing.entitlement.alignment.IPlanAligner.TimedPhase;
-import com.ning.billing.entitlement.alignment.PlanAligner;
import com.ning.billing.entitlement.api.IEntitlementService;
-import com.ning.billing.entitlement.api.billing.BillingApi;
+import com.ning.billing.entitlement.api.billing.EntitlementBillingApi;
import com.ning.billing.entitlement.api.billing.IEntitlementBillingApi;
import com.ning.billing.entitlement.api.test.EntitlementTestApi;
import com.ning.billing.entitlement.api.test.IEntitlementTestApi;
import com.ning.billing.entitlement.api.user.EntitlementUserApi;
-import com.ning.billing.entitlement.api.user.IApiListener;
import com.ning.billing.entitlement.api.user.IEntitlementUserApi;
import com.ning.billing.entitlement.api.user.Subscription;
import com.ning.billing.entitlement.engine.dao.IEntitlementDao;
@@ -44,11 +38,13 @@ import com.ning.billing.entitlement.events.IEvent;
import com.ning.billing.entitlement.events.IEvent.EventType;
import com.ning.billing.entitlement.events.phase.IPhaseEvent;
import com.ning.billing.entitlement.events.phase.PhaseEvent;
-import com.ning.billing.entitlement.events.user.IUserEvent;
+import com.ning.billing.entitlement.events.user.IApiEvent;
import com.ning.billing.lifecycle.IService;
import com.ning.billing.lifecycle.LyfecycleHandlerType;
import com.ning.billing.lifecycle.LyfecycleHandlerType.LyfecycleLevel;
import com.ning.billing.util.clock.IClock;
+import com.ning.billing.util.eventbus.IEventBus;
+import com.ning.billing.util.eventbus.IEventBus.EventBusException;
public class Engine implements IEventListener, IEntitlementService {
@@ -63,24 +59,21 @@ public class Engine implements IEventListener, IEntitlementService {
private final IEntitlementUserApi userApi;
private final IEntitlementBillingApi billingApi;
private final IEntitlementTestApi testApi;
- private final IEntitlementConfig config;
- private List<IApiListener> observers;
-
+ private final IEventBus eventBus;
@Inject
public Engine(IClock clock, IEntitlementDao dao, IApiEventProcessor apiEventProcessor,
- IPlanAligner planAligner, IEntitlementConfig config) {
+ IPlanAligner planAligner, IEntitlementConfig config, EntitlementUserApi userApi,
+ EntitlementBillingApi billingApi, EntitlementTestApi testApi, IEventBus eventBus) {
super();
this.clock = clock;
this.dao = dao;
this.apiEventProcessor = apiEventProcessor;
this.planAligner = planAligner;
- this.config = config;
- this.observers = null;
- this.userApi = new EntitlementUserApi(this, clock, planAligner, dao);
- this.billingApi = new BillingApi(this, clock, dao);
- this.testApi = new EntitlementTestApi(apiEventProcessor, config);
-
+ this.userApi = userApi;
+ this.testApi = testApi;
+ this.billingApi = billingApi;
+ this.eventBus = eventBus;
}
@Override
@@ -104,8 +97,7 @@ public class Engine implements IEventListener, IEntitlementService {
}
@Override
- public IEntitlementUserApi getUserApi(List<IApiListener> listeners) {
- userApi.initialize(listeners);
+ public IEntitlementUserApi getUserApi() {
return userApi;
}
@@ -120,51 +112,22 @@ public class Engine implements IEventListener, IEntitlementService {
return testApi;
}
- public void registerApiObservers(List<IApiListener> observers) {
- this.observers = observers;
- }
-
-
@Override
public void processEventReady(IEvent event) {
- if (observers == null) {
- return;
- }
Subscription subscription = (Subscription) dao.getSubscriptionFromId(event.getSubscriptionId());
if (subscription == null) {
log.warn("Failed to retrieve subscription for id %s", event.getSubscriptionId());
return;
}
- if (event.getType() == EventType.API_USER) {
- dispatchApiEvent((IUserEvent) event, subscription);
- } else {
- dispatchPhaseEvent((IPhaseEvent) event, subscription);
+ if (event.getType() == EventType.PHASE) {
insertNextPhaseEvent(subscription);
}
- }
-
- private void dispatchApiEvent(IUserEvent event, Subscription subscription) {
- for (IApiListener listener : observers) {
- switch(event.getEventType()) {
- case CREATE:
- listener.subscriptionCreated(subscription.getLatestTranstion());
- break;
- case CHANGE:
- listener.subscriptionChanged(subscription.getLatestTranstion());
- break;
- case CANCEL:
- listener.subscriptionCancelled(subscription.getLatestTranstion());
- break;
- default:
- break;
- }
+ try {
+ eventBus.post(subscription.getLatestTranstion());
+ } catch (EventBusException e) {
+ log.warn("Failed to post entitlement event " + event, e);
}
- }
- private void dispatchPhaseEvent(IPhaseEvent event, Subscription subscription) {
- for (IApiListener listener : observers) {
- listener.subscriptionPhaseChanged(subscription.getLatestTranstion());
- }
}
private void insertNextPhaseEvent(Subscription subscription) {
@@ -174,7 +137,6 @@ public class Engine implements IEventListener, IEntitlementService {
TimedPhase nextTimedPhase = planAligner.getNextTimedPhase(subscription, subscription.getCurrentPlan(), now, subscription.getCurrentPlanStart());
IPhaseEvent nextPhaseEvent = PhaseEvent.getNextPhaseEvent(nextTimedPhase, subscription, now);
if (nextPhaseEvent != null) {
- // STEPH Harden since event could be processed twice
dao.createNextPhaseEvent(subscription.getId(), nextPhaseEvent);
}
}
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 db4e375..197e4bd 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
@@ -41,7 +41,7 @@ import com.ning.billing.entitlement.api.user.SubscriptionBundle;
import com.ning.billing.entitlement.events.IEvent;
import com.ning.billing.entitlement.events.IEvent.EventType;
import com.ning.billing.entitlement.events.user.ApiEventType;
-import com.ning.billing.entitlement.events.user.IUserEvent;
+import com.ning.billing.entitlement.events.user.IApiEvent;
import com.ning.billing.entitlement.exceptions.EntitlementError;
import com.ning.billing.util.Hostname;
import com.ning.billing.util.clock.IClock;
@@ -249,7 +249,7 @@ public class EntitlementDao implements IEntitlementDao {
List<IEvent> events = dao.getFutureActiveEventForSubscription(subscriptionId.toString(), now);
for (IEvent cur : events) {
- if (cur.getType() == EventType.API_USER && ((IUserEvent) cur).getEventType() == ApiEventType.CANCEL) {
+ if (cur.getType() == EventType.API_USER && ((IApiEvent) cur).getEventType() == ApiEventType.CANCEL) {
if (existingCancelId != null) {
throw new EntitlementError(String.format("Found multiple cancel active events for subscriptions %s", subscriptionId.toString()));
}
@@ -299,7 +299,7 @@ public class EntitlementDao implements IEntitlementDao {
List<IEvent> events = dao.getFutureActiveEventForSubscription(subscriptionId.toString(), now);
for (IEvent cur : events) {
if (cur.getType() == type &&
- (apiType == null || apiType == ((IUserEvent) cur).getEventType() )) {
+ (apiType == null || apiType == ((IApiEvent) cur).getEventType() )) {
if (futureEventId != null) {
throw new EntitlementError(
String.format("Found multiple future events for type %s for subscriptions %s",
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/IEventSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/IEventSqlDao.java
index 007d5b9..3c2180d 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/IEventSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/IEventSqlDao.java
@@ -50,7 +50,7 @@ import com.ning.billing.entitlement.events.user.ApiEventPause;
import com.ning.billing.entitlement.events.user.ApiEventResume;
import com.ning.billing.entitlement.events.user.ApiEventType;
import com.ning.billing.entitlement.events.user.ApiEventUncancel;
-import com.ning.billing.entitlement.events.user.IUserEvent;
+import com.ning.billing.entitlement.events.user.IApiEvent;
import com.ning.billing.entitlement.exceptions.EntitlementError;
@ExternalizedSqlViaStringTemplate3()
@@ -99,15 +99,15 @@ public interface IEventSqlDao extends Transactional<IEventSqlDao>, CloseMe, Tran
public void bind(@SuppressWarnings("rawtypes") SQLStatement stmt, Bind bind, IEvent evt) {
stmt.bind("event_id", evt.getId().toString());
stmt.bind("event_type", evt.getType().toString());
- stmt.bind("user_type", (evt.getType() == EventType.API_USER) ? ((IUserEvent) evt).getEventType().toString() : null);
+ stmt.bind("user_type", (evt.getType() == EventType.API_USER) ? ((IApiEvent) evt).getEventType().toString() : null);
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) ? ((IUserEvent) evt).getEventPlan() : null);
- stmt.bind("phase_name", (evt.getType() == EventType.API_USER) ? ((IUserEvent) evt).getEventPlanPhase() : ((IPhaseEvent) evt).getPhase());
- stmt.bind("plist_name", (evt.getType() == EventType.API_USER) ? ((IUserEvent) evt).getPriceList() : null);
+ stmt.bind("plan_name", (evt.getType() == EventType.API_USER) ? ((IApiEvent) evt).getEventPlan() : null);
+ stmt.bind("phase_name", (evt.getType() == EventType.API_USER) ? ((IApiEvent) evt).getEventPlanPhase() : ((IPhaseEvent) evt).getPhase());
+ stmt.bind("plist_name", (evt.getType() == EventType.API_USER) ? ((IApiEvent) evt).getPriceList() : null);
stmt.bind("current_version", evt.getActiveVersion());
stmt.bind("is_active", evt.isActive());
stmt.bind("processing_available_dt", getDate(evt.getNextAvailableDate()));
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/events/EventBase.java b/entitlement/src/main/java/com/ning/billing/entitlement/events/EventBase.java
index a0c526a..b1e9f02 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/events/EventBase.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/events/EventBase.java
@@ -22,7 +22,7 @@ import org.joda.time.DateTime;
import com.ning.billing.catalog.api.IPlan;
import com.ning.billing.entitlement.events.IEventLyfecycle.IEventLyfecycleState;
-import com.ning.billing.entitlement.events.user.IUserEvent;
+import com.ning.billing.entitlement.events.user.IApiEvent;
import com.ning.billing.entitlement.exceptions.EntitlementError;
public abstract class EventBase implements IEvent {
@@ -213,7 +213,7 @@ public abstract class EventBase implements IEvent {
} else if (getType() != other.getType()) {
return (getType() == EventType.PHASE) ? -1 : 1;
} else if (getType() == EventType.API_USER) {
- return ((IUserEvent) this).getEventType().compareTo(((IUserEvent) other).getEventType());
+ return ((IApiEvent) this).getEventType().compareTo(((IApiEvent) other).getEventType());
} else {
return uuid.compareTo(other.getId());
}
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 4e2944e..e1b9177 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
@@ -27,7 +27,7 @@ import com.ning.billing.entitlement.engine.core.Engine;
import com.ning.billing.entitlement.events.EventBase;
import com.ning.billing.entitlement.events.IEventLyfecycle.IEventLyfecycleState;
-public class ApiEventBase extends EventBase implements IUserEvent {
+public class ApiEventBase extends EventBase implements IApiEvent {
private final ApiEventType eventType;
// Only valid for CREATE/CHANGE
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventType.java b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventType.java
index e07e39f..3d66a5b 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventType.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventType.java
@@ -16,16 +16,35 @@
package com.ning.billing.entitlement.events.user;
+import com.ning.billing.entitlement.api.user.ISubscriptionTransition.SubscriptionTransitionType;
+
public enum ApiEventType {
- /*
- * STEPH should be changed
- * Ordering is important for unit tests today.
- */
- CREATE,
- CHANGE,
- PAUSE,
- RESUME,
- CANCEL,
- UNCANCEL
+ CREATE {
+ @Override
+ public SubscriptionTransitionType getSubscriptionTransitionType() { return SubscriptionTransitionType.CREATE; }
+ },
+ CHANGE {
+ @Override
+ public SubscriptionTransitionType getSubscriptionTransitionType() { return SubscriptionTransitionType.CHANGE; }
+ },
+ PAUSE {
+ @Override
+ public SubscriptionTransitionType getSubscriptionTransitionType() { return SubscriptionTransitionType.PAUSE; }
+ },
+ RESUME {
+ @Override
+ public SubscriptionTransitionType getSubscriptionTransitionType() { return SubscriptionTransitionType.RESUME; }
+ },
+ CANCEL {
+ @Override
+ public SubscriptionTransitionType getSubscriptionTransitionType() { return SubscriptionTransitionType.CANCEL; }
+ },
+ UNCANCEL {
+ @Override
+ public SubscriptionTransitionType getSubscriptionTransitionType() { return SubscriptionTransitionType.UNCANCEL; }
+ };
+
+ // Used to map from internal events to User visible events (both user and phase)
+ public abstract SubscriptionTransitionType getSubscriptionTransitionType();
}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/glue/EntitlementModule.java b/entitlement/src/main/java/com/ning/billing/entitlement/glue/EntitlementModule.java
index d303b30..89b58c8 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/glue/EntitlementModule.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/glue/EntitlementModule.java
@@ -23,6 +23,10 @@ import com.ning.billing.config.IEntitlementConfig;
import com.ning.billing.entitlement.alignment.IPlanAligner;
import com.ning.billing.entitlement.alignment.PlanAligner;
import com.ning.billing.entitlement.api.IEntitlementService;
+import com.ning.billing.entitlement.api.billing.EntitlementBillingApi;
+import com.ning.billing.entitlement.api.billing.IEntitlementBillingApi;
+import com.ning.billing.entitlement.api.test.EntitlementTestApi;
+import com.ning.billing.entitlement.api.test.IEntitlementTestApi;
import com.ning.billing.entitlement.api.user.EntitlementUserApi;
import com.ning.billing.entitlement.api.user.IEntitlementUserApi;
import com.ning.billing.entitlement.engine.core.ApiEventProcessor;
@@ -59,6 +63,9 @@ public class EntitlementModule extends AbstractModule {
bind(IEntitlementService.class).to(Engine.class).asEagerSingleton();
bind(Engine.class).asEagerSingleton();
bind(IPlanAligner.class).to(PlanAligner.class).asEagerSingleton();
+ bind(IEntitlementTestApi.class).to(EntitlementTestApi.class).asEagerSingleton();
+ bind(IEntitlementUserApi.class).to(EntitlementUserApi.class).asEagerSingleton();
+ bind(IEntitlementBillingApi.class).to(EntitlementBillingApi.class).asEagerSingleton();
}
protected void installInjectorMagic() {
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/ApiTestListener.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/ApiTestListener.java
index 9591ee6..5b5100b 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/ApiTestListener.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/ApiTestListener.java
@@ -22,10 +22,11 @@ import java.util.Stack;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.ning.billing.entitlement.api.user.IApiListener;
+import com.google.common.eventbus.Subscribe;
import com.ning.billing.entitlement.api.user.ISubscriptionTransition;
+import com.ning.billing.util.eventbus.IEventBus;
-public class ApiTestListener implements IApiListener {
+public class ApiTestListener {
private static final Logger log = LoggerFactory.getLogger(ApiTestListener.class);
@@ -42,11 +43,40 @@ public class ApiTestListener implements IApiListener {
PHASE
}
- public ApiTestListener() {
+ public ApiTestListener(IEventBus eventBus) {
this.nextExpectedEvent = new Stack<NextEvent>();
this.completed = false;
}
+ @Subscribe
+ public void handleEntitlementEvent(ISubscriptionTransition event) {
+ switch (event.getTransitionType()) {
+ case CREATE:
+ subscriptionCreated(event);
+ break;
+ case CANCEL:
+ subscriptionCancelled(event);
+ break;
+ case CHANGE:
+ subscriptionChanged(event);
+ break;
+ case PAUSE:
+ subscriptionPaused(event);
+ break;
+ case RESUME:
+ subscriptionResumed(event);
+ break;
+ case UNCANCEL:
+ break;
+ case PHASE:
+ subscriptionPhaseChanged(event);
+ break;
+ default:
+ throw new RuntimeException("Unexpected event type " + event.getRequestedTransitionTime());
+ }
+
+ }
+
public void pushExpectedEvent(NextEvent next) {
synchronized (this) {
nextExpectedEvent.push(next);
@@ -98,42 +128,42 @@ public class ApiTestListener implements IApiListener {
}
}
- @Override
+
public void subscriptionCreated(ISubscriptionTransition created) {
log.debug("-> Got event CREATED");
assertEqualsNicely(nextExpectedEvent.pop(), NextEvent.CREATE);
notifyIfStackEmpty();
}
- @Override
+
public void subscriptionCancelled(ISubscriptionTransition cancelled) {
log.debug("-> Got event CANCEL");
assertEqualsNicely(nextExpectedEvent.pop(), NextEvent.CANCEL);
notifyIfStackEmpty();
}
- @Override
+
public void subscriptionChanged(ISubscriptionTransition changed) {
log.debug("-> Got event CHANGE");
assertEqualsNicely(nextExpectedEvent.pop(), NextEvent.CHANGE);
notifyIfStackEmpty();
}
- @Override
+
public void subscriptionPaused(ISubscriptionTransition paused) {
log.debug("-> Got event PAUSE");
assertEqualsNicely(nextExpectedEvent.pop(), NextEvent.PAUSE);
notifyIfStackEmpty();
}
- @Override
+
public void subscriptionResumed(ISubscriptionTransition resumed) {
log.debug("-> Got event RESUME");
assertEqualsNicely(nextExpectedEvent.pop(), NextEvent.RESUME);
notifyIfStackEmpty();
}
- @Override
+
public void subscriptionPhaseChanged(
ISubscriptionTransition phaseChanged) {
log.debug("-> Got event PHASE");
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiBase.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiBase.java
index 4094acd..924f1eb 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiBase.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiBase.java
@@ -63,11 +63,13 @@ import com.ning.billing.entitlement.engine.dao.IEntitlementDaoMock;
import com.ning.billing.entitlement.events.IEvent;
import com.ning.billing.entitlement.events.phase.IPhaseEvent;
import com.ning.billing.entitlement.events.user.ApiEventType;
-import com.ning.billing.entitlement.events.user.IUserEvent;
+import com.ning.billing.entitlement.events.user.IApiEvent;
import com.ning.billing.entitlement.glue.InjectorMagic;
import com.ning.billing.lifecycle.IService.ServiceException;
import com.ning.billing.util.clock.ClockMock;
import com.ning.billing.util.clock.IClock;
+import com.ning.billing.util.eventbus.EventBusService;
+import com.ning.billing.util.eventbus.IEventBusService;
public abstract class TestUserApiBase {
@@ -82,14 +84,13 @@ public abstract class TestUserApiBase {
protected IEntitlementConfig config;
protected IEntitlementDao dao;
protected ClockMock clock;
+ protected IEventBusService busService;
protected IAccount account;
protected ICatalog catalog;
protected ApiTestListener testListener;
protected ISubscriptionBundle bundle;
- private InjectorMagic injectorMagic;
-
public static void loadSystemPropertiesFromClasspath( final String resource )
{
final URL url = TestUserApiBase.class.getResource(resource);
@@ -104,7 +105,14 @@ public abstract class TestUserApiBase {
@AfterClass(groups={"setup"})
public void tearDown() {
- InjectorMagic.instance = null;
+ try {
+ InjectorMagic.instance = null;
+ busService.getEventBus().register(testListener);
+ ((EventBusService) busService).stopBus();
+ } catch (Exception e) {
+ log.warn("Failed to tearDown test properly ", e);
+ }
+
}
@BeforeClass(groups={"setup"})
@@ -113,18 +121,17 @@ public abstract class TestUserApiBase {
loadSystemPropertiesFromClasspath("/entitlement.properties");
final Injector g = getInjector();
- injectorMagic = g.getInstance(InjectorMagic.class);
-
-
entitlementService = g.getInstance(IEntitlementService.class);
catalogService = g.getInstance(ICatalogService.class);
+ busService = g.getInstance(IEventBusService.class);
config = g.getInstance(IEntitlementConfig.class);
dao = g.getInstance(IEntitlementDao.class);
clock = (ClockMock) g.getInstance(IClock.class);
try {
((CatalogService) catalogService).loadCatalog();
- ((Engine)entitlementService).initialize();
+ ((EventBusService) busService).startBus();
+ ((Engine) entitlementService).initialize();
init();
} catch (EntitlementUserApiException e) {
Assert.fail(e.getMessage());
@@ -142,10 +149,9 @@ public abstract class TestUserApiBase {
catalog = catalogService.getCatalog();
assertNotNull(catalog);
- testListener = new ApiTestListener();
- List<IApiListener> listeners = new ArrayList<IApiListener>();
- listeners.add(testListener);
- entitlementApi = entitlementService.getUserApi(listeners);
+
+ testListener = new ApiTestListener(busService.getEventBus());
+ entitlementApi = entitlementService.getUserApi();
billingApi = entitlementService.getBillingApi();
}
@@ -157,11 +163,13 @@ public abstract class TestUserApiBase {
log.warn("RESET TEST FRAMEWORK\n\n");
testListener.reset();
+
clock.resetDeltaFromReality();
((IEntitlementDaoMock) dao).reset();
try {
+ busService.getEventBus().register(testListener);
bundle = entitlementApi.createBundleForAccount(account, "myDefaultBundle");
- } catch (EntitlementUserApiException e) {
+ } catch (Exception e) {
Assert.fail(e.getMessage());
}
assertNotNull(bundle);
@@ -222,8 +230,8 @@ public abstract class TestUserApiBase {
assertEquals(foundPhase, false);
foundPhase = true;
assertEquals(cur.getEffectiveDate(), expPhaseChange);
- } else if (cur instanceof IUserEvent) {
- IUserEvent uEvent = (IUserEvent) cur;
+ } else if (cur instanceof IApiEvent) {
+ IApiEvent uEvent = (IApiEvent) cur;
assertEquals(ApiEventType.CHANGE, uEvent.getEventType());
assertEquals(foundChange, false);
foundChange = true;
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlan.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlan.java
index 373db87..32f1dc9 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlan.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlan.java
@@ -36,7 +36,7 @@ import com.ning.billing.catalog.api.PhaseType;
import com.ning.billing.catalog.api.ProductCategory;
import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
import com.ning.billing.entitlement.events.IEvent;
-import com.ning.billing.entitlement.events.user.IUserEvent;
+import com.ning.billing.entitlement.events.user.IApiEvent;
import com.ning.billing.util.clock.Clock;
public abstract class TestUserApiChangePlan extends TestUserApiBase {
@@ -145,7 +145,7 @@ public abstract class TestUserApiChangePlan extends TestUserApiBase {
// ALSO VERIFY PENDING CHANGE EVENT
List<IEvent> events = dao.getPendingEventsForSubscription(subscription.getId());
- assertTrue(events.get(0) instanceof IUserEvent);
+ assertTrue(events.get(0) instanceof IApiEvent);
// MOVE TO EOT
@@ -341,7 +341,6 @@ public abstract class TestUserApiChangePlan extends TestUserApiBase {
IPlanPhase trialPhase = subscription.getCurrentPhase();
assertEquals(trialPhase.getPhaseType(), PhaseType.TRIAL);
- // MOVE TO NEXT PHASE
testListener.pushExpectedEvent(NextEvent.PHASE);
clock.setDeltaFromReality(trialPhase.getDuration(), DAY_IN_MS);
assertTrue(testListener.isCompleted(2000));
@@ -378,9 +377,25 @@ public abstract class TestUserApiChangePlan extends TestUserApiBase {
assertNotNull(currentPhase);
assertEquals(currentPhase.getPhaseType(), PhaseType.DISCOUNT);
+ // ACTIVATE CHNAGE BY MOVING AFTER CTD
+ testListener.pushExpectedEvent(NextEvent.CHANGE);
+ clock.addDeltaFromReality(ctd);
+ assertTrue(testListener.isCompleted(3000));
+
+ currentPlan = subscription.getCurrentPlan();
+ assertNotNull(currentPlan);
+ assertEquals(currentPlan.getProduct().getName(), "Pistol");
+ assertEquals(currentPlan.getProduct().getCategory(), ProductCategory.BASE);
+ assertEquals(currentPlan.getBillingPeriod(), BillingPeriod.ANNUAL);
+
+ currentPhase = subscription.getCurrentPhase();
+ assertNotNull(currentPhase);
+ assertEquals(currentPhase.getPhaseType(), PhaseType.DISCOUNT);
+
+
+
// MOVE TO NEXT PHASE
testListener.pushExpectedEvent(NextEvent.PHASE);
- testListener.pushExpectedEvent(NextEvent.CHANGE);
clock.addDeltaFromReality(currentPhase.getDuration());
assertTrue(testListener.isCompleted(3000));
subscription = (Subscription) entitlementApi.getSubscriptionFromId(subscription.getId());
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanMemory.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanMemory.java
index 4455436..8847612 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanMemory.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanMemory.java
@@ -57,7 +57,7 @@ public class TestUserApiChangePlanMemory extends TestUserApiChangePlan {
invokeRealMethod(this);
}
- // STEPH set to false until we implement rescue example.
+ // Set to false until we implement rescue example.
@Test(enabled=false, groups={"fast"})
public void testChangePlanChangePlanAlignEOTWithChargeThroughDate() {
invokeRealMethod(this);
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanSql.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanSql.java
index 174b85e..60d777d 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanSql.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanSql.java
@@ -79,7 +79,7 @@ public class TestUserApiChangePlanSql extends TestUserApiChangePlan {
invokeRealMethod(this);
}
- // STEPH rescue not implemented
+ // rescue not implemented yet
@Test(enabled=false, groups={"sql"})
public void testChangePlanChangePlanAlignEOTWithChargeThroughDate() {
invokeRealMethod(this);
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/EntitlementDaoMemoryMock.java b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/EntitlementDaoMemoryMock.java
index cc574ee..1e043eb 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/EntitlementDaoMemoryMock.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/EntitlementDaoMemoryMock.java
@@ -40,7 +40,7 @@ import com.ning.billing.entitlement.events.IEvent;
import com.ning.billing.entitlement.events.IEvent.EventType;
import com.ning.billing.entitlement.events.IEventLyfecycle.IEventLyfecycleState;
import com.ning.billing.entitlement.events.user.ApiEventType;
-import com.ning.billing.entitlement.events.user.IUserEvent;
+import com.ning.billing.entitlement.events.user.IApiEvent;
import com.ning.billing.util.clock.IClock;
public class EntitlementDaoMemoryMock implements IEntitlementDao, IEntitlementDaoMock {
@@ -294,7 +294,7 @@ public class EntitlementDaoMemoryMock implements IEntitlementDao, IEntitlementDa
continue;
}
if (cur.getType() == EventType.API_USER &&
- ApiEventType.CHANGE == ((IUserEvent) cur).getEventType() &&
+ ApiEventType.CHANGE == ((IApiEvent) cur).getEventType() &&
cur.getProcessingState() == IEventLyfecycleState.AVAILABLE) {
cur.deactivate();
break;
@@ -315,7 +315,7 @@ public class EntitlementDaoMemoryMock implements IEntitlementDao, IEntitlementDa
continue;
}
if (cur.getType() == EventType.API_USER &&
- ((IUserEvent) cur).getEventType() == ApiEventType.CANCEL) {
+ ((IApiEvent) cur).getEventType() == ApiEventType.CANCEL) {
cur.deactivate();
foundCancel = true;
break;
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/glue/EngineModuleMock.java b/entitlement/src/test/java/com/ning/billing/entitlement/glue/EngineModuleMock.java
index d70a2f7..de812ee 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/glue/EngineModuleMock.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/glue/EngineModuleMock.java
@@ -18,6 +18,7 @@ package com.ning.billing.entitlement.glue;
import com.ning.billing.util.clock.ClockMock;
import com.ning.billing.util.clock.IClock;
+import com.ning.billing.util.glue.EventBusModule;
public class EngineModuleMock extends EntitlementModule {
@@ -29,6 +30,7 @@ public class EngineModuleMock extends EntitlementModule {
@Override
protected void configure() {
super.configure();
+ install(new EventBusModule());
}
}
invoice/pom.xml 2(+1 -1)
diff --git a/invoice/pom.xml b/invoice/pom.xml
index 3e589ad..0958498 100644
--- a/invoice/pom.xml
+++ b/invoice/pom.xml
@@ -13,7 +13,7 @@
<parent>
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
- <version>0.0.13-SNAPSHOT</version>
+ <version>0.0.14-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-invoice</artifactId>
payment/pom.xml 2(+1 -1)
diff --git a/payment/pom.xml b/payment/pom.xml
index b902c81..e69d221 100644
--- a/payment/pom.xml
+++ b/payment/pom.xml
@@ -13,7 +13,7 @@
<parent>
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
- <version>0.0.13-SNAPSHOT</version>
+ <version>0.0.14-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-payment</artifactId>
pom.xml 3(+2 -1)
diff --git a/pom.xml b/pom.xml
index bf8e215..8038652 100644
--- a/pom.xml
+++ b/pom.xml
@@ -17,7 +17,7 @@
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
<packaging>pom</packaging>
- <version>0.0.13-SNAPSHOT</version>
+ <version>0.0.14-SNAPSHOT</version>
<name>killbill</name>
<description>Library for managing recurring subscriptions and the associated billing</description>
<url>http://github.com/ning/killbill</url>
@@ -373,6 +373,7 @@
<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>
</systemPropertyVariables>
util/pom.xml 11(+5 -6)
diff --git a/util/pom.xml b/util/pom.xml
index e451499..3f3a40e 100644
--- a/util/pom.xml
+++ b/util/pom.xml
@@ -13,7 +13,7 @@
<parent>
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
- <version>0.0.13-SNAPSHOT</version>
+ <version>0.0.14-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-util</artifactId>
@@ -29,6 +29,10 @@
<artifactId>bonecp</artifactId>
</dependency>
<dependency>
+ <groupId>org.jdbi</groupId>
+ <artifactId>jdbi</artifactId>
+ </dependency>
+ <dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>3.0</version>
@@ -47,11 +51,6 @@
<scope>test</scope>
</dependency>
<dependency>
- <groupId>org.jdbi</groupId>
- <artifactId>jdbi</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<scope>test</scope>
diff --git a/util/src/main/java/com/ning/billing/util/eventbus/EventBusService.java b/util/src/main/java/com/ning/billing/util/eventbus/EventBusService.java
new file mode 100644
index 0000000..53c07c9
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/eventbus/EventBusService.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.util.eventbus;
+
+import com.google.inject.Inject;
+import com.ning.billing.lifecycle.LyfecycleHandlerType;
+import com.ning.billing.lifecycle.LyfecycleHandlerType.LyfecycleLevel;
+
+public class EventBusService implements IEventBusService {
+
+ private final static String EVENT_BUS_SERVICE = "eventbus-service";
+
+ private final IEventBus eventBus;
+
+ @Inject
+ public EventBusService(IEventBus eventBus) {
+ this.eventBus = eventBus;
+ }
+
+ @Override
+ public String getName() {
+ return EVENT_BUS_SERVICE;
+ }
+
+ @LyfecycleHandlerType(LyfecycleLevel.INIT_BUS)
+ public void startBus() {
+ eventBus.start();
+ }
+
+ @LyfecycleHandlerType(LyfecycleLevel.STOP_BUS)
+ public void stopBus() {
+ eventBus.stop();
+ }
+
+ @Override
+ public IEventBus getEventBus() {
+ return eventBus;
+ }
+
+}
diff --git a/util/src/main/java/com/ning/billing/util/eventbus/MemoryEventBus.java b/util/src/main/java/com/ning/billing/util/eventbus/MemoryEventBus.java
new file mode 100644
index 0000000..7cc0453
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/eventbus/MemoryEventBus.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.util.eventbus;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.eventbus.AsyncEventBus;
+
+public class MemoryEventBus implements IEventBus {
+
+ // STEPH config ?
+ private final static int MAX_EVENT_THREADS = 1;
+
+ private final static String EVENT_BUS_IDENTIFIER = "eventbus-service";
+ private final static String EVENT_BUS_GROUP_NAME = "eventbus-grp";
+ private final static String EVENT_BUS_TH_NAME = "eventbus-th";
+
+ private static final Logger log = LoggerFactory.getLogger(MemoryEventBus.class);
+
+ private final EventBusDelegate delegate;
+ private final AtomicBoolean isInitialized;
+
+ public class EventBusDelegate extends AsyncEventBus {
+
+ private final Executor executor;
+ private final ThreadGroup grp;
+
+ public EventBusDelegate(String name, ThreadGroup grp, Executor executor) {
+ super(name, executor);
+ this.executor = executor;
+ this.grp = grp;
+ }
+
+ public void completeDispatch() {
+ dispatchQueuedEvents();
+ }
+
+ // No way to really 'stop' an executor; what we do is:
+ // i) disallow any new events into the queue
+ // ii) empty the queue
+ //
+ // That will only work if the event submitter handles EventBusException correctly when posting.
+ //
+ public void stop() {
+ }
+ }
+
+ public MemoryEventBus() {
+
+ final ThreadGroup group = new ThreadGroup(EVENT_BUS_GROUP_NAME);
+ Executor executor = Executors.newFixedThreadPool(MAX_EVENT_THREADS, new ThreadFactory() {
+ @Override
+ public Thread newThread(Runnable r) {
+ return new Thread(group, r, EVENT_BUS_TH_NAME);
+ }
+ });
+
+ this.delegate = new EventBusDelegate(EVENT_BUS_IDENTIFIER, group, executor);
+ this.isInitialized = new AtomicBoolean(false);
+ }
+
+ @Override
+ public void register(Object handlerInstnace) throws EventBusException {
+ checkInitialized("register");
+ delegate.register(handlerInstnace);
+ }
+
+ @Override
+ public void unregister(Object handlerInstance) throws EventBusException {
+ checkInitialized("unregister");
+ delegate.unregister(handlerInstance);
+ }
+
+ @Override
+ public void post(IEventBusType event) throws EventBusException {
+ checkInitialized("post");
+ delegate.post(event);
+ }
+
+ @Override
+ public void postFromTransaction(IEventBusType event, Transmogrifier dao) throws EventBusException {
+ checkInitialized("postFromTransaction");
+ delegate.post(event);
+ }
+
+ @Override
+ public void start() {
+ if (isInitialized.compareAndSet(false, true)) {
+ log.info("MemoryEventBus started...");
+
+ }
+ }
+
+
+ private void checkInitialized(String operation) throws EventBusException {
+ if (!isInitialized.get()) {
+ throw new EventBusException(String.format("Attempting operation %s on an non initialized eventbus",
+ operation));
+ }
+ }
+ @Override
+ public void stop() {
+ if (isInitialized.compareAndSet(true, false)) {
+ log.info("MemoryEventBus stopping...");
+ delegate.completeDispatch();
+ delegate.stop();
+ log.info("MemoryEventBus stoped...");
+ }
+ }
+}
diff --git a/util/src/main/java/com/ning/billing/util/glue/EventBusModule.java b/util/src/main/java/com/ning/billing/util/glue/EventBusModule.java
new file mode 100644
index 0000000..e6e995a
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/glue/EventBusModule.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.util.glue;
+
+import com.google.inject.AbstractModule;
+import com.ning.billing.util.eventbus.EventBusService;
+import com.ning.billing.util.eventbus.IEventBus;
+import com.ning.billing.util.eventbus.IEventBusService;
+import com.ning.billing.util.eventbus.MemoryEventBus;
+
+public class EventBusModule extends AbstractModule {
+
+ @Override
+ protected void configure() {
+ bind(IEventBusService.class).to(EventBusService.class);
+ bind(IEventBus.class).to(MemoryEventBus.class).asEagerSingleton();
+
+ }
+
+}
diff --git a/util/src/test/java/com/ning/billing/util/eventbus/TestEventBus.java b/util/src/test/java/com/ning/billing/util/eventbus/TestEventBus.java
new file mode 100644
index 0000000..e4e41df
--- /dev/null
+++ b/util/src/test/java/com/ning/billing/util/eventbus/TestEventBus.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.util.eventbus;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import com.google.common.eventbus.Subscribe;
+
+public class TestEventBus {
+
+ private static final Logger log = LoggerFactory.getLogger(TestEventBus.class);
+
+ private IEventBus eventBus;
+
+
+ @BeforeClass
+ public void setup() {
+ eventBus = new MemoryEventBus();
+ eventBus.start();
+ }
+
+ @AfterClass
+ public void tearDown() {
+ eventBus.stop();
+ }
+
+ public static final class MyEvent implements IEventBusType {
+ String name;
+ Long value;
+
+ public MyEvent(String name, Long value) {
+ this.name = name;
+ this.value = value;
+ }
+ }
+
+ public static class MyEventHandler {
+
+ private final int expectedEvents;
+
+ private int gotEvents;
+
+
+ public MyEventHandler(int exp) {
+ this.expectedEvents = exp;
+ this.gotEvents = 0;
+ }
+
+ public synchronized int getEvents() {
+ return gotEvents;
+ }
+
+ @Subscribe
+ public synchronized void processEvent(MyEvent event) {
+ gotEvents++;
+ log.info("Got event {} {}", event.name, event.value);
+ }
+
+ public synchronized boolean waitForCompletion(long timeoutMs) {
+
+ while (gotEvents < expectedEvents) {
+ try {
+ wait(timeoutMs);
+ break;
+ } catch (InterruptedException ignore) {
+ }
+ }
+ return (gotEvents == expectedEvents);
+ }
+ }
+
+ @Test()
+ public void test() {
+ try {
+
+ int nbEvents = 127;
+ MyEventHandler handler = new MyEventHandler(nbEvents);
+ eventBus.register(handler);
+
+ for (int i = 0; i < nbEvents; i++) {
+ eventBus.post(new MyEvent("my-event", (long) i));
+ }
+
+ boolean completed = handler.waitForCompletion(3000);
+ Assert.assertEquals(completed, true);
+ } catch (Exception e) {
+ Assert.fail("",e);
+ }
+
+ }
+}
util/src/test/resources/log4j.xml 2(+1 -1)
diff --git a/util/src/test/resources/log4j.xml b/util/src/test/resources/log4j.xml
index 75abc76..ded1f80 100644
--- a/util/src/test/resources/log4j.xml
+++ b/util/src/test/resources/log4j.xml
@@ -25,7 +25,7 @@
</appender>
- <logger name="com.ning.billing.entitlement">
+ <logger name="com.ning.billing.util">
<level value="info"/>
</logger>