initial version of spring custom namespace
This commit is contained in:
parent
4afd6a9f54
commit
9533b54fbf
25 changed files with 1028 additions and 445 deletions
|
|
@ -1,77 +1,76 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>akka-spring</artifactId>
|
||||
<name>Akka Spring Integration</name>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<artifactId>akka-spring</artifactId>
|
||||
<name>Akka Spring Module</name>
|
||||
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<parent>
|
||||
<artifactId>akka</artifactId>
|
||||
<groupId>se.scalablesolutions.akka</groupId>
|
||||
<version>0.6</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<parent>
|
||||
<artifactId>akka</artifactId>
|
||||
<groupId>se.scalablesolutions.akka</groupId>
|
||||
<version>0.7-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
|
||||
|
||||
<dependencies>
|
||||
<!-- Core deps -->
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<scope>test</scope>
|
||||
<artifactId>akka-core</artifactId>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>akka-util-java</artifactId>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>akka-util</artifactId>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring</artifactId>
|
||||
<version>2.5.5</version>
|
||||
<version>2.5.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>akka-core</artifactId>
|
||||
<groupId>se.scalablesolutions.akka</groupId>
|
||||
<version>0.6</version>
|
||||
<!--dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
<version>3.0.1.RELEASE</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-beans</artifactId>
|
||||
<version>3.0.1.RELEASE</version>
|
||||
</dependency-->
|
||||
<dependency>
|
||||
<groupId>org.scala-lang</groupId>
|
||||
<artifactId>scala-library</artifactId>
|
||||
<version>2.7.5</version>
|
||||
<version>${scala.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.scala-lang</groupId>
|
||||
<artifactId>scala-compiler</artifactId>
|
||||
<version>${scala.version}</version>
|
||||
</dependency>
|
||||
<!-- For Testing -->
|
||||
<dependency>
|
||||
<groupId>org.scalatest</groupId>
|
||||
<artifactId>scalatest</artifactId>
|
||||
<version>1.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.aspectwerkz</groupId>
|
||||
<artifactId>aspectwerkz-nodeps-jdk5</artifactId>
|
||||
<version>2.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.aspectwerkz</groupId>
|
||||
<artifactId>aspectwerkz-jdk5</artifactId>
|
||||
<version>2.1</version>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<sourceDirectory>src/main/java</sourceDirectory>
|
||||
<testSourceDirectory>src/test/java</testSourceDirectory>
|
||||
<testResources>
|
||||
<testResource>
|
||||
<directory>src/test/resources</directory>
|
||||
</testResource>
|
||||
</testResources>
|
||||
|
||||
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.5</source>
|
||||
<target>1.5</target>
|
||||
<includes>
|
||||
<include>**/*</include>
|
||||
</includes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
|
|
|||
|
|
@ -1,81 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
1
akka-spring/src/main/resources/META-INF/spring.handlers
Normal file
1
akka-spring/src/main/resources/META-INF/spring.handlers
Normal file
|
|
@ -0,0 +1 @@
|
|||
http\://www.akkasource.org/schema/akka=se.scalablesolutions.akka.spring.AkkaNamespaceHandler
|
||||
1
akka-spring/src/main/resources/META-INF/spring.schemas
Normal file
1
akka-spring/src/main/resources/META-INF/spring.schemas
Normal file
|
|
@ -0,0 +1 @@
|
|||
http\://www.akkasource.org/schema/akka=se/scalablesolutions/akka/spring/akka.xsd
|
||||
|
|
@ -0,0 +1,163 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xsd:schema xmlns="http://www.akkasource.org/schema/akka"
|
||||
targetNamespace="http://www.akkasource.org/schema/akka"
|
||||
elementFormDefault="qualified" attributeFormDefault="unqualified"
|
||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||
|
||||
<!-- base types -->
|
||||
|
||||
<!-- restart strategies enumeration -->
|
||||
<xsd:simpleType name="failover-type">
|
||||
<xsd:restriction base="xsd:token">
|
||||
<xsd:enumeration value="AllForOne"/>
|
||||
<xsd:enumeration value="OneForOne"/>
|
||||
</xsd:restriction>
|
||||
</xsd:simpleType>
|
||||
|
||||
<!-- restart strategies enumeration -->
|
||||
<xsd:simpleType name="lifecycle-type">
|
||||
<xsd:restriction base="xsd:token">
|
||||
<xsd:enumeration value="permanent"/>
|
||||
<xsd:enumeration value="temporary"/>
|
||||
</xsd:restriction>
|
||||
</xsd:simpleType>
|
||||
|
||||
|
||||
<!-- Remote -->
|
||||
<xsd:complexType name="remote-type">
|
||||
<xsd:attribute name="host" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Name of the remote host.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="port" type="xsd:integer" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Port of the remote host.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<!-- callbacks -->
|
||||
<xsd:complexType name="restart-callbacks-type">
|
||||
<xsd:attribute name="pre" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Pre restart callback method that is called during restart.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="post" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Post restart callback method that is called during restart.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<!-- active object -->
|
||||
<xsd:complexType name="active-object-type">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="restart-callbacks" type="restart-callbacks-type" minOccurs="0" maxOccurs="1" />
|
||||
<xsd:element name="remote" type="remote-type" minOccurs="0" maxOccurs="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="id" type="xsd:ID" />
|
||||
<xsd:attribute name="target" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Name of the target class.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="timeout" type="xsd:long" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
default timeout for '!!' invocations
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="transactional" type="xsd:boolean">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Set to true if messages should have REQUIRES_NEW semantics
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="interface" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Interface implemented by target class.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="lifecycle" type="lifecycle-type">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Lifecycle, permanent or temporary
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<!-- trap exits -->
|
||||
<xsd:complexType name="trap-exits-type">
|
||||
<xsd:choice minOccurs="1" maxOccurs="unbounded">
|
||||
<xsd:element name="trap-exit" type="xsd:string"/>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
|
||||
<!-- active objects -->
|
||||
<xsd:complexType name="active-objects-type">
|
||||
<xsd:choice minOccurs="1" maxOccurs="unbounded">
|
||||
<xsd:element name="active-object" type="active-object-type"/>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
|
||||
<!-- Supervisor -->
|
||||
<xsd:complexType name="strategy-type" >
|
||||
<xsd:sequence>
|
||||
<xsd:element name="trap-exits" type="trap-exits-type" minOccurs="1" maxOccurs="1"/>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="failover" type="failover-type">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Failover scheme, AllForOne or OneForOne
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="retries" type="xsd:int">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Maximal number of retries.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="timerange" type="xsd:int">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Timerange for restart.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<!-- ActiveObject -->
|
||||
<xsd:element name="active-object" type="active-object-type"/>
|
||||
|
||||
|
||||
<!-- Supervision -->
|
||||
<xsd:element name="supervision">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="restart-strategy" type="strategy-type" minOccurs="1" maxOccurs="1"/>
|
||||
<xsd:element name="active-objects" type="active-objects-type" minOccurs="1" maxOccurs="1"/>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="id" type="xsd:ID" use="required"/>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
</xsd:schema>
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
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: '<akka:" + childName + ">'")
|
||||
} else {
|
||||
childElement
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
81
akka-spring/src/main/scala/ActiveObjectFactoryBean.scala
Normal file
81
akka-spring/src/main/scala/ActiveObjectFactoryBean.scala
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
41
akka-spring/src/main/scala/ActiveObjectProperties.scala
Normal file
41
akka-spring/src/main/scala/ActiveObjectProperties.scala
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
||||
18
akka-spring/src/main/scala/AkkaNamespaceHandler.scala
Normal file
18
akka-spring/src/main/scala/AkkaNamespaceHandler.scala
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
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]
|
||||
}
|
||||
41
akka-spring/src/main/scala/AkkaSpringConfigurationTags.scala
Normal file
41
akka-spring/src/main/scala/AkkaSpringConfigurationTags.scala
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
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"
|
||||
}
|
||||
24
akka-spring/src/main/scala/StringReflect.scala
Normal file
24
akka-spring/src/main/scala/StringReflect.scala
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
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]]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
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]
|
||||
}
|
||||
63
akka-spring/src/main/scala/SupervisionFactoryBean.scala
Normal file
63
akka-spring/src/main/scala/SupervisionFactoryBean.scala
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
@ -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<String, String> mapState;
|
||||
private TransactionalVector<String> vectorState;
|
||||
private TransactionalRef<String> 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");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
|
||||
|
||||
<bean id="akkaInterceptor" class="se.scalablesolutions.akka.spring.AkkaSpringInterceptor">
|
||||
</bean>
|
||||
|
||||
<bean id="service" class="se.scalablesolutions.akka.spring.MyService">
|
||||
</bean>
|
||||
|
||||
<bean id="actorBeanService" class="org.springframework.aop.framework.ProxyFactoryBean">
|
||||
<property name="target">
|
||||
<ref bean="service"/>
|
||||
</property>
|
||||
<property name="interceptorNames">
|
||||
<list>
|
||||
<value>akkaInterceptor</value>
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
62
akka-spring/src/test/resources/test-config.xml
Normal file
62
akka-spring/src/test/resources/test-config.xml
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:akka="http://www.akkasource.org/schema/akka"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
|
||||
http://www.akkasource.org/schema/akka
|
||||
file:////../../main/resources/se/scalablesolutions/akka/spring/akka.xsd">
|
||||
|
||||
<bean id="wrappedService"
|
||||
class="se.scalablesolutions.akka.actor.ActiveObject"
|
||||
factory-method="newInstance">
|
||||
<constructor-arg index="0" type="java.lang.Class" value="foo.bar.MyPojo"/>
|
||||
<constructor-arg index="1" value="1000"/>
|
||||
</bean>
|
||||
|
||||
<akka:active-object id="active-object1"
|
||||
target="foo.bar.MyPojo"
|
||||
timeout="1000"/>
|
||||
|
||||
<akka:active-object id="service2"
|
||||
target="foo.bar.MyPojo"
|
||||
timeout="2000"
|
||||
transactional="true"/>
|
||||
|
||||
<akka:active-object id="service3"
|
||||
target="foo.bar.MyPojo"
|
||||
timeout="2000"
|
||||
transactional="true">
|
||||
<akka:restart-callbacks pre="preRestart"/>
|
||||
</akka:active-object>
|
||||
|
||||
<akka:active-object id="active-object2"
|
||||
target="foo.bar.MyPojo"
|
||||
timeout="2000"
|
||||
transactional="true">
|
||||
<akka:restart-callbacks pre="preRestartMethod" post="postRestartMethod"/>
|
||||
<akka:remote host="localhost" port="9998" />
|
||||
</akka:active-object>
|
||||
|
||||
<akka:active-object id="remote-service1" target="foo.bar.MyPojo" timeout="1000">
|
||||
<akka:remote host="localhost" port="9998" />
|
||||
</akka:active-object>
|
||||
|
||||
<akka:supervision id="supervision1">
|
||||
<akka:restart-strategy failover="AllForOne" retries="3" timerange="1000">
|
||||
<akka:trap-exits>
|
||||
<akka:trap-exit>java.io.IOException</akka:trap-exit>
|
||||
<akka:trap-exit>java.lang.NullPointerException</akka:trap-exit>
|
||||
</akka:trap-exits>
|
||||
</akka:restart-strategy>
|
||||
<akka:active-objects>
|
||||
<akka:active-object target="foo.bar.Foo" lifecycle="permanent" timeout="1000"/>
|
||||
<akka:active-object interface="foo.bar.Bar" target="foo.bar.BarImpl" lifecycle="permanent" timeout="1000"/>
|
||||
<akka:active-object target="foo.bar.SomeService" lifecycle="temporary" timeout="1000">
|
||||
<akka:restart-callbacks pre="preRestartMethod" post="postRestartMethod"/>
|
||||
</akka:active-object>
|
||||
</akka:active-objects>
|
||||
</akka:supervision>
|
||||
|
||||
|
||||
</beans>
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
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 = <akka:active-object id="active-object1"
|
||||
target="foo.bar.MyPojo"
|
||||
timeout="1000"
|
||||
transactional="true"/>
|
||||
dom(xml).getDocumentElement
|
||||
}
|
||||
|
||||
private def createTestElement2 : Element = {
|
||||
val xml = <akka:active-object id="active-object1"
|
||||
timeout="1000"
|
||||
transactional="true"/>
|
||||
dom(xml).getDocumentElement
|
||||
}
|
||||
}
|
||||
41
akka-spring/src/test/scala/ActiveObjectFactoryBeanTest.scala
Normal file
41
akka-spring/src/test/scala/ActiveObjectFactoryBeanTest.scala
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
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:
|
||||
}
|
||||
}
|
||||
}
|
||||
40
akka-spring/src/test/scala/ScalaDom.scala
Normal file
40
akka-spring/src/test/scala/ScalaDom.scala
Normal file
|
|
@ -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
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
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 = <akka:active-object id="active-object1"
|
||||
target="foo.bar.MyPojo"
|
||||
timeout="1000"
|
||||
transactional="true"/>
|
||||
dom(xml).getDocumentElement
|
||||
}
|
||||
|
||||
private def createSupervisorElement : Element = {
|
||||
val xml = <akka:supervision id="supervision1">
|
||||
<akka:restart-strategy failover="AllForOne" retries="3" timerange="1000">
|
||||
<akka:trap-exits>
|
||||
<akka:trap-exit>java.io.IOException</akka:trap-exit>
|
||||
<akka:trap-exit>java.lang.NullPointerException</akka:trap-exit>
|
||||
</akka:trap-exits>
|
||||
</akka:restart-strategy>
|
||||
<akka:active-objects>
|
||||
<akka:active-object target="foo.bar.Foo" lifecycle="permanent" timeout="1000"/>
|
||||
<akka:active-object interface="foo.bar.IBar" target="foo.bar.Bar" lifecycle="permanent" timeout="1000"/>
|
||||
<akka:active-object target="foo.bar.MyPojo" lifecycle="temporary" timeout="1000">
|
||||
<akka:restart-callbacks pre="preRestart" post="postRestart"/>
|
||||
</akka:active-object>
|
||||
</akka:active-objects>
|
||||
</akka:supervision>
|
||||
dom(xml).getDocumentElement
|
||||
}
|
||||
|
||||
|
||||
private def createSupervisorMissingAttribute : Element = {
|
||||
val xml = <akka:supervision id="supervision1">
|
||||
<akka:restart-strategy failover="AllForOne" retries="3">
|
||||
<akka:trap-exits>
|
||||
<akka:trap-exit>java.io.IOException</akka:trap-exit>
|
||||
</akka:trap-exits>
|
||||
</akka:restart-strategy>
|
||||
<akka:active-objects>
|
||||
<akka:active-object target="foo.bar.Foo" lifecycle="permanent" timeout="1000"/>
|
||||
</akka:active-objects>
|
||||
</akka:supervision>
|
||||
dom(xml).getDocumentElement
|
||||
}
|
||||
|
||||
private def createSupervisorMissingElement : Element = {
|
||||
val xml = <akka:supervision id="supervision1">
|
||||
<akka:restart-strategy failover="AllForOne" retries="3" timerange="1000">
|
||||
</akka:restart-strategy>
|
||||
<akka:active-objects>
|
||||
<akka:active-object target="foo.bar.Foo" lifecycle="permanent" timeout="1000"/>
|
||||
<akka:active-object interface="foo.bar.IBar" target="foo.bar.Bar" lifecycle="permanent" timeout="1000"/>
|
||||
</akka:active-objects>
|
||||
</akka:supervision>
|
||||
dom(xml).getDocumentElement
|
||||
}
|
||||
}
|
||||
|
||||
41
akka-spring/src/test/scala/SupervisionFactoryBeanTest.scala
Normal file
41
akka-spring/src/test/scala/SupervisionFactoryBeanTest.scala
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
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])
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue