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") }