diff --git a/akka-core/src/main/scala/actor/ActiveObject.scala b/akka-core/src/main/scala/actor/ActiveObject.scala
index 9b5a6b409a..e425d29216 100644
--- a/akka-core/src/main/scala/actor/ActiveObject.scala
+++ b/akka-core/src/main/scala/actor/ActiveObject.scala
@@ -209,22 +209,24 @@ object ActiveObject {
}
private[akka] object AspectInitRegistry {
- private val inits = new java.util.concurrent.ConcurrentHashMap[AnyRef, AspectInit]
+ private val initializations = new java.util.concurrent.ConcurrentHashMap[AnyRef, AspectInit]
def initFor(target: AnyRef) = {
- val init = inits.get(target)
- inits.remove(target)
+ val init = initializations.get(target)
+ initializations.remove(target)
init
}
- def register(target: AnyRef, init: AspectInit) = inits.put(target, init)
+ def register(target: AnyRef, init: AspectInit) = initializations.put(target, init)
}
private[akka] sealed case class AspectInit(
val target: Class[_],
val actor: Dispatcher,
val remoteAddress: Option[InetSocketAddress],
- val timeout: Long)
+ val timeout: Long) {
+ def this(target: Class[_],actor: Dispatcher, timeout: Long) = this(target, actor, None, timeout)
+}
/**
* AspectWerkz Aspect that is turning POJOs into Active Object.
@@ -350,20 +352,26 @@ private[akka] sealed class ActiveObjectAspect {
}
}
+object Dispatcher {
+ val ZERO_ITEM_CLASS_ARRAY = Array[Class[_]]()
+ val ZERO_ITEM_OBJECT_ARRAY = Array[Object[_]]()
+}
+
/**
* Generic Actor managing Invocation dispatch, transaction and error management.
*
* @author Jonas Bonér
*/
private[akka] class Dispatcher(transactionalRequired: Boolean, val callbacks: Option[RestartCallbacks]) extends Actor {
- private val ZERO_ITEM_CLASS_ARRAY = Array[Class[_]]()
- private val ZERO_ITEM_OBJECT_ARRAY = Array[Object[_]]()
-
+ import Dispatcher._
+
private[actor] var target: Option[AnyRef] = None
private var preRestart: Option[Method] = None
private var postRestart: Option[Method] = None
private var initTxState: Option[Method] = None
+ def this(transactionalRequired: Boolean) = this(transactionalRequired,None)
+
private[actor] def initialize(targetClass: Class[_], targetInstance: AnyRef) = {
if (transactionalRequired || targetClass.isAnnotationPresent(Annotations.transactionrequired)) makeTransactionRequired
id = targetClass.getName
diff --git a/akka-spring/akka-spring-test-java/pom.xml b/akka-spring/akka-spring-test-java/pom.xml
new file mode 100755
index 0000000000..3a9474ab06
--- /dev/null
+++ b/akka-spring/akka-spring-test-java/pom.xml
@@ -0,0 +1,333 @@
+
+ 4.0.0
+
+ Akka Spring Tests in Java
+ akka-spring-test-java
+ se.scalablesolutions.akka
+ 0.7-SNAPSHOT
+ jar
+
+
+ 2.7.7
+ 0.5.2
+ 1.1.5
+ 1.9.18-i
+
+
+
+
+ project.embedded.module
+ Project Embedded Repository
+ file://${env.AKKA_HOME}/embedded-repo
+
+
+ repo1.maven
+ Maven Main Repository
+ http://repo1.maven.org/maven2
+
+
+ scala-tools-snapshots
+ Scala-Tools Maven2 Snapshot Repository
+ http://scala-tools.org/repo-snapshots
+
+
+ scala-tools
+ Scala-Tools Maven2 Repository
+ http://scala-tools.org/repo-releases
+
+
+ lag
+ Configgy's' Repository
+ http://www.lag.net/repo
+
+
+ multiverse-releases
+ http://multiverse.googlecode.com/svn/maven-repository/releases
+
+ false
+
+
+
+ multiverse-snaphosts
+ http://multiverse.googlecode.com/svn/maven-repository/snapshots
+
+
+ maven2-repository.dev.java.net
+ Java.net Repository for Maven
+ http://download.java.net/maven/2
+
+
+ java.net
+ Java.net Legacy Repository for Maven
+ http://download.java.net/maven/1
+ legacy
+
+
+ guiceyfruit.release
+ GuiceyFruit Release Repository
+ http://guiceyfruit.googlecode.com/svn/repo/releases/
+
+ false
+
+
+ true
+
+
+
+ guiceyfruit.snapshot
+ GuiceyFruit Snapshot Repository
+ http://guiceyfruit.googlecode.com/svn/repo/snapshots/
+
+ true
+
+
+ false
+
+
+
+ guice-maven
+ guice maven
+ http://guice-maven.googlecode.com/svn/trunk
+
+
+ google-maven-repository
+ Google Maven Repository
+ http://google-maven-repository.googlecode.com/svn/repository/
+
+
+ repository.codehaus.org
+ Codehaus Maven Repository
+ http://repository.codehaus.org
+
+ true
+
+
+
+ repository.jboss.org
+ JBoss Repository for Maven
+ http://repository.jboss.org/maven2
+
+ false
+
+
+
+ nexus.griddynamics.net
+ Grid Dynamics Maven Repository
+ https://nexus.griddynamics.net/nexus/content/groups/public
+
+ false
+
+
+
+ databinder.net/repo/
+ dbDispatch Repository for Maven
+ http://databinder.net/repo
+
+ false
+
+
+
+
+
+
+
+ akka-core
+ se.scalablesolutions.akka
+ 0.7-SNAPSHOT
+
+
+ akka-util
+ se.scalablesolutions.akka
+ 0.7-SNAPSHOT
+
+
+ akka-util-java
+ se.scalablesolutions.akka
+ 0.7-SNAPSHOT
+
+
+ akka-spring
+ se.scalablesolutions.akka
+ 0.7-SNAPSHOT
+
+
+ spring
+ org.springframework
+
+
+
+
+
+
+
+ org.springframework
+ spring-beans
+ 3.0.1.RELEASE
+
+
+ org.springframework
+ spring-context
+ 3.0.1.RELEASE
+
+
+
+ net.lag
+ configgy
+ 1.4.7
+
+
+ org.codehaus.aspectwerkz
+ aspectwerkz-nodeps-jdk5
+ 2.1
+
+
+ org.codehaus.aspectwerkz
+ aspectwerkz-jdk5
+ 2.1
+
+
+
+ org.guiceyfruit
+ guice-core
+ 2.0-beta-4
+
+
+ com.google.protobuf
+ protobuf-java
+ 2.2.0
+
+
+ com.google.protobuf
+ protobuf-java
+ 2.2.0
+
+
+ org.multiverse
+ multiverse-alpha
+ 0.4-SNAPSHOT
+
+
+ commons-io
+ commons-io
+ 1.4
+
+
+ org.jboss.netty
+ netty
+ 3.2.0.BETA1
+
+
+ net.databinder
+ dispatch-json_2.7.7
+ 0.6.4
+
+
+ net.databinder
+ dispatch-http_2.7.7
+ 0.6.4
+
+
+ sjson.json
+ sjson
+ 0.4
+
+
+
+ sbinary
+ sbinary
+ 0.3
+
+
+ org.codehaus.jackson
+ jackson-mapper-asl
+ 1.2.1
+
+
+ org.codehaus.jackson
+ jackson-core-asl
+ 1.2.1
+
+
+ voldemort.store.compress
+ h2-lzf
+ 1.0
+
+
+ org.scala-tools
+ javautils
+ 2.7.4-0.1
+
+
+ org.scala-lang
+ scala-library
+ 2.7.7
+
+
+
+ org.scala-lang
+ scala-library
+ 2.7.7
+
+
+
+
+ junit
+ junit
+ 4.5
+ test
+
+
+
+
+ src/main/java
+ src/test/java
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ 1.5
+ 1.5
+
+ **/*
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ **/*Persistent*
+
+
+
+
+
+
+ false
+ src/test/resources
+
+
+ false
+ src/main/resources
+
+
+ false
+ src/test/java
+
+ **
+
+
+ **/*.java
+
+
+
+
+
+
+
+
diff --git a/akka-spring/akka-spring-test-java/src/main/java/se/scalablesolutions/akka/spring/foo/Bar.java b/akka-spring/akka-spring-test-java/src/main/java/se/scalablesolutions/akka/spring/foo/Bar.java
new file mode 100644
index 0000000000..fc3392250a
--- /dev/null
+++ b/akka-spring/akka-spring-test-java/src/main/java/se/scalablesolutions/akka/spring/foo/Bar.java
@@ -0,0 +1,10 @@
+package se.scalablesolutions.akka.spring.foo;
+
+public class Bar implements IBar {
+
+ @Override
+ public String getBar() {
+ return "bar";
+ }
+
+}
diff --git a/akka-spring/akka-spring-test-java/src/main/java/se/scalablesolutions/akka/spring/foo/Foo.java b/akka-spring/akka-spring-test-java/src/main/java/se/scalablesolutions/akka/spring/foo/Foo.java
new file mode 100644
index 0000000000..36536cdb5d
--- /dev/null
+++ b/akka-spring/akka-spring-test-java/src/main/java/se/scalablesolutions/akka/spring/foo/Foo.java
@@ -0,0 +1,9 @@
+package se.scalablesolutions.akka.spring.foo;
+
+public class Foo {
+
+ public String foo() {
+ return "foo";
+ }
+
+}
diff --git a/akka-spring/akka-spring-test-java/src/main/java/se/scalablesolutions/akka/spring/foo/IBar.java b/akka-spring/akka-spring-test-java/src/main/java/se/scalablesolutions/akka/spring/foo/IBar.java
new file mode 100644
index 0000000000..66f91ee464
--- /dev/null
+++ b/akka-spring/akka-spring-test-java/src/main/java/se/scalablesolutions/akka/spring/foo/IBar.java
@@ -0,0 +1,7 @@
+package se.scalablesolutions.akka.spring.foo;
+
+public interface IBar {
+
+ String getBar();
+
+}
diff --git a/akka-spring/akka-spring-test-java/src/main/java/se/scalablesolutions/akka/spring/foo/MyPojo.java b/akka-spring/akka-spring-test-java/src/main/java/se/scalablesolutions/akka/spring/foo/MyPojo.java
new file mode 100644
index 0000000000..2d1f032cf8
--- /dev/null
+++ b/akka-spring/akka-spring-test-java/src/main/java/se/scalablesolutions/akka/spring/foo/MyPojo.java
@@ -0,0 +1,40 @@
+package se.scalablesolutions.akka.spring.foo;
+
+public class MyPojo {
+
+ private String foo;
+ private String bar;
+
+
+ public MyPojo() {
+ this.foo = "foo";
+ this.bar = "bar";
+ }
+
+
+ public String getFoo() {
+ return foo;
+ }
+
+
+ public String getBar() {
+ return bar;
+ }
+
+ public void preRestart() {
+ System.out.println("pre restart");
+ }
+
+ public void postRestart() {
+ System.out.println("post restart");
+ }
+
+ public String longRunning() {
+ try {
+ Thread.sleep(6000);
+ } catch (InterruptedException e) {
+ }
+ return "this took long";
+ }
+
+}
diff --git a/akka-spring/akka-spring-test-java/src/main/java/se/scalablesolutions/akka/spring/foo/StatefulPojo.java b/akka-spring/akka-spring-test-java/src/main/java/se/scalablesolutions/akka/spring/foo/StatefulPojo.java
new file mode 100644
index 0000000000..7db06c9a65
--- /dev/null
+++ b/akka-spring/akka-spring-test-java/src/main/java/se/scalablesolutions/akka/spring/foo/StatefulPojo.java
@@ -0,0 +1,47 @@
+package se.scalablesolutions.akka.spring.foo;
+
+import se.scalablesolutions.akka.stm.TransactionalMap;
+import se.scalablesolutions.akka.stm.TransactionalVector;
+import se.scalablesolutions.akka.stm.TransactionalRef;
+import se.scalablesolutions.akka.stm.TransactionalState;
+
+public class StatefulPojo {
+ private TransactionalMap mapState;
+ private TransactionalVector vectorState;
+ private TransactionalRef refState;
+ private boolean isInitialized = false;
+
+ public void init() {
+ if (!isInitialized) {
+ mapState = TransactionalState.newMap();
+ vectorState = TransactionalState.newVector();
+ refState = TransactionalState.newRef();
+ isInitialized = true;
+ }
+ }
+
+ public String getMapState(String key) {
+ return (String)mapState.get(key).get();
+ }
+
+ public String getVectorState() {
+ return (String)vectorState.last();
+ }
+
+ public String getRefState() {
+ return (String)refState.get().get();
+ }
+
+ public void setMapState(String key, String msg) {
+ mapState.put(key, msg);
+ }
+
+ public void setVectorState(String msg) {
+ vectorState.add(msg);
+ }
+
+ public void setRefState(String msg) {
+ refState.swap(msg);
+ }
+
+}
diff --git a/akka-spring/akka-spring-test-java/src/main/resources/se/scalablesolutions/akka/spring/foo/test-config.xml b/akka-spring/akka-spring-test-java/src/main/resources/se/scalablesolutions/akka/spring/foo/test-config.xml
new file mode 100644
index 0000000000..aa45e3aa4e
--- /dev/null
+++ b/akka-spring/akka-spring-test-java/src/main/resources/se/scalablesolutions/akka/spring/foo/test-config.xml
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ java.io.IOException
+ java.lang.NullPointerException
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ java.lang.Exception
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/akka-spring/akka-spring-test-java/src/test/java/se/scalablesolutions/akka/spring/SpringConfigurationTest.java b/akka-spring/akka-spring-test-java/src/test/java/se/scalablesolutions/akka/spring/SpringConfigurationTest.java
new file mode 100644
index 0000000000..5737b8da30
--- /dev/null
+++ b/akka-spring/akka-spring-test-java/src/test/java/se/scalablesolutions/akka/spring/SpringConfigurationTest.java
@@ -0,0 +1,120 @@
+package se.scalablesolutions.akka.spring;
+
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import org.springframework.beans.factory.support.DefaultListableBeanFactory;
+import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.Resource;
+
+import se.scalablesolutions.akka.config.ActiveObjectConfigurator;
+import se.scalablesolutions.akka.dispatch.FutureTimeoutException;
+import se.scalablesolutions.akka.config.Config;
+import se.scalablesolutions.akka.remote.RemoteNode;
+import se.scalablesolutions.akka.spring.foo.Foo;
+import se.scalablesolutions.akka.spring.foo.IBar;
+import se.scalablesolutions.akka.spring.foo.MyPojo;
+import se.scalablesolutions.akka.spring.foo.StatefulPojo;
+
+/**
+ * Tests for spring configuration of active objects and supervisor configuration.
+ */
+public class SpringConfigurationTest {
+
+ private ApplicationContext context = null;
+
+ @Before
+ public void setUp() {
+ context = new ClassPathXmlApplicationContext("se/scalablesolutions/akka/spring/foo/test-config.xml");
+ }
+
+ /**
+ * Tests that the <akka:active-object/> and <akka:supervision/> element
+ * can be used as a top level element.
+ */
+ @Test
+ public void testParse() throws Exception {
+ final Resource CONTEXT = new ClassPathResource("se/scalablesolutions/akka/spring/foo/test-config.xml");
+ DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
+ XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
+ reader.loadBeanDefinitions(CONTEXT);
+ assertTrue(beanFactory.containsBeanDefinition("simple-active-object"));
+ assertTrue(beanFactory.containsBeanDefinition("remote-active-object"));
+ assertTrue(beanFactory.containsBeanDefinition("supervision1"));
+ }
+
+ @Test
+ public void testSimpleActiveObject() {
+ MyPojo myPojo = (MyPojo) context.getBean("simple-active-object");
+ String msg = myPojo.getFoo();
+ msg += myPojo.getBar();
+ assertEquals("wrong invocation order", "foobar", msg);
+ }
+
+ @Test(expected=FutureTimeoutException.class)
+ public void testSimpleActiveObject_Timeout() {
+ MyPojo myPojo = (MyPojo) context.getBean("simple-active-object");
+ myPojo.longRunning();
+ }
+
+ @Test
+ public void testSimpleActiveObject_NoTimeout() {
+ MyPojo myPojo = (MyPojo) context.getBean("simple-active-object-long-timeout");
+ String msg = myPojo.longRunning();
+ assertEquals("this took long", msg);
+ }
+
+ @Test
+ public void testTransactionalActiveObject() {
+ MyPojo myPojo = (MyPojo) context.getBean("transactional-active-object");
+ String msg = myPojo.getFoo();
+ msg += myPojo.getBar();
+ assertEquals("wrong invocation order", "foobar", msg);
+ }
+
+ @Test
+ public void testRemoteActiveObject() {
+ new Thread(new Runnable() {
+ public void run() {
+ RemoteNode.start();
+ }
+ }).start();
+ try { Thread.currentThread().sleep(1000); } catch (Exception e) {}
+ Config.config();
+
+ MyPojo myPojo = (MyPojo) context.getBean("remote-active-object");
+ assertEquals("foo", myPojo.getFoo());
+ }
+
+ @Test
+ public void testSupervision() {
+ // get ActiveObjectConfigurator bean from spring context
+ ActiveObjectConfigurator myConfigurator = (ActiveObjectConfigurator) context.getBean("supervision1");
+ // get ActiveObjects
+ Foo foo = myConfigurator.getInstance(Foo.class);
+ assertNotNull(foo);
+ IBar bar = myConfigurator.getInstance(IBar.class);
+ assertNotNull(bar);
+ MyPojo pojo = myConfigurator.getInstance(MyPojo.class);
+ assertNotNull(pojo);
+ }
+
+ @Test
+ public void testTransactionalState() {
+ ActiveObjectConfigurator conf = (ActiveObjectConfigurator) context.getBean("supervision2");
+ StatefulPojo stateful = conf.getInstance(StatefulPojo.class);
+ stateful.init();
+ stateful.setMapState("testTransactionalState", "some map state");
+ stateful.setVectorState("some vector state");
+ stateful.setRefState("some ref state");
+ assertEquals("some map state", stateful.getMapState("testTransactionalState"));
+ assertEquals("some vector state", stateful.getVectorState());
+ assertEquals("some ref state", stateful.getRefState());
+ }
+
+}
diff --git a/akka-spring/src/main/resources/META-INF/spring.handlers b/akka-spring/src/main/resources/META-INF/spring.handlers
new file mode 100644
index 0000000000..c8d9dc55ae
--- /dev/null
+++ b/akka-spring/src/main/resources/META-INF/spring.handlers
@@ -0,0 +1 @@
+http\://www.akkasource.org/schema/akka=se.scalablesolutions.akka.spring.AkkaNamespaceHandler
\ No newline at end of file
diff --git a/akka-spring/src/main/resources/META-INF/spring.schemas b/akka-spring/src/main/resources/META-INF/spring.schemas
new file mode 100644
index 0000000000..d04d65566a
--- /dev/null
+++ b/akka-spring/src/main/resources/META-INF/spring.schemas
@@ -0,0 +1 @@
+http\://www.akkasource.org/schema/akka=se/scalablesolutions/akka/spring/akka.xsd
diff --git a/akka-spring/src/main/resources/se/scalablesolutions/akka/spring/akka.xsd b/akka-spring/src/main/resources/se/scalablesolutions/akka/spring/akka.xsd
new file mode 100644
index 0000000000..134e53e82f
--- /dev/null
+++ b/akka-spring/src/main/resources/se/scalablesolutions/akka/spring/akka.xsd
@@ -0,0 +1,163 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Name of the remote host.
+
+
+
+
+
+
+ Port of the remote host.
+
+
+
+
+
+
+
+
+
+
+ Pre restart callback method that is called during restart.
+
+
+
+
+
+
+ Post restart callback method that is called during restart.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Name of the target class.
+
+
+
+
+
+
+ default timeout for '!!' invocations
+
+
+
+
+
+
+ Set to true if messages should have REQUIRES_NEW semantics
+
+
+
+
+
+
+ Interface implemented by target class.
+
+
+
+
+
+
+ Lifecycle, permanent or temporary
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Failover scheme, AllForOne or OneForOne
+
+
+
+
+
+
+ Maximal number of retries.
+
+
+
+
+
+
+ Timerange for restart.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/akka-spring/src/main/scala/ActiveObjectBeanDefinitionParser.scala b/akka-spring/src/main/scala/ActiveObjectBeanDefinitionParser.scala
new file mode 100644
index 0000000000..e4b976188d
--- /dev/null
+++ b/akka-spring/src/main/scala/ActiveObjectBeanDefinitionParser.scala
@@ -0,0 +1,88 @@
+/**
+ * Copyright (C) 2009-2010 Scalable Solutions AB
+ */
+package se.scalablesolutions.akka.spring
+
+import org.springframework.util.xml.DomUtils
+import se.scalablesolutions.akka.util.Logging
+import org.w3c.dom.Element
+
+/**
+ * Parser for custom namespace configuration for active-object.
+ * @author michaelkober
+ */
+trait ActiveObjectBeanDefinitionParser extends Logging {
+ import AkkaSpringConfigurationTags._
+
+ /**
+ * Parses the given element and returns a ActiveObjectProperties.
+ * @param element dom element to parse
+ * @return configuration for the active object
+ */
+ def parseActiveObject(element: Element): ActiveObjectProperties = {
+ val objectProperties = new ActiveObjectProperties()
+ val remoteElement = DomUtils.getChildElementByTagName(element, REMOTE_TAG);
+ val callbacksElement = DomUtils.getChildElementByTagName(element, RESTART_CALLBACKS_TAG);
+
+ if (remoteElement != null) {
+ objectProperties.host = mandatory(remoteElement, HOST)
+ objectProperties.port = mandatory(remoteElement, PORT).toInt
+ }
+
+ if (callbacksElement != null) {
+ objectProperties.preRestart = callbacksElement.getAttribute(PRE_RESTART)
+ objectProperties.postRestart = callbacksElement.getAttribute(POST_RESTART)
+ if ((objectProperties.preRestart.isEmpty) && (objectProperties.preRestart.isEmpty)) {
+ throw new IllegalStateException("At least one of pre or post must be defined.")
+ }
+ }
+
+ try {
+ objectProperties.timeout = mandatory(element, TIMEOUT).toLong
+ } catch {
+ case nfe: NumberFormatException =>
+ log.error(nfe, "could not parse timeout %s", element.getAttribute(TIMEOUT))
+ throw nfe
+ }
+
+ objectProperties.target = mandatory(element, TARGET)
+ objectProperties.transactional = if (element.getAttribute(TRANSACTIONAL).isEmpty) false else element.getAttribute(TRANSACTIONAL).toBoolean
+
+ if (!element.getAttribute(INTERFACE).isEmpty) {
+ objectProperties.interface = element.getAttribute(INTERFACE)
+ }
+
+ if (!element.getAttribute(LIFECYCLE).isEmpty) {
+ objectProperties.lifecyclye = element.getAttribute(LIFECYCLE)
+ }
+ objectProperties
+ }
+
+ /**
+ * Get a mandatory element attribute.
+ * @param element the element with the mandatory attribute
+ * @param attribute name of the mandatory attribute
+ */
+ def mandatory(element: Element, attribute: String): String = {
+ if ((element.getAttribute(attribute) == null) || (element.getAttribute(attribute).isEmpty)) {
+ throw new IllegalArgumentException("Mandatory attribute missing: " + attribute)
+ } else {
+ element.getAttribute(attribute)
+ }
+ }
+
+ /**
+ * Get a mandatory child element.
+ * @param element the parent element
+ * @param childName name of the mandatory child element
+ */
+ def mandatoryElement(element: Element, childName: String): Element = {
+ val childElement = DomUtils.getChildElementByTagName(element, childName);
+ if (childElement == null) {
+ throw new IllegalArgumentException("Mandatory element missing: ''")
+ } else {
+ childElement
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/akka-spring/src/main/scala/ActiveObjectFactoryBean.scala b/akka-spring/src/main/scala/ActiveObjectFactoryBean.scala
new file mode 100644
index 0000000000..fb83c6fe09
--- /dev/null
+++ b/akka-spring/src/main/scala/ActiveObjectFactoryBean.scala
@@ -0,0 +1,81 @@
+/**
+ * Copyright (C) 2009-2010 Scalable Solutions AB
+ */
+
+package se.scalablesolutions.akka.spring
+
+import org.springframework.beans.factory.config.AbstractFactoryBean
+import se.scalablesolutions.akka.actor.ActiveObject
+import reflect.BeanProperty
+import se.scalablesolutions.akka.config.ScalaConfig.RestartCallbacks
+
+
+
+
+/**
+ * Factory bean for active objects.
+ * @author michaelkober
+ */
+class ActiveObjectFactoryBean extends AbstractFactoryBean[AnyRef] {
+ import StringReflect._
+
+ @BeanProperty var target: String = ""
+ @BeanProperty var timeout: Long = _
+ @BeanProperty var interface: String = ""
+ @BeanProperty var transactional: Boolean = false
+ @BeanProperty var pre: String = ""
+ @BeanProperty var post: String = ""
+ @BeanProperty var host: String = ""
+ @BeanProperty var port: Int = _
+ @BeanProperty var lifecycle: String = ""
+
+
+ /*
+ * @see org.springframework.beans.factory.FactoryBean#getObjectType()
+ */
+ def getObjectType: Class[AnyRef] = target.toClass
+
+
+ /*
+ * @see org.springframework.beans.factory.config.AbstractFactoryBean#createInstance()
+ */
+ def createInstance: AnyRef = {
+ ActiveObject.newInstance(target.toClass, timeout, transactional, restartCallbacks)
+ if (isRemote) {
+ newRemoteInstance(target, timeout, interface, transactional, restartCallbacks, host, port)
+ } else {
+ newInstance(target, timeout, interface, transactional, restartCallbacks);
+ }
+ }
+
+ private[akka] def isRemote = (host != null) && (!host.isEmpty)
+
+ /**
+ * create Option[RestartCallback]
+ */
+ private def restartCallbacks: Option[RestartCallbacks] = {
+ if (((pre == null) || pre.isEmpty) && ((post == null) || post.isEmpty)) {
+ None
+ } else {
+ val callbacks = new RestartCallbacks(pre, post)
+ Some(callbacks)
+ }
+ }
+
+ private def newInstance(target: String, timeout: Long, interface: String, transactional: Boolean, callbacks: Option[RestartCallbacks]): AnyRef = {
+ if ((interface == null) || interface.isEmpty) {
+ ActiveObject.newInstance(target.toClass, timeout, transactional, callbacks)
+ } else {
+ ActiveObject.newInstance(interface.toClass, target.toClass, timeout, transactional, callbacks)
+ }
+ }
+
+ private def newRemoteInstance(target: String, timeout: Long, interface: String, transactional: Boolean, callbacks: Option[RestartCallbacks], host: String, port: Int): AnyRef = {
+ if ((interface == null) || interface.isEmpty) {
+ ActiveObject.newRemoteInstance(target.toClass, timeout, transactional, host, port, callbacks)
+ } else {
+ ActiveObject.newRemoteInstance(interface.toClass, target.toClass, timeout, transactional, host, port, callbacks)
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/akka-spring/src/main/scala/ActiveObjectProperties.scala b/akka-spring/src/main/scala/ActiveObjectProperties.scala
new file mode 100644
index 0000000000..bd1f838d9b
--- /dev/null
+++ b/akka-spring/src/main/scala/ActiveObjectProperties.scala
@@ -0,0 +1,41 @@
+/**
+ * Copyright (C) 2009-2010 Scalable Solutions AB
+ */
+
+package se.scalablesolutions.akka.spring
+
+import org.springframework.beans.factory.support.BeanDefinitionBuilder
+import AkkaSpringConfigurationTags._
+
+/**
+ * Data container for active object configuration data.
+ * @author michaelkober
+ */
+class ActiveObjectProperties {
+ var target: String = ""
+ var timeout: Long = _
+ var interface: String = ""
+ var transactional: Boolean = false
+ var preRestart: String = ""
+ var postRestart: String = ""
+ var host: String = ""
+ var port: Int = _
+ var lifecyclye: String = ""
+
+ /**
+ * Sets the properties to the given builder.
+ * @param builder bean definition builder
+ */
+ def setAsProperties(builder: BeanDefinitionBuilder) {
+ builder.addPropertyValue(HOST, host)
+ builder.addPropertyValue(PORT, port)
+ builder.addPropertyValue(PRE_RESTART, preRestart)
+ builder.addPropertyValue(POST_RESTART, postRestart)
+ builder.addPropertyValue(TIMEOUT, timeout)
+ builder.addPropertyValue(TARGET, target)
+ builder.addPropertyValue(INTERFACE, interface)
+ builder.addPropertyValue(TRANSACTIONAL, transactional)
+ builder.addPropertyValue(LIFECYCLE, lifecyclye)
+ }
+
+}
\ No newline at end of file
diff --git a/akka-spring/src/main/scala/AkkaNamespaceHandler.scala b/akka-spring/src/main/scala/AkkaNamespaceHandler.scala
new file mode 100644
index 0000000000..ebf70a7ae3
--- /dev/null
+++ b/akka-spring/src/main/scala/AkkaNamespaceHandler.scala
@@ -0,0 +1,18 @@
+/**
+ * Copyright (C) 2009-2010 Scalable Solutions AB
+ */
+package se.scalablesolutions.akka.spring
+
+import org.springframework.beans.factory.xml.NamespaceHandlerSupport
+import AkkaSpringConfigurationTags._
+
+/**
+ * Custom spring namespace handler for Akka.
+ * @author michaelkober
+ */
+class AkkaNamespaceHandler extends NamespaceHandlerSupport {
+ def init = {
+ registerBeanDefinitionParser(ACTIVE_OBJECT_TAG, new AkkaObjectBeanDefinitionParser());
+ registerBeanDefinitionParser(SUPERVISION_TAG, new SupervisionBeanDefinitionParser());
+ }
+}
\ No newline at end of file
diff --git a/akka-spring/src/main/scala/AkkaObjectBeanDefinitionParser.scala b/akka-spring/src/main/scala/AkkaObjectBeanDefinitionParser.scala
new file mode 100644
index 0000000000..1f90c17454
--- /dev/null
+++ b/akka-spring/src/main/scala/AkkaObjectBeanDefinitionParser.scala
@@ -0,0 +1,30 @@
+/**
+ * Copyright (C) 2009-2010 Scalable Solutions AB
+ */
+package se.scalablesolutions.akka.spring
+
+import org.springframework.beans.factory.support.BeanDefinitionBuilder
+import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser
+import org.springframework.beans.factory.xml.ParserContext
+import org.w3c.dom.Element
+import se.scalablesolutions.akka.util.Logging
+
+
+/**
+ * Parser for custom namespace configuration.
+ * @author michaelkober
+ */
+class AkkaObjectBeanDefinitionParser extends AbstractSingleBeanDefinitionParser with ActiveObjectBeanDefinitionParser {
+ /*
+ * @see org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#doParse(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext, org.springframework.beans.factory.support.BeanDefinitionBuilder)
+ */
+ override def doParse(element: Element, parserContext: ParserContext, builder: BeanDefinitionBuilder) {
+ val activeObjectConf = parseActiveObject(element)
+ activeObjectConf.setAsProperties(builder)
+ }
+
+ /*
+ * @see org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#getBeanClass(org.w3c.dom.Element)
+ */
+ override def getBeanClass(element: Element) = classOf[ActiveObjectFactoryBean]
+}
\ No newline at end of file
diff --git a/akka-spring/src/main/scala/AkkaSpringConfigurationTags.scala b/akka-spring/src/main/scala/AkkaSpringConfigurationTags.scala
new file mode 100644
index 0000000000..058d654ea7
--- /dev/null
+++ b/akka-spring/src/main/scala/AkkaSpringConfigurationTags.scala
@@ -0,0 +1,41 @@
+/**
+ * Copyright (C) 2009-2010 Scalable Solutions AB
+ */
+package se.scalablesolutions.akka.spring
+
+/**
+ * XML configuration tags.
+ * @author michaelkober
+ */
+object AkkaSpringConfigurationTags {
+ // top level tags
+ val ACTIVE_OBJECT_TAG = "active-object"
+ val SUPERVISION_TAG = "supervision"
+ // active-object sub tags
+ val RESTART_CALLBACKS_TAG = "restart-callbacks"
+ val REMOTE_TAG = "remote";
+ // superivision sub tags
+ val ACTIVE_OBJECTS_TAG = "active-objects"
+ val STRATEGY_TAG = "restart-strategy"
+ val TRAP_EXISTS_TAG = "trap-exits"
+ val TRAP_EXIT_TAG = "trap-exit"
+ // active object attributes
+ val TIMEOUT = "timeout"
+ val TARGET = "target"
+ val INTERFACE = "interface"
+ val TRANSACTIONAL = "transactional"
+ val HOST = "host"
+ val PORT = "port"
+ val PRE_RESTART = "pre"
+ val POST_RESTART = "post"
+ val LIFECYCLE = "lifecycle"
+ // supervision attributes
+ val FAILOVER = "failover"
+ val RETRIES = "retries"
+ val TIME_RANGE = "timerange"
+ // Value types
+ val VAL_LIFECYCYLE_TEMPORARY = "temporary"
+ val VAL_LIFECYCYLE_PERMANENT = "permanent"
+ val VAL_ALL_FOR_ONE = "AllForOne"
+ val VAL_ONE_FOR_ONE = "OneForOne"
+}
\ No newline at end of file
diff --git a/akka-spring/src/main/scala/StringReflect.scala b/akka-spring/src/main/scala/StringReflect.scala
new file mode 100644
index 0000000000..7dda9dba08
--- /dev/null
+++ b/akka-spring/src/main/scala/StringReflect.scala
@@ -0,0 +1,24 @@
+/**
+ * Copyright (C) 2009-2010 Scalable Solutions AB
+ */
+package se.scalablesolutions.akka.spring
+
+object StringReflect {
+ /**
+ * Implicit conversion from String to StringReflect.
+ */
+ implicit def string2StringReflect(x: String) = new StringReflect(x)
+}
+
+/**
+ * Reflection helper class.
+ * @author michaelkober
+ */
+class StringReflect(val self: String) {
+ def toClass[T <: AnyRef]: Class[T] = {
+ val clazz = Class.forName(self)
+ clazz.asInstanceOf[Class[T]]
+ }
+}
+
+
diff --git a/akka-spring/src/main/scala/SupervisionBeanDefinitionParser.scala b/akka-spring/src/main/scala/SupervisionBeanDefinitionParser.scala
new file mode 100644
index 0000000000..7134675af1
--- /dev/null
+++ b/akka-spring/src/main/scala/SupervisionBeanDefinitionParser.scala
@@ -0,0 +1,65 @@
+/**
+ * Copyright (C) 2009-2010 Scalable Solutions AB
+ */
+package se.scalablesolutions.akka.spring
+
+import se.scalablesolutions.akka.util.Logging
+import org.springframework.beans.factory.support.BeanDefinitionBuilder
+import org.springframework.beans.factory.xml.{ParserContext, AbstractSingleBeanDefinitionParser}
+import se.scalablesolutions.akka.config.JavaConfig._
+import AkkaSpringConfigurationTags._
+
+
+import org.w3c.dom.Element
+import org.springframework.util.xml.DomUtils
+
+
+/**
+ * Parser for custom namespace for Akka declarative supervisor configuration.
+ * @author michaelkober
+ */
+class SupervisionBeanDefinitionParser extends AbstractSingleBeanDefinitionParser with ActiveObjectBeanDefinitionParser {
+ /* (non-Javadoc)
+ * @see org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#doParse(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext, org.springframework.beans.factory.support.BeanDefinitionBuilder)
+ */
+ override def doParse(element: Element, parserContext: ParserContext, builder: BeanDefinitionBuilder) {
+ parseSupervisor(element, builder)
+ }
+
+ /**
+ * made accessible for testing
+ */
+ private[akka] def parseSupervisor(element: Element, builder: BeanDefinitionBuilder) {
+ val strategyElement = mandatoryElement(element, STRATEGY_TAG);
+ val activeObjectsElement = mandatoryElement(element, ACTIVE_OBJECTS_TAG);
+ parseRestartStrategy(strategyElement, builder)
+ parseActiveObjectList(activeObjectsElement, builder)
+ }
+
+ private[akka] def parseRestartStrategy(element: Element, builder: BeanDefinitionBuilder) {
+ val failover = if (mandatory(element, FAILOVER) == "AllForOne") new AllForOne() else new OneForOne()
+ val timeRange = mandatory(element, TIME_RANGE).toInt
+ val retries = mandatory(element, RETRIES).toInt
+ val trapExitsElement = mandatoryElement(element, TRAP_EXISTS_TAG)
+ val trapExceptions = parseTrapExits(trapExitsElement)
+ val restartStrategy = new RestartStrategy(failover, retries, timeRange, trapExceptions)
+ builder.addPropertyValue("restartStrategy", restartStrategy)
+ }
+
+ private[akka] def parseActiveObjectList(element: Element, builder: BeanDefinitionBuilder) {
+ val activeObjects = DomUtils.getChildElementsByTagName(element, ACTIVE_OBJECT_TAG).toArray.toList.asInstanceOf[List[Element]]
+ val activeObjectProperties = activeObjects.map(parseActiveObject(_))
+ builder.addPropertyValue("supervised", activeObjectProperties)
+ }
+
+ private def parseTrapExits(element: Element): Array[Class[_ <: Throwable]] = {
+ import StringReflect._
+ val trapExits = DomUtils.getChildElementsByTagName(element, TRAP_EXIT_TAG).toArray.toList.asInstanceOf[List[Element]]
+ trapExits.map(DomUtils.getTextValue(_).toClass).toArray
+ }
+
+ /*
+ * @see org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#getBeanClass(org.w3c.dom.Element)
+ */
+ override def getBeanClass(element: Element) = classOf[SupervisionFactoryBean]
+}
\ No newline at end of file
diff --git a/akka-spring/src/main/scala/SupervisionFactoryBean.scala b/akka-spring/src/main/scala/SupervisionFactoryBean.scala
new file mode 100644
index 0000000000..7044445ba8
--- /dev/null
+++ b/akka-spring/src/main/scala/SupervisionFactoryBean.scala
@@ -0,0 +1,63 @@
+/**
+ * Copyright (C) 2009-2010 Scalable Solutions AB
+ */
+package se.scalablesolutions.akka.spring
+
+import org.springframework.beans.factory.config.AbstractFactoryBean
+import se.scalablesolutions.akka.config.ActiveObjectConfigurator
+import se.scalablesolutions.akka.config.JavaConfig._
+import AkkaSpringConfigurationTags._
+import reflect.BeanProperty
+
+
+/**
+ * Factory bean for supervisor configuration.
+ * @author michaelkober
+ */
+class SupervisionFactoryBean extends AbstractFactoryBean[ActiveObjectConfigurator] {
+ @BeanProperty var restartStrategy: RestartStrategy = _
+ @BeanProperty var supervised: List[ActiveObjectProperties] = _
+
+ /*
+ * @see org.springframework.beans.factory.FactoryBean#getObjectType()
+ */
+ def getObjectType: Class[ActiveObjectConfigurator] = classOf[ActiveObjectConfigurator]
+
+ /*
+ * @see org.springframework.beans.factory.config.AbstractFactoryBean#createInstance()
+ */
+ def createInstance: ActiveObjectConfigurator = {
+ val configurator = new ActiveObjectConfigurator()
+
+ configurator.configure(
+ restartStrategy,
+ supervised.map(createComponent(_)).toArray
+ ).supervise
+ }
+
+ /**
+ * Create configuration for ActiveObject
+ */
+ private[akka] def createComponent(props: ActiveObjectProperties): Component = {
+ import StringReflect._
+ val lifeCycle = if (!props.lifecyclye.isEmpty && props.lifecyclye.equalsIgnoreCase(VAL_LIFECYCYLE_TEMPORARY)) new LifeCycle(new Temporary()) else new LifeCycle(new Permanent())
+ val isRemote = (props.host != null) && (!props.host.isEmpty)
+ val withInterface = (props.interface != null) && (!props.interface.isEmpty)
+ // FIXME: timeout int vs long
+ val timeout = props.timeout.asInstanceOf[Int]
+ if (isRemote) {
+ val remote = new RemoteAddress(props.host, props.port)
+ if (withInterface) {
+ new Component(props.interface.toClass, props.target.toClass, lifeCycle, timeout, props.transactional, remote)
+ } else {
+ new Component(props.target.toClass, lifeCycle, timeout, props.transactional, remote)
+ }
+ } else {
+ if (withInterface) {
+ new Component(props.interface.toClass, props.target.toClass, lifeCycle, timeout, props.transactional)
+ } else {
+ new Component(props.target.toClass, lifeCycle, timeout, props.transactional)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/akka-spring/src/test/scala/ActiveObjectBeanDefinitionParserTest.scala b/akka-spring/src/test/scala/ActiveObjectBeanDefinitionParserTest.scala
new file mode 100644
index 0000000000..a445d92cba
--- /dev/null
+++ b/akka-spring/src/test/scala/ActiveObjectBeanDefinitionParserTest.scala
@@ -0,0 +1,51 @@
+/**
+ * Copyright (C) 2009-2010 Scalable Solutions AB
+ */
+package se.scalablesolutions.akka.spring
+
+import org.scalatest.Spec
+import org.scalatest.matchers.ShouldMatchers
+import org.scalatest.junit.JUnitRunner
+import org.junit.runner.RunWith
+import ScalaDom._
+
+import org.w3c.dom.Element
+
+/**
+ * Test for ActiveObjectBeanDefinitionParser
+ * @author michaelkober
+ */
+@RunWith(classOf[JUnitRunner])
+class ActiveObjectBeanDefinitionParserTest extends Spec with ShouldMatchers {
+ private class Parser extends ActiveObjectBeanDefinitionParser
+
+ describe("An ActiveObjectBeanDefinitionParser") {
+ val parser = new Parser()
+ it("should parse the active object configuration") {
+ val props = parser.parseActiveObject(createTestElement);
+ assert(props != null)
+ assert(props.timeout == 1000)
+ assert(props.target == "foo.bar.MyPojo")
+ assert(props.transactional)
+ }
+
+ it("should throw IllegalArgumentException on missing mandatory attributes") {
+ evaluating { parser.parseActiveObject(createTestElement2) } should produce [IllegalArgumentException]
+ }
+ }
+
+ private def createTestElement : Element = {
+ val xml =
+ dom(xml).getDocumentElement
+ }
+
+ private def createTestElement2 : Element = {
+ val xml =
+ dom(xml).getDocumentElement
+ }
+}
\ No newline at end of file
diff --git a/akka-spring/src/test/scala/ActiveObjectFactoryBeanTest.scala b/akka-spring/src/test/scala/ActiveObjectFactoryBeanTest.scala
new file mode 100644
index 0000000000..e0eec060bf
--- /dev/null
+++ b/akka-spring/src/test/scala/ActiveObjectFactoryBeanTest.scala
@@ -0,0 +1,41 @@
+/**
+ * Copyright (C) 2009-2010 Scalable Solutions AB
+ */
+package se.scalablesolutions.akka.spring
+
+import org.scalatest.Spec
+import org.scalatest.matchers.ShouldMatchers
+import org.scalatest.junit.JUnitRunner
+import org.junit.runner.RunWith
+
+/**
+ * Test for ActiveObjectFactoryBean
+ * @author michaelkober
+ */
+@RunWith(classOf[JUnitRunner])
+class ActiveObjectFactoryBeanTest extends Spec with ShouldMatchers {
+
+ describe("A ActiveObjectFactoryBean") {
+ val bean = new ActiveObjectFactoryBean
+ it("should have java getters and setters for all properties") {
+ bean.setTarget("java.lang.String")
+ assert(bean.getTarget == "java.lang.String")
+ bean.setTimeout(1000)
+ assert(bean.getTimeout == 1000)
+ }
+
+ it("should create a remote active object when a host is set") {
+ bean.setHost("some.host.com");
+ assert(bean.isRemote)
+ }
+
+ it("should return the object type") {
+ bean.setTarget("java.lang.String")
+ assert(bean.getObjectType == classOf[String])
+ }
+
+ it("should create an active object") {
+ // TODO:
+ }
+ }
+}
diff --git a/akka-spring/src/test/scala/ScalaDom.scala b/akka-spring/src/test/scala/ScalaDom.scala
new file mode 100644
index 0000000000..70531861e1
--- /dev/null
+++ b/akka-spring/src/test/scala/ScalaDom.scala
@@ -0,0 +1,40 @@
+package se.scalablesolutions.akka.spring
+/**
+ * from http://stackoverflow.com/questions/2002685/any-conversion-from-scalas-xml-to-w3c-dom
+ */
+
+object ScalaDom {
+ import scala.xml._
+ import org.w3c.dom.{Document => JDocument, Node => JNode}
+ import javax.xml.parsers.DocumentBuilderFactory
+
+ def dom(n: Node): JDocument = {
+
+ val doc = DocumentBuilderFactory
+ .newInstance
+ .newDocumentBuilder
+ .getDOMImplementation
+ .createDocument(null, null, null)
+
+ def build(node: Node, parent: JNode): Unit = {
+ val jnode: JNode = node match {
+ case e: Elem => {
+ val jn = doc.createElement(e.label)
+ e.attributes foreach { a => jn.setAttribute(a.key, a.value.mkString) }
+ jn
+ }
+ case a: Atom[_] => doc.createTextNode(a.text)
+ case c: Comment => doc.createComment(c.commentText)
+ case er: EntityRef => doc.createEntityReference(er.entityName)
+ case pi: ProcInstr => doc.createProcessingInstruction(pi.target, pi.proctext)
+ }
+ parent.appendChild(jnode)
+ node.child.map { build(_, jnode) }
+ }
+
+ build(n, doc)
+ doc
+
+ }
+}
+
\ No newline at end of file
diff --git a/akka-spring/src/test/scala/SupervisionBeanDefinitionParserTest.scala b/akka-spring/src/test/scala/SupervisionBeanDefinitionParserTest.scala
new file mode 100644
index 0000000000..e5677113af
--- /dev/null
+++ b/akka-spring/src/test/scala/SupervisionBeanDefinitionParserTest.scala
@@ -0,0 +1,122 @@
+/**
+ * Copyright (C) 2009-2010 Scalable Solutions AB
+ */
+package se.scalablesolutions.akka.spring
+
+import org.scalatest.Spec
+import org.scalatest.matchers.ShouldMatchers
+import org.scalatest.junit.JUnitRunner
+import org.junit.runner.RunWith
+import ScalaDom._
+
+import se.scalablesolutions.akka.config.JavaConfig._
+
+import org.w3c.dom.Element
+import org.springframework.beans.factory.support.BeanDefinitionBuilder
+
+/**
+ * Test for SupervisionBeanDefinitionParser
+ * @author michaelkober
+ */
+@RunWith(classOf[JUnitRunner])
+class SupervisionBeanDefinitionParserTest extends Spec with ShouldMatchers {
+ private class Parser extends SupervisionBeanDefinitionParser
+
+ describe("A SupervisionBeanDefinitionParser") {
+ val parser = new Parser()
+ val builder = BeanDefinitionBuilder.genericBeanDefinition("foo.bar.Foo")
+
+ it("should be able to parse active object configuration") {
+ val props = parser.parseActiveObject(createActiveObjectElement);
+ assert(props != null)
+ assert(props.timeout == 1000)
+ assert(props.target == "foo.bar.MyPojo")
+ assert(props.transactional)
+ }
+
+ it("should parse the supervisor restart strategy") {
+ parser.parseSupervisor(createSupervisorElement, builder);
+ val strategy = builder.getBeanDefinition.getPropertyValues.getPropertyValue("restartStrategy").getValue.asInstanceOf[RestartStrategy]
+ assert(strategy != null)
+ assert(strategy.scheme match {
+ case x:AllForOne => true
+ case _ => false })
+ expect(3) { strategy.maxNrOfRetries }
+ expect(1000) { strategy.withinTimeRange }
+ }
+
+ it("should parse the supervised active objects") {
+ parser.parseSupervisor(createSupervisorElement, builder);
+ val supervised = builder.getBeanDefinition.getPropertyValues.getPropertyValue("supervised").getValue.asInstanceOf[List[ActiveObjectProperties]]
+ assert(supervised != null)
+ expect(3) { supervised.length }
+ val iterator = supervised.elements
+ expect("foo.bar.Foo") { iterator.next.target }
+ expect("foo.bar.Bar") { iterator.next.target }
+ expect("foo.bar.MyPojo") { iterator.next.target }
+ }
+
+ it("should throw IllegalArgumentException on missing mandatory attributes") {
+ evaluating { parser.parseSupervisor(createSupervisorMissingAttribute, builder) } should produce [IllegalArgumentException]
+ }
+
+ it("should throw IllegalArgumentException on missing mandatory elements") {
+ evaluating { parser.parseSupervisor(createSupervisorMissingElement, builder) } should produce [IllegalArgumentException]
+ }
+ }
+
+ private def createActiveObjectElement : Element = {
+ val xml =
+ dom(xml).getDocumentElement
+ }
+
+ private def createSupervisorElement : Element = {
+ val xml =
+
+
+ java.io.IOException
+ java.lang.NullPointerException
+
+
+
+
+
+
+
+
+
+
+ dom(xml).getDocumentElement
+ }
+
+
+ private def createSupervisorMissingAttribute : Element = {
+ val xml =
+
+
+ java.io.IOException
+
+
+
+
+
+
+ dom(xml).getDocumentElement
+ }
+
+ private def createSupervisorMissingElement : Element = {
+ val xml =
+
+
+
+
+
+
+
+ dom(xml).getDocumentElement
+ }
+}
+
diff --git a/akka-spring/src/test/scala/SupervisionFactoryBeanTest.scala b/akka-spring/src/test/scala/SupervisionFactoryBeanTest.scala
new file mode 100644
index 0000000000..dbb0798c9d
--- /dev/null
+++ b/akka-spring/src/test/scala/SupervisionFactoryBeanTest.scala
@@ -0,0 +1,41 @@
+/**
+ * Copyright (C) 2009-2010 Scalable Solutions AB
+ */
+package se.scalablesolutions.akka.spring
+
+import org.scalatest.Spec
+import org.scalatest.matchers.ShouldMatchers
+import org.scalatest.junit.JUnitRunner
+import org.junit.runner.RunWith
+import se.scalablesolutions.akka.config.JavaConfig._
+import se.scalablesolutions.akka.config.ActiveObjectConfigurator
+
+private[akka] class Foo
+
+@RunWith(classOf[JUnitRunner])
+class SupervisionFactoryBeanTest extends Spec with ShouldMatchers {
+
+ val restartStrategy = new RestartStrategy(new AllForOne(), 3, 1000, Array(classOf[Throwable]))
+ val activeObjects = List(createActiveObjectProperties("se.scalablesolutions.akka.spring.Foo", 1000L))
+
+ def createActiveObjectProperties(target: String, timeout: Long) : ActiveObjectProperties = {
+ val properties = new ActiveObjectProperties()
+ properties.target = target
+ properties.timeout = timeout
+ properties
+ }
+
+ describe("A SupervisionFactoryBean") {
+ val bean = new SupervisionFactoryBean
+ it("should have java getters and setters for all properties") {
+ bean.setRestartStrategy(restartStrategy)
+ assert(bean.getRestartStrategy == restartStrategy)
+ bean.setSupervised(activeObjects)
+ assert(bean.getSupervised == activeObjects)
+ }
+
+ it("should return the object type ActiveObjectConfigurator") {
+ assert(bean.getObjectType == classOf[ActiveObjectConfigurator])
+ }
+ }
+}
\ No newline at end of file
diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala
index a891ce1668..158217031c 100644
--- a/project/build/AkkaProject.scala
+++ b/project/build/AkkaProject.scala
@@ -85,8 +85,9 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) {
lazy val akka_security = project("akka-security", "akka-security", new AkkaSecurityProject(_), akka_core)
lazy val akka_persistence = project("akka-persistence", "akka-persistence", new AkkaPersistenceParentProject(_))
lazy val akka_cluster = project("akka-cluster", "akka-cluster", new AkkaClusterParentProject(_))
+ lazy val akka_spring = project("akka-spring", "akka-spring", new AkkaSpringProject(_), akka_core)
lazy val akka_kernel = project("akka-kernel", "akka-kernel", new AkkaKernelProject(_),
- akka_core, akka_rest, akka_persistence, akka_cluster, akka_amqp, akka_security, akka_comet, akka_camel, akka_patterns)
+ akka_core, akka_rest, akka_spring, akka_persistence, akka_cluster, akka_amqp, akka_security, akka_comet, akka_patterns)
// functional tests in java
lazy val akka_fun_test = project("akka-fun-test-java", "akka-fun-test-java", new AkkaFunTestProject(_), akka_kernel)
@@ -122,7 +123,8 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) {
" dist/akka-persistence-redis_%s-%s.jar".format(defScalaVersion.value, version) +
" dist/akka-persistence-mongo_%s-%s.jar".format(defScalaVersion.value, version) +
" dist/akka-persistence-cassandra_%s-%s.jar".format(defScalaVersion.value, version) +
- " dist/akka-kernel_%s-%s.jar".format(defScalaVersion.value, version)
+ " dist/akka-kernel_%s-%s.jar".format(defScalaVersion.value, version) +
+ " dist/akka-spring_%s-%s.jar".format(defScalaVersion.value, version)
)
// ------------------------------------------------------------
@@ -288,6 +290,15 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) {
lazy val akka_cluster_shoal = project("akka-cluster-shoal", "akka-cluster-shoal", new AkkaShoalProject(_), akka_core)
}
+ class AkkaSpringProject(info: ProjectInfo) extends DefaultProject(info) {
+ val spring_beans = "org.springframework" % "spring-beans" % "3.0.1.RELEASE"
+ val spring_context = "org.springframework" % "spring-context" % "3.0.1.RELEASE"
+ // testing
+ val scalatest = "org.scalatest" % "scalatest" % "1.0" % "test"
+ val junit = "junit" % "junit" % "4.5" % "test"
+ lazy val dist = deployTask(info, distPath) dependsOn(`package`) describedAs("Deploying")
+ }
+
class AkkaKernelProject(info: ProjectInfo) extends DefaultProject(info) {
lazy val dist = deployTask(info, distPath) dependsOn(`package`) describedAs("Deploying")
}