Fixed failing (and temporarily disabled) tests in akka-spring after refactoring from ActiveObject to TypedActor.

- Further:
  * Dropped support for @PostConstruct
  * Cleaned up typed actor implementations for testing
This commit is contained in:
Martin Krasser 2010-07-30 13:17:44 +02:00
parent 2903613098
commit 01c3be9af4
23 changed files with 120 additions and 179 deletions

View file

@ -1,10 +1,6 @@
package se.scalablesolutions.akka.camel;
import org.apache.camel.Body;
import org.apache.camel.Header;
import se.scalablesolutions.akka.actor.annotation.consume;
import se.scalablesolutions.akka.actor.*;
import se.scalablesolutions.akka.actor.TypedActor;
/**
* @author Martin Krasser
@ -15,21 +11,18 @@ public class PojoBase extends TypedActor implements PojoBaseIntf {
return "m1base: " + b + " " + h;
}
@consume("direct:m2base")
public String m2(@Body String b, @Header("test") String h) {
public String m2(String b, String h) {
return "m2base: " + b + " " + h;
}
@consume("direct:m3base")
public String m3(@Body String b, @Header("test") String h) {
public String m3(String b, String h) {
return "m3base: " + b + " " + h;
}
@consume("direct:m4base")
public String m4(@Body String b, @Header("test") String h) {
public String m4(String b, String h) {
return "m4base: " + b + " " + h;
}
public void m5(@Body String b, @Header("test") String h) {
public void m5(String b, String h) {
}
}

View file

@ -1,10 +1,6 @@
package se.scalablesolutions.akka.camel;
import org.apache.camel.Body;
import org.apache.camel.Header;
import se.scalablesolutions.akka.actor.annotation.consume;
import se.scalablesolutions.akka.actor.*;
import se.scalablesolutions.akka.actor.TypedActor;
/**
* @author Martin Krasser
@ -15,8 +11,7 @@ public class PojoImpl extends TypedActor implements PojoIntf {
return "m1impl: " + b + " " + h;
}
@consume("direct:m2impl")
public String m2(@Body String b, @Header("test") String h) {
public String m2(String b, String h) {
return "m2impl: " + b + " " + h;
}
}

View file

@ -1,14 +1,12 @@
package se.scalablesolutions.akka.camel;
import se.scalablesolutions.akka.actor.annotation.consume;
import se.scalablesolutions.akka.actor.*;
import se.scalablesolutions.akka.actor.TypedActor;
/**
* @author Martin Krasser
*/
public class PojoRemote extends TypedActor implements PojoRemoteIntf {
@consume("direct:remote-active-object")
public String foo(String s) {
return String.format("remote typed actor: %s", s);
}

View file

@ -1,14 +1,12 @@
package se.scalablesolutions.akka.camel;
import se.scalablesolutions.akka.actor.annotation.consume;
import se.scalablesolutions.akka.actor.*;
import se.scalablesolutions.akka.actor.TypedActor;
/**
* @author Martin Krasser
*/
public class PojoSingle extends TypedActor implements PojoSingleIntf {
@consume("direct:foo")
public void foo(String b) {
}

View file

@ -1,16 +1,11 @@
package se.scalablesolutions.akka.camel;
import org.apache.camel.Body;
import org.apache.camel.Header;
import se.scalablesolutions.akka.actor.annotation.consume;
import se.scalablesolutions.akka.actor.*;
import se.scalablesolutions.akka.actor.TypedActor;
public class PojoSub extends PojoBase implements PojoSubIntf {
@Override
@consume("direct:m1sub")
public String m1(@Body String b, @Header("test") String h) {
public String m1(String b, String h) {
return "m1sub: " + b + " " + h;
}
@ -20,8 +15,7 @@ public class PojoSub extends PojoBase implements PojoSubIntf {
}
@Override
@consume("direct:m3sub")
public String m3(@Body String b, @Header("test") String h) {
public String m3(String b, String h) {
return "m3sub: " + b + " " + h;
}

View file

@ -1,18 +0,0 @@
package sample.camel;
import org.apache.camel.Body;
import org.apache.camel.Header;
import se.scalablesolutions.akka.actor.TypedActor;
import se.scalablesolutions.akka.actor.annotation.consume;
/**
* @author Martin Krasser
*/
public class ConsumerPojo2Impl extends TypedActor implements ConsumerPojo2 {
@consume("direct:default")
public String foo(String body) {
return String.format("default: %s", body);
}
}

View file

@ -8,7 +8,7 @@ import se.scalablesolutions.akka.actor.annotation.consume;
/**
* @author Martin Krasser
*/
public interface ConsumerPojo1 {
public interface TypedConsumer1 {
@consume("file:data/input/pojo")
public void foo(String body);

View file

@ -3,21 +3,18 @@ package sample.camel;
import org.apache.camel.Body;
import org.apache.camel.Header;
import se.scalablesolutions.akka.actor.annotation.consume;
import se.scalablesolutions.akka.actor.TypedActor;
/**
* @author Martin Krasser
*/
public class ConsumerPojo1Impl extends TypedActor implements ConsumerPojo1 {
public class TypedConsumer1Impl extends TypedActor implements TypedConsumer1 {
@consume("file:data/input/pojo")
public void foo(String body) {
System.out.println("Received message:");
System.out.println(body);
}
@consume("jetty:http://0.0.0.0:8877/camel/pojo")
public String bar(@Body String body, @Header("name") String header) {
return String.format("body=%s header=%s", body, header);
}

View file

@ -7,7 +7,7 @@ import se.scalablesolutions.akka.actor.annotation.consume;
/**
* @author Martin Krasser
*/
public interface ConsumerPojo2 {
public interface TypedConsumer2 {
@consume("direct:default")
public String foo(String body);

View file

@ -0,0 +1,13 @@
package sample.camel;
import se.scalablesolutions.akka.actor.TypedActor;
/**
* @author Martin Krasser
*/
public class TypedConsumer2Impl extends TypedActor implements TypedConsumer2 {
public String foo(String body) {
return String.format("default: %s", body);
}
}

View file

@ -8,7 +8,7 @@ import se.scalablesolutions.akka.actor.annotation.consume;
/**
* @author Martin Krasser
*/
public interface RemoteConsumerPojo1 {
public interface TypedRemoteConsumer1 {
@consume("jetty:http://localhost:6644/camel/remote-active-object-1")
public String foo(@Body String body, @Header("name") String header);

View file

@ -3,15 +3,13 @@ package sample.camel;
import org.apache.camel.Body;
import org.apache.camel.Header;
import se.scalablesolutions.akka.actor.annotation.consume;
import se.scalablesolutions.akka.actor.TypedActor;
/**
* @author Martin Krasser
*/
public class RemoteConsumerPojo1Impl extends TypedActor implements RemoteConsumerPojo1 {
public class TypedRemoteConsumer1Impl extends TypedActor implements TypedRemoteConsumer1 {
@consume("jetty:http://localhost:6644/camel/remote-active-object-1")
public String foo(@Body String body, @Header("name") String header) {
return String.format("remote1: body=%s header=%s", body, header);
}

View file

@ -7,7 +7,7 @@ import se.scalablesolutions.akka.actor.annotation.consume;
/**
* @author Martin Krasser
*/
public class RemoteConsumerPojo2 {
public class TypedRemoteConsumer2 {
@consume("jetty:http://localhost:6644/camel/remote-active-object-2")
public String foo(@Body String body, @Header("name") String header) {

View file

@ -20,6 +20,6 @@ http://camel.apache.org/schema/spring/camel-spring.xsd">
<akka:camel-context ref="camelContext" />
</akka:camel-service>
<akka:active-object id="pojo3" target="sample.camel.BeanImpl" timeout="1000" />
<akka:active-object id="pojo3" interface="sample.camel.BeanIntf" target="sample.camel.BeanImpl" timeout="1000" />
</beans>

View file

@ -89,7 +89,7 @@ class Boot {
// Active object example
// -----------------------------------------------------------------------
TypedActor.newInstance(classOf[ConsumerPojo1], classOf[ConsumerPojo1Impl])
TypedActor.newInstance(classOf[TypedConsumer1], classOf[TypedConsumer1Impl])
}
/**

View file

@ -18,8 +18,8 @@ object ClientApplication {
val actor1 = actorOf[RemoteActor1]
val actor2 = RemoteClient.actorFor("remote2", "localhost", 7777)
val actobj1 = TypedActor.newRemoteInstance(classOf[RemoteConsumerPojo1], classOf[RemoteConsumerPojo1Impl], "localhost", 7777)
//val actobj2 = TODO: create reference to server-managed typed actor (RemoteConsumerPojo2)
val actobj1 = TypedActor.newRemoteInstance(classOf[TypedRemoteConsumer1], classOf[TypedRemoteConsumer1Impl], "localhost", 7777)
//val actobj2 = TODO: create reference to server-managed typed actor (TypedRemoteConsumer2)
actor1.start

View file

@ -18,7 +18,7 @@ object StandaloneApplication {
// 'externally' register typed actors
val registry = new SimpleRegistry
registry.put("pojo1", TypedActor.newInstance(classOf[BeanIntf], classOf[BeanImpl]))
registry.put("sample", TypedActor.newInstance(classOf[BeanIntf], classOf[BeanImpl]))
// customize CamelContext
CamelContextManager.init(new DefaultCamelContext(registry))
@ -28,17 +28,19 @@ object StandaloneApplication {
val camelService = CamelService.newInstance.load
// access 'externally' registered typed actors
assert("hello msg1" == context.createProducerTemplate.requestBody("direct:test1", "msg1"))
assert("hello msg2" == context.createProducerTemplate.requestBody("direct:test2", "msg2"))
assert("hello msg1" == context.createProducerTemplate.requestBody("direct:test", "msg1"))
// set expectations on upcoming endpoint activation
val activation = camelService.expectEndpointActivationCount(1)
// 'internally' register typed actor (requires CamelService)
TypedActor.newInstance(classOf[ConsumerPojo2], classOf[ConsumerPojo2Impl])
TypedActor.newInstance(classOf[TypedConsumer2], classOf[TypedConsumer2Impl])
// internal registration is done in background. Wait a bit ...
Thread.sleep(1000)
activation.await
// access 'internally' (automatically) registered active-objects
// (see @consume annotation value at ConsumerPojo2.foo method)
// (see @consume annotation value at TypedConsumer2.foo method)
assert("default: msg3" == context.createProducerTemplate.requestBody("direct:default", "msg3"))
// shutdown CamelService
@ -51,9 +53,8 @@ object StandaloneApplication {
class StandaloneApplicationRoute extends RouteBuilder {
def configure = {
// routes to typed actors (in SimpleRegistry)
from("direct:test1").to("active-object:pojo1?method=foo")
from("direct:test2").to("active-object:pojo2?method=foo")
// route to typed actors (in SimpleRegistry)
from("direct:test").to("active-object:sample?method=foo")
}
}

View file

@ -20,7 +20,7 @@ import org.springframework.context.{ApplicationContext,ApplicationContextAware}
import org.springframework.util.ReflectionUtils
import org.springframework.util.StringUtils
import se.scalablesolutions.akka.actor.{TypedActorConfiguration, TypedActor}
import se.scalablesolutions.akka.actor.{AspectInitRegistry, TypedActorConfiguration, TypedActor}
import se.scalablesolutions.akka.config.ScalaConfig.{ShutdownCallback, RestartCallbacks}
import se.scalablesolutions.akka.dispatch.MessageDispatcher
import se.scalablesolutions.akka.util.{Logging, Duration}
@ -75,7 +75,7 @@ class TypedActorFactoryBean extends AbstractFactoryBean[AnyRef] with Logging wit
target.toClass
} catch {
// required by contract to return null
case e: ClassNotFoundException => null
case e: IllegalArgumentException => null
}
/*
@ -86,30 +86,15 @@ class TypedActorFactoryBean extends AbstractFactoryBean[AnyRef] with Logging wit
if (isRemote) argumentList += "r"
if (hasInterface) argumentList += "i"
if (hasDispatcher) argumentList += "d"
postConstruct(setProperties(create(argumentList)))
val ref = create(argumentList)
setProperties(AspectInitRegistry.initFor(ref).targetInstance)
ref
}
/**
* Stop the typed actor if it is a singleton.
*/
override def destroyInstance(instance: AnyRef) = TypedActor.stop(instance.asInstanceOf[TypedActor])
/**
* Invokes any method annotated with @PostConstruct
* When interfaces are specified, this method is invoked both on the
* target instance and on the typed actor, so a developer is free do decide
* where the annotation should be. If no interface is specified it is only invoked
* on the typed actor
*/
private def postConstruct(ref: AnyRef): AnyRef = {
// Invoke postConstruct method if any
for {
method <- ref.getClass.getMethods
if method.isAnnotationPresent(classOf[PostConstruct])
} method.invoke(ref)
ref
}
override def destroyInstance(instance: AnyRef) = TypedActor.stop(instance)
private def setProperties(ref: AnyRef): AnyRef = {
if (hasSetDependecies) return ref
@ -139,12 +124,11 @@ class TypedActorFactoryBean extends AbstractFactoryBean[AnyRef] with Logging wit
if (target == null || target == "") throw new AkkaBeansException(
"The 'target' part of the 'akka:actor' element in the Spring config file can't be null or empty string")
argList match {
case "ri" => TypedActor.newInstance(interface.toClass, newInstanceFor(target.toClass), createConfig.makeRemote(host, port))
case "i" => TypedActor.newInstance(interface.toClass, newInstanceFor(target.toClass), createConfig)
case "id" => TypedActor.newInstance(interface.toClass, newInstanceFor(target.toClass), createConfig.dispatcher(dispatcherInstance))
case "rid" => TypedActor.newInstance(interface.toClass, newInstanceFor(target.toClass),
createConfig.makeRemote(host, port).dispatcher(dispatcherInstance))
case _ => TypedActor.newInstance(interface.toClass, newInstanceFor(target.toClass), createConfig)
case "ri" => TypedActor.newInstance(interface.toClass, target.toClass, createConfig.makeRemote(host, port))
case "i" => TypedActor.newInstance(interface.toClass, target.toClass, createConfig)
case "id" => TypedActor.newInstance(interface.toClass, target.toClass, createConfig.dispatcher(dispatcherInstance))
case "rid" => TypedActor.newInstance(interface.toClass, target.toClass, createConfig.makeRemote(host, port).dispatcher(dispatcherInstance))
case _ => TypedActor.newInstance(interface.toClass, target.toClass, createConfig)
// case "rd" => TypedActor.newInstance(target.toClass, createConfig.makeRemote(host, port).dispatcher(dispatcherInstance))
// case "r" => TypedActor.newInstance(target.toClass, createConfig.makeRemote(host, port))
// case "d" => TypedActor.newInstance(target.toClass, createConfig.dispatcher(dispatcherInstance))
@ -159,13 +143,6 @@ class TypedActorFactoryBean extends AbstractFactoryBean[AnyRef] with Logging wit
config
}
def newInstanceFor[T <: AnyRef](clazz: Class[T]): T = {
var ref = clazz.newInstance().asInstanceOf[T]
postConstruct(setProperties(ref))
hasSetDependecies = true
ref
}
private[akka] def isRemote = (host != null) && (!host.isEmpty)
private[akka] def hasInterface = (interface != null) && (!interface.isEmpty)

View file

@ -10,10 +10,11 @@ import se.scalablesolutions.akka.actor.*;
public class Pojo extends TypedActor implements PojoInf, ApplicationContextAware {
private String string;
private String stringFromVal;
private String stringFromRef;
private boolean gotApplicationContext = false;
private boolean postConstructInvoked = false;
private boolean initInvoked = false;
public boolean gotApplicationContext() {
return gotApplicationContext;
@ -23,20 +24,28 @@ public class Pojo extends TypedActor implements PojoInf, ApplicationContextAware
gotApplicationContext = true;
}
public void setString(String s) {
string = s;
public String getStringFromVal() {
return stringFromVal;
}
public String getString() {
return string;
public void setStringFromVal(String s) {
stringFromVal = s;
}
@PostConstruct
public void create() {
postConstructInvoked = true;
public String getStringFromRef() {
return stringFromRef;
}
public boolean isPostConstructInvoked() {
return postConstructInvoked;
public void setStringFromRef(String s) {
stringFromRef = s;
}
@Override
public void init() {
initInvoked = true;
}
public boolean isInitInvoked() {
return initInvoked;
}
}

View file

@ -5,10 +5,9 @@ import javax.annotation.PostConstruct;
public interface PojoInf {
public String getString();
public String getStringFromVal();
public String getStringFromRef();
public boolean gotApplicationContext();
public boolean isPostConstructInvoked();
public boolean isInitInvoked();
@PostConstruct
public void create();
}

View file

@ -21,23 +21,16 @@
timeout="1000"
scope="prototype"/>
<bean id="string" class="java.lang.String">
<constructor-arg value="someString"/>
</bean>
<akka:active-object id="pojoInf"
<akka:active-object id="typedActor"
interface="se.scalablesolutions.akka.spring.PojoInf"
target="se.scalablesolutions.akka.spring.Pojo"
scope="singleton"
timeout="1000">
<property name="string" value="akka rocks"/>
<property name="stringFromVal" value="akka rocks"/>
<property name="stringFromRef" ref="string"/>
</akka:active-object>
<!--akka:active-object id="bean"
target="org.springframework.core.io.ResourceEditor"
transactional="true"
timeout="1000"
scope="prototype">
<property name="source" ref="string"/>
</akka:active-object-->
<bean id="string" class="java.lang.String">
<constructor-arg value="spring rocks"/>
</bean>
</beans>

View file

@ -17,7 +17,6 @@ class CamelServiceSpringFeatureTest extends FeatureSpec with BeforeAndAfterEach
ActorRegistry.shutdownAll
}
/*
feature("start CamelService from Spring application context") {
import CamelContextManager._
scenario("with a custom CamelContext and access a registered typed actor") {
@ -40,5 +39,4 @@ class CamelServiceSpringFeatureTest extends FeatureSpec with BeforeAndAfterEach
appctx.close
}
}
*/
}

View file

@ -3,19 +3,22 @@
*/
package se.scalablesolutions.akka.spring
import org.scalatest.Spec
import org.scalatest.matchers.ShouldMatchers
import org.scalatest.junit.JUnitRunner
import se.scalablesolutions.akka.actor.ActorRegistry;
import org.junit.runner.RunWith
import org.springframework.core.io.ResourceEditor
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext
import org.scalatest.junit.JUnitRunner
import org.scalatest.{BeforeAndAfterAll, Spec}
import org.scalatest.matchers.ShouldMatchers
/**
* Test for TypedActorFactoryBean
* @author michaelkober
*/
@RunWith(classOf[JUnitRunner])
class TypedActorFactoryBeanTest extends Spec with ShouldMatchers {
class TypedActorFactoryBeanTest extends Spec with ShouldMatchers with BeforeAndAfterAll {
override protected def afterAll = ActorRegistry.shutdownAll
describe("A TypedActorFactoryBean") {
val bean = new TypedActorFactoryBean
@ -48,22 +51,37 @@ class TypedActorFactoryBeanTest extends Spec with ShouldMatchers {
assert(bean.getObjectType == classOf[String])
}
/*
it("should create a proxy of type PojoInf") {
val bean = new TypedActorFactoryBean()
bean.setInterface("se.scalablesolutions.akka.spring.PojoInf")
bean.setTarget("se.scalablesolutions.akka.spring.Pojo")
bean.timeout = 1000
val entries = new PropertyEntries()
val entry = new PropertyEntry()
entry.name = "stringFromVal"
entry.value = "tests rock"
entries.add(entry)
bean.setProperty(entries)
assert(classOf[PojoInf].isAssignableFrom(bean.getObjectType))
// Check that we have injected the depencency correctly
val target = bean.createInstance.asInstanceOf[PojoInf]
assert(target.getStringFromVal === entry.value)
}
it("should create an application context and verify dependency injection") {
var ctx = new ClassPathXmlApplicationContext("appContext.xml");
val target:ResourceEditor = ctx.getBean("bean").asInstanceOf[ResourceEditor]
assert(target.getSource === "someString")
val pojoInf = ctx.getBean("pojoInf").asInstanceOf[PojoInf];
Thread.sleep(200)
assert(pojoInf.isPostConstructInvoked)
assert(pojoInf.getString == "akka rocks")
assert(pojoInf.gotApplicationContext)
val ta = ctx.getBean("typedActor").asInstanceOf[PojoInf];
assert(ta.isInitInvoked)
assert(ta.getStringFromVal == "akka rocks")
assert(ta.getStringFromRef == "spring rocks")
assert(ta.gotApplicationContext)
ctx.close
}
it("should stop the created typed actor when scope is singleton and the context is closed") {
var ctx = new ClassPathXmlApplicationContext("appContext.xml");
val target = ctx.getBean("bean-singleton").asInstanceOf[SampleBean]
val target = ctx.getBean("bean-singleton").asInstanceOf[SampleBeanIntf]
assert(!target.down)
ctx.close
assert(target.down)
@ -71,32 +89,10 @@ class TypedActorFactoryBeanTest extends Spec with ShouldMatchers {
it("should not stop the created typed actor when scope is prototype and the context is closed") {
var ctx = new ClassPathXmlApplicationContext("appContext.xml");
val target = ctx.getBean("bean-prototype").asInstanceOf[SampleBean]
val target = ctx.getBean("bean-prototype").asInstanceOf[SampleBeanIntf]
assert(!target.down)
ctx.close
assert(!target.down)
}
*/
}
}
/*
// ------ NOTE: Can't work now when we only support POJO with interface -----
it("should create a proxy of type ResourceEditor") {
val bean = new TypedActorFactoryBean()
// we must have a java class here
bean.setTarget("org.springframework.core.io.ResourceEditor")
val entries = new PropertyEntries()
val entry = new PropertyEntry()
entry.name = "source"
entry.value = "sourceBeanIsAString"
entries.add(entry)
bean.setProperty(entries)
assert(bean.getObjectType == classOf[ResourceEditor])
// Check that we have injected the depencency correctly
val target:ResourceEditor = bean.createInstance.asInstanceOf[ResourceEditor]
assert(target.getSource === entry.value)
}
*/