diff --git a/akka-spring/pom.xml b/akka-spring/pom.xml
index 5a2cd11eac..126be1c991 100644
--- a/akka-spring/pom.xml
+++ b/akka-spring/pom.xml
@@ -1,77 +1,76 @@
-
- 4.0.0
+ 4.0.0
+ akka-spring
+ Akka Spring Integration
+ jar
- akka-spring
- Akka Spring Module
-
- jar
-
-
- akka
- se.scalablesolutions.akka
- 0.6
- ../pom.xml
-
+
+ akka
+ se.scalablesolutions.akka
+ 0.7-SNAPSHOT
+
+
+
- junit
- junit
- 3.8.1
- test
+ akka-core
+ ${project.groupId}
+ ${project.version}
+
+ akka-util-java
+ ${project.groupId}
+ ${project.version}
+
+
+ akka-util
+ ${project.groupId}
+ ${project.version}
+
+
org.springframework
spring
- 2.5.5
+ 2.5.6
-
- akka-core
- se.scalablesolutions.akka
- 0.6
+
org.scala-lang
scala-library
- 2.7.5
+ ${scala.version}
+
+
+ org.scala-lang
+ scala-compiler
+ ${scala.version}
+
+
+
+ org.scalatest
+ scalatest
+ 1.0
+ test
- org.codehaus.aspectwerkz
- aspectwerkz-nodeps-jdk5
- 2.1
-
-
- org.codehaus.aspectwerkz
- aspectwerkz-jdk5
- 2.1
+ junit
+ junit
+ 4.5
+ test
-
-
- src/main/java
- src/test/java
-
-
- src/test/resources
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
-
- 1.5
- 1.5
-
- **/*
-
-
-
-
-
+
diff --git a/akka-spring/src/main/java/se/scalablesolutions/akka/spring/AkkaSpringInterceptor.java b/akka-spring/src/main/java/se/scalablesolutions/akka/spring/AkkaSpringInterceptor.java
deleted file mode 100644
index 456dd15572..0000000000
--- a/akka-spring/src/main/java/se/scalablesolutions/akka/spring/AkkaSpringInterceptor.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/**
- * Copyright (C) 2009-2010 Scalable Solutions AB
- */
-
-package se.scalablesolutions.akka.spring;
-
-import se.scalablesolutions.akka.actor.ActiveObjectAspect;
-import se.scalablesolutions.akka.actor.AspectInit;
-import se.scalablesolutions.akka.actor.AspectInitRegistry;
-import se.scalablesolutions.akka.actor.Dispatcher;
-
-import org.springframework.beans.factory.config.BeanPostProcessor;
-
-import org.aopalliance.intercept.MethodInterceptor;
-import org.aopalliance.intercept.MethodInvocation;
-import org.springframework.beans.BeansException;
-import org.springframework.beans.factory.config.BeanPostProcessor;
-
-import java.lang.reflect.Method;
-
-public class AkkaSpringInterceptor extends ActiveObjectAspect implements MethodInterceptor, BeanPostProcessor {
-
- static final String TRANSACTION_MANAGER_CLASS_NAME = "org.springframework.transaction.support.TransactionSynchronizationManager";
- static final String IS_TRANSACTION_ALIVE_METHOD_NAME = "isActualTransactionActive";
-
- static private Method IS_TRANSACTION_ALIVE_METHOD = null;
-
- static {
- try {
- ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
- Class clazz = null;
- if (contextClassLoader != null) {
- clazz = contextClassLoader.loadClass(TRANSACTION_MANAGER_CLASS_NAME);
- } else {
- ClassLoader springClassLoader = AkkaSpringInterceptor.class.getClassLoader();
- clazz = springClassLoader.loadClass(TRANSACTION_MANAGER_CLASS_NAME);
- }
- if (clazz != null) IS_TRANSACTION_ALIVE_METHOD = clazz.getDeclaredMethod(IS_TRANSACTION_ALIVE_METHOD_NAME, null);
- } catch (Exception e) {
- }
- }
-
- // FIXME make configurable
- static final int TIME_OUT = 1000;
-
- @Override
- public Object invoke(MethodInvocation methodInvocation) throws Throwable {
- Dispatcher dispatcher = new Dispatcher(isTransactional());
- dispatcher.start();
- try {
- AspectInitRegistry.register(methodInvocation.getThis(), new AspectInit(
- methodInvocation.getThis().getClass(),
- dispatcher,
- TIME_OUT));
- return this.invoke(AkkaSpringJoinPointWrapper.createSpringAkkaAspectWerkzWrapper(methodInvocation));
- } finally {
- dispatcher.stop();
- }
- }
-
- @Override
- public Object postProcessAfterInitialization(Object bean, String arg) throws BeansException {
- return bean;
- }
-
- @Override
- public Object postProcessBeforeInitialization(Object bean, String arg) throws BeansException {
- return bean;
- }
-
- /**
- * Checks if intercepted Spring bean is in a transaction.
- */
- private boolean isTransactional() {
- try {
- return (Boolean) IS_TRANSACTION_ALIVE_METHOD.invoke(null, null);
- } catch (Exception e) {
- throw new RuntimeException("Could not check if the Spring bean is executing within a transaction", e);
- }
- }
-}
\ No newline at end of file
diff --git a/akka-spring/src/main/java/se/scalablesolutions/akka/spring/AkkaSpringJoinPointWrapper.java b/akka-spring/src/main/java/se/scalablesolutions/akka/spring/AkkaSpringJoinPointWrapper.java
deleted file mode 100644
index 569bb4511d..0000000000
--- a/akka-spring/src/main/java/se/scalablesolutions/akka/spring/AkkaSpringJoinPointWrapper.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/**
- * Copyright (C) 2009-2010 Scalable Solutions AB
- */
-
-package se.scalablesolutions.akka.spring;
-
-import org.aopalliance.intercept.MethodInvocation;
-
-import org.codehaus.aspectwerkz.joinpoint.*;
-import org.codehaus.aspectwerkz.joinpoint.management.JoinPointType;
-
-public class AkkaSpringJoinPointWrapper implements JoinPoint {
-
- private MethodInvocation methodInvocation = null;
-
- public static AkkaSpringJoinPointWrapper createSpringAkkaAspectWerkzWrapper(MethodInvocation methodInvocation) {
- AkkaSpringJoinPointWrapper joinPointWrapper = new AkkaSpringJoinPointWrapper();
- joinPointWrapper.setMethodInvocation(methodInvocation);
- return joinPointWrapper;
- }
-
- public MethodInvocation getMethodInvocation() {
- return methodInvocation;
- }
-
- public void setMethodInvocation(MethodInvocation methodInvocation) {
- this.methodInvocation = methodInvocation;
- }
-
- public Object proceed() throws Throwable {
- return methodInvocation.proceed();
- }
-
- public Rtti getRtti() {
- return new AkkaSpringRttiWrapper(methodInvocation);
- }
-
- public Object getTarget() {
- return methodInvocation.getThis();
- }
-
- public Object getThis() {
- return methodInvocation.getThis();
- }
-
- public Object getCallee() {
- throw new UnsupportedOperationException();
- }
-
- public Object getCaller() {
- throw new UnsupportedOperationException();
- }
-
- public void addMetaData(Object arg0, Object arg1) {
- throw new UnsupportedOperationException();
- }
-
- public Class getCalleeClass() {
- throw new UnsupportedOperationException();
- }
-
- public Class getCallerClass() {
- throw new UnsupportedOperationException();
- }
-
- public EnclosingStaticJoinPoint getEnclosingStaticJoinPoint() {
- throw new UnsupportedOperationException();
- }
-
- public Object getMetaData(Object arg0) {
- throw new UnsupportedOperationException();
- }
-
- public Signature getSignature() {
- throw new UnsupportedOperationException();
- }
-
- public Class getTargetClass() {
- throw new UnsupportedOperationException();
- }
-
- public JoinPointType getType() {
- throw new UnsupportedOperationException();
- }
-
- public StaticJoinPoint copy() {
- throw new UnsupportedOperationException();
- }
-}
diff --git a/akka-spring/src/main/java/se/scalablesolutions/akka/spring/AkkaSpringRttiWrapper.java b/akka-spring/src/main/java/se/scalablesolutions/akka/spring/AkkaSpringRttiWrapper.java
deleted file mode 100644
index 35f07065b6..0000000000
--- a/akka-spring/src/main/java/se/scalablesolutions/akka/spring/AkkaSpringRttiWrapper.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/**
- * Copyright (C) 2009-2010 Scalable Solutions AB
- */
-
-package se.scalablesolutions.akka.spring;
-
-import org.aopalliance.intercept.MethodInvocation;
-
-import org.codehaus.aspectwerkz.joinpoint.MethodRtti;
-import org.codehaus.aspectwerkz.joinpoint.Rtti;
-
-import java.lang.reflect.Method;
-
-public class AkkaSpringRttiWrapper implements MethodRtti {
-
- private MethodInvocation methodInvocation = null;
-
- public AkkaSpringRttiWrapper(MethodInvocation methodInvocation) {
- this.methodInvocation = methodInvocation;
- }
-
- public Method getMethod() {
- return methodInvocation.getMethod();
- }
-
- public Class getReturnType() {
- throw new UnsupportedOperationException();
- }
-
- public Object getReturnValue() {
- throw new UnsupportedOperationException();
- }
-
- public Class[] getExceptionTypes() {
- throw new UnsupportedOperationException();
- }
-
- public Class[] getParameterTypes() {
- throw new UnsupportedOperationException();
- }
-
- public Object[] getParameterValues() {
- throw new UnsupportedOperationException();
- }
-
- public void setParameterValues(Object[] arg0) {
- throw new UnsupportedOperationException();
- }
-
- public Rtti cloneFor(Object arg0, Object arg1) {
- throw new UnsupportedOperationException();
- }
-
- public Class getDeclaringType() {
- throw new UnsupportedOperationException();
- }
-
- public int getModifiers() {
- throw new UnsupportedOperationException();
- }
-
- public String getName() {
- throw new UnsupportedOperationException();
- }
-
- public Object getTarget() {
- throw new UnsupportedOperationException();
- }
-
- public Object getThis() {
- throw new UnsupportedOperationException();
- }
-}
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..3d4a4508de
--- /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 {
+ 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 = 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..f02ed6c283
--- /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 {
+ @BeanProperty var restartStrategy: RestartStrategy = _
+ @BeanProperty var supervised: List[ActiveObjectProperties] = _
+
+ /*
+ * @see org.springframework.beans.factory.FactoryBean#getObjectType()
+ */
+ def getObjectType = classOf[ActiveObjectConfigurator]
+
+ /*
+ * @see org.springframework.beans.factory.config.AbstractFactoryBean#createInstance()
+ */
+ def createInstance: AnyRef = {
+ 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/java/se/scalablesolutions/akka/spring/AkkaSpringInterceptorTest.java b/akka-spring/src/test/java/se/scalablesolutions/akka/spring/AkkaSpringInterceptorTest.java
deleted file mode 100644
index 73c08adeca..0000000000
--- a/akka-spring/src/test/java/se/scalablesolutions/akka/spring/AkkaSpringInterceptorTest.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package se.scalablesolutions.akka.spring;
-
-import junit.framework.Test;
-import junit.framework.TestCase;
-import junit.framework.TestSuite;
-
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.support.ClassPathXmlApplicationContext;
-
-public class AkkaSpringInterceptorTest extends TestCase {
-
- public AkkaSpringInterceptorTest(String testName) {
- super(testName);
- }
-
- public static Test suite() {
- return new TestSuite(AkkaSpringInterceptorTest.class);
- }
-
- public void testInvokingAkkaEnabledSpringBeanMethodWithReturnValue() {
- ApplicationContext context = new ClassPathXmlApplicationContext("spring-test-config.xml");
- MyService myService = (MyService) context.getBean("actorBeanService");
- Object obj = myService.getNumbers(12);
- assertEquals(new Integer(12), obj);
- }
-
- public void testThatAkkaEnabledSpringBeanMethodCallIsInvokedInADifferentThreadThanTheTest() {
- ApplicationContext context = new ClassPathXmlApplicationContext("spring-test-config.xml");
- MyService myService = (MyService) context.getBean("actorBeanService");
- assertNotSame(Thread.currentThread().getName(), myService.getThreadName());
- }
-
- public void testInvokingAkkaEnabledSpringBeanMethodWithTransactionalMethod() {
- ApplicationContext context = new ClassPathXmlApplicationContext("spring-test-config.xml");
- MyService myService = (MyService) context.getBean("actorBeanService");
- myService.init();
- myService.setMapState("key", "value");
- myService.setRefState("value");
- myService.setVectorState("value");
- assertEquals("value", myService.getMapState("key"));
- assertEquals("value", myService.getRefState());
- assertEquals("value", myService.getVectorState());
- }
-}
diff --git a/akka-spring/src/test/java/se/scalablesolutions/akka/spring/MyService.java b/akka-spring/src/test/java/se/scalablesolutions/akka/spring/MyService.java
deleted file mode 100644
index 117e282af5..0000000000
--- a/akka-spring/src/test/java/se/scalablesolutions/akka/spring/MyService.java
+++ /dev/null
@@ -1,79 +0,0 @@
-package se.scalablesolutions.akka.spring;
-
-import org.springframework.transaction.annotation.Transactional;
-
-import se.scalablesolutions.akka.state.TransactionalState;
-import se.scalablesolutions.akka.state.TransactionalRef;
-import se.scalablesolutions.akka.state.TransactionalVector;
-import se.scalablesolutions.akka.state.TransactionalMap;
-
-import se.scalablesolutions.akka.annotation.oneway;
-import se.scalablesolutions.akka.annotation.prerestart;
-import se.scalablesolutions.akka.annotation.postrestart;
-
-public class MyService {
-
- private TransactionalMap mapState;
- private TransactionalVector vectorState;
- private TransactionalRef refState;
- private boolean isInitialized = false;
-
- @Transactional
- public void init() {
- if (!isInitialized) {
- mapState = TransactionalState.newMap();
- vectorState = TransactionalState.newVector();
- refState = TransactionalState.newRef();
- isInitialized = true;
- }
- }
-
- public String getThreadName() {
- return Thread.currentThread().getName();
- }
-
- public Integer getNumbers(int aTestNumber) {
- return new Integer(aTestNumber);
- }
-
- @Transactional
- public String getMapState(String key) {
- return (String)mapState.get(key).get();
- }
-
- @Transactional
- public String getVectorState() {
- return (String)vectorState.last();
- }
-
- @Transactional
- public String getRefState() {
- return (String)refState.get().get();
- }
-
- @Transactional
- public void setMapState(String key, String msg) {
- mapState.put(key, msg);
- }
-
- @Transactional
- public void setVectorState(String msg) {
- vectorState.add(msg);
- }
-
- @Transactional
- public void setRefState(String msg) {
- refState.swap(msg);
- }
-
- @prerestart
- public void preRestart() {
- System.out.println("################ PRE RESTART");
- }
-
- @postrestart
- public void postRestart() {
- System.out.println("################ POST RESTART");
- }
-}
-
diff --git a/akka-spring/src/test/resources/spring-test-config.xml b/akka-spring/src/test/resources/spring-test-config.xml
deleted file mode 100644
index dadbcf56d1..0000000000
--- a/akka-spring/src/test/resources/spring-test-config.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- akkaInterceptor
-
-
-
-
-
\ No newline at end of file
diff --git a/akka-spring/src/test/resources/test-config.xml b/akka-spring/src/test/resources/test-config.xml
new file mode 100644
index 0000000000..d3a8077945
--- /dev/null
+++ b/akka-spring/src/test/resources/test-config.xml
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ java.io.IOException
+ java.lang.NullPointerException
+
+
+
+
+
+
+
+
+
+
+
+
+
\ 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