diff --git a/api-java/src/main/java/se/scalablesolutions/akka/api/ActiveObjectGuiceConfigurator.java b/api-java/src/main/java/se/scalablesolutions/akka/api/ActiveObjectGuiceConfigurator.java new file mode 100755 index 0000000000..2eecce344c --- /dev/null +++ b/api-java/src/main/java/se/scalablesolutions/akka/api/ActiveObjectGuiceConfigurator.java @@ -0,0 +1,121 @@ +/** + * Copyright (C) 2009 Scalable Solutions. + */ + +package se.scalablesolutions.akka.api; + +import com.google.inject.*; +import com.google.inject.jsr250.ResourceProviderFactory; + +import se.scalablesolutions.akka.kernel.configuration.*; +import se.scalablesolutions.akka.kernel.ActiveObjectFactory; +import se.scalablesolutions.akka.kernel.ActiveObjectProxy; +import se.scalablesolutions.akka.kernel.Supervisor; +import se.scalablesolutions.akka.kernel.Worker; + +import java.util.List; +import java.util.ArrayList; +import java.util.Map; +import java.util.HashMap; + +/** + * @author Jonas Bonér + */ +public class ActiveObjectGuiceConfigurator { + private List modules = new ArrayList(); + private Injector injector; + private Supervisor supervisor; + private RestartStrategy restartStrategy; + private Component[] components; + private Map configRegistry = new HashMap(); + private Map activeObjectRegistry = new HashMap(); + private ActiveObjectFactory activeObjectFactory = new ActiveObjectFactory(); + + public synchronized T getExternalDependency(Class clazz) { + return injector.getInstance(clazz); + } + + /** + * Returns the active abject that has been put under supervision for the class specified. + * + * @param clazz the class for the active object + * @return the active object for the class + */ + public synchronized T getActiveObject(Class clazz) { + if (injector == null) throw new IllegalStateException("inject() and supervise() must be called before invoking newInstance(clazz)"); + if (activeObjectRegistry.containsKey(clazz)) { + final ActiveObjectProxy activeObjectProxy = activeObjectRegistry.get(clazz); + activeObjectProxy.setTargetInstance(injector.getInstance(clazz)); + return (T)activeObjectFactory.newInstance(clazz, activeObjectProxy); + } else throw new IllegalStateException("Class " + clazz.getName() + " has not been put under supervision (by passing in the config to the supervise() method"); + } + + public synchronized ActiveObjectGuiceConfigurator configureActiveObjects(final RestartStrategy restartStrategy, final Component[] components) { + this.restartStrategy = restartStrategy; + this.components = components; + modules.add(new AbstractModule() { + protected void configure() { + bind(ResourceProviderFactory.class); + for (int i = 0; i < components.length; i++) { + Component c = components[i]; + bind((Class) c.intf()).to((Class) c.target()).in(Singleton.class); + } + } + }); + return this; + } + + public synchronized ActiveObjectGuiceConfigurator inject() { + if (injector != null) throw new IllegalStateException("inject() has already been called on this configurator"); + injector = Guice.createInjector(modules); + return this; + } + + public synchronized ActiveObjectGuiceConfigurator supervise() { + if (injector == null) inject(); + injector = Guice.createInjector(modules); + List workers = new ArrayList(); + for (int i = 0; i < components.length; i++) { + final Component c = components[i]; + final ActiveObjectProxy activeObjectProxy = new ActiveObjectProxy(c.intf(), c.target(), c.timeout()); + workers.add(c.newWorker(activeObjectProxy)); + activeObjectRegistry.put(c.intf(), activeObjectProxy); + } + supervisor = activeObjectFactory.supervise(restartStrategy.transform(), workers); + return this; + } + + + /** + * Add additional services to be wired in. + *
+   * ActiveObjectGuiceConfigurator.addExternalGuiceModule(new AbstractModule {
+   *   protected void configure() {
+   *     bind(Foo.class).to(FooImpl.class).in(Scopes.SINGLETON);
+   *     bind(BarImpl.class);
+   *     link(Bar.class).to(BarImpl.class);
+   *     bindConstant(named("port")).to(8080);
+   *   }})
+   * 
+ */ + public synchronized ActiveObjectGuiceConfigurator addExternalGuiceModule(Module module) { + modules.add(module); + return this; + } + + public List getGuiceModules() { + return modules; + } + + public synchronized void reset() { + modules = new ArrayList(); + configRegistry = new HashMap(); + activeObjectRegistry = new HashMap(); + injector = null; + restartStrategy = null; + } + + public synchronized void stop() { + // TODO: fix supervisor.stop(); + } +} diff --git a/api-java/src/test/java/se/scalablesolutions/akka/api/ActiveObjectGuiceConfiguratorTest.java b/api-java/src/test/java/se/scalablesolutions/akka/api/ActiveObjectGuiceConfiguratorTest.java new file mode 100755 index 0000000000..0f40e57bbc --- /dev/null +++ b/api-java/src/test/java/se/scalablesolutions/akka/api/ActiveObjectGuiceConfiguratorTest.java @@ -0,0 +1,169 @@ +/** + * Copyright (C) 2009 Scalable Solutions. + */ + +package se.scalablesolutions.akka.api; + +import se.scalablesolutions.akka.annotation.oneway; +import se.scalablesolutions.akka.kernel.configuration.*; + +import com.google.inject.Inject; +import com.google.inject.AbstractModule; +import com.google.inject.Scopes; + +import junit.framework.TestCase; + +public class ActiveObjectGuiceConfiguratorTest extends TestCase { + static String messageLog = ""; + + final private ActiveObjectGuiceConfigurator conf = new ActiveObjectGuiceConfigurator(); + + protected void setUp() { + conf.addExternalGuiceModule(new AbstractModule() { + protected void configure() { + bind(Ext.class).to(ExtImpl.class).in(Scopes.SINGLETON); + } + }).configureActiveObjects( + new RestartStrategy(new AllForOne(), 3, 100), new Component[]{ + new Component( + Foo.class, + FooImpl.class, + new LifeCycle(new Permanent(), 100), + 1000), + new Component( + Bar.class, + BarImpl.class, + new LifeCycle(new Permanent(), 100), + 1000) + }).inject().supervise(); + + } + + public void testGuiceActiveObjectInjection() { + messageLog = ""; + Foo foo = conf.getActiveObject(Foo.class); + Bar bar = conf.getActiveObject(Bar.class); + assertTrue(foo.getBar().toString().equals(bar.toString())); + } + + public void testGuiceExternalDependencyInjection() { + messageLog = ""; + Bar bar = conf.getActiveObject(Bar.class); + Ext ext = conf.getExternalDependency(Ext.class); + assertTrue(bar.getExt().toString().equals(ext.toString())); + } + + public void testActiveObjectInvocation() throws InterruptedException { + messageLog = ""; + Foo foo = conf.getActiveObject(Foo.class); + messageLog += foo.foo("foo "); + foo.bar("bar "); + messageLog += "before_bar "; + Thread.sleep(500); + assertEquals("foo return_foo before_bar ", messageLog); + } + + public void testActiveObjectInvocationsInvocation() throws InterruptedException { + messageLog = ""; + Foo foo = conf.getActiveObject(Foo.class); + Bar bar = conf.getActiveObject(Bar.class); + messageLog += foo.foo("foo "); + foo.bar("bar "); + messageLog += "before_bar "; + Thread.sleep(500); + assertEquals("foo return_foo before_bar ", messageLog); + } + + public void testForcedTimeout() { + messageLog = ""; + Foo foo = conf.getActiveObject(Foo.class); + try { + foo.longRunning(); + fail("exception should have been thrown"); + } catch (se.scalablesolutions.akka.kernel.ActiveObjectInvocationTimeoutException e) { + } + } + + public void testForcedException() { + messageLog = ""; + Foo foo = conf.getActiveObject(Foo.class); + try { + foo.throwsException(); + fail("exception should have been thrown"); + } catch (RuntimeException e) { + } + } +} + +// ============== TEST SERVICES =============== + +interface Foo { + public String foo(String msg); + + @oneway + public void bar(String msg); + + public void longRunning(); + + public void throwsException(); + + public Bar getBar(); +} + +class FooImpl implements Foo { + @Inject + private Bar bar; + + public Bar getBar() { + return bar; + } + + public String foo(String msg) { + return msg + "return_foo "; + } + + public void bar(String msg) { + bar.bar(msg); + } + + public void longRunning() { + try { + Thread.sleep(10000); + } catch (InterruptedException e) { + } + } + + public void throwsException() { + throw new RuntimeException("expected"); + } +} + +interface Bar { + @oneway + void bar(String msg); + + Ext getExt(); +} + +class BarImpl implements Bar { + @Inject + private Ext ext; + + public Ext getExt() { + return ext; + } + + public void bar(String msg) { + } +} + +interface Ext { + void ext(); +} + +class ExtImpl implements Ext { + public void ext() { + } +} + + diff --git a/util-java/src/main/java/se/scalablesolutions/akka/annotation/oneway.java b/util-java/src/main/java/se/scalablesolutions/akka/annotation/oneway.java new file mode 100644 index 0000000000..7a0c7261f0 --- /dev/null +++ b/util-java/src/main/java/se/scalablesolutions/akka/annotation/oneway.java @@ -0,0 +1,11 @@ +/** + * Copyright (C) 2009 Scalable Solutions. + */ + +package se.scalablesolutions.akka.annotation; + +import java.lang.annotation.*; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface oneway {}