Merge branch 'wip-typed-actor-jboner' into master
Conflicts: akka-core/src/test/scala/ActiveObjectContextSpec.scala akka-core/src/test/scala/ActiveObjectGuiceConfiguratorSpec.scala akka-core/src/test/scala/ActorObjectUtilFunctionsSpec.scala akka-core/src/test/scala/NestedTransactionalActiveObjectSpec.scala akka-core/src/test/scala/NestedTransactionalTypedActorSpec.scala akka-core/src/test/scala/RemoteTransactionalActiveObjectSpec.scala akka-core/src/test/scala/RemoteTransactionalTypedActorSpec.scala akka-core/src/test/scala/RestartNestedTransactionalActiveObjectSpec.scala akka-core/src/test/scala/RestartNestedTransactionalTypedActorSpec.scala akka-core/src/test/scala/RestartTransactionalActiveObjectSpec.scala akka-core/src/test/scala/RestartTransactionalTypedActorSpec.scala akka-core/src/test/scala/TransactionalActiveObjectSpec.scala akka-core/src/test/scala/TransactionalTypedActorSpec.scala akka-core/src/test/scala/TypedActorContextSpec.scala akka-core/src/test/scala/TypedActorGuiceConfiguratorSpec.scala akka-core/src/test/scala/TypedActorUtilFunctionsSpec.scala akka-core/src/test/scala/actor/ActiveObjectContextSpec.scala akka-core/src/test/scala/actor/ActiveObjectGuiceConfiguratorSpec.scala akka-core/src/test/scala/actor/ActiveObjectLifecycleSpec.scala akka-core/src/test/scala/actor/ActorObjectUtilFunctionsSpec.scala akka-core/src/test/scala/actor/NestedTransactionalActiveObjectSpec.scala akka-core/src/test/scala/actor/RestartNestedTransactionalActiveObjectSpec.scala akka-core/src/test/scala/actor/RestartTransactionalActiveObjectSpec.scala akka-core/src/test/scala/actor/TransactionalActiveObjectSpec.scala akka-core/src/test/scala/remote/RemoteTransactionalActiveObjectSpec.scala
This commit is contained in:
commit
117eb3bf92
123 changed files with 3546 additions and 2511 deletions
|
|
@ -3,7 +3,7 @@
|
|||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<name>Akka Active Object Tests in Java</name>
|
||||
<name>Akka TypedActor Tests in Java</name>
|
||||
<artifactId>akka-active-object-test</artifactId>
|
||||
<groupId>se.scalablesolutions.akka</groupId>
|
||||
<version>0.9</version>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ public class AllTest extends TestCase {
|
|||
suite.addTestSuite(InMemoryStateTest.class);
|
||||
suite.addTestSuite(InMemNestedStateTest.class);
|
||||
suite.addTestSuite(RemoteInMemoryStateTest.class);
|
||||
suite.addTestSuite(ActiveObjectGuiceConfiguratorTest.class);
|
||||
suite.addTestSuite(TypedActorGuiceConfiguratorTest.class);
|
||||
return suite;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ package se.scalablesolutions.akka.api;
|
|||
|
||||
import se.scalablesolutions.akka.config.*;
|
||||
import se.scalablesolutions.akka.config.Config;
|
||||
import se.scalablesolutions.akka.config.ActiveObjectConfigurator;
|
||||
import se.scalablesolutions.akka.config.TypedActorConfigurator;
|
||||
import static se.scalablesolutions.akka.config.JavaConfig.*;
|
||||
import se.scalablesolutions.akka.actor.*;
|
||||
import junit.framework.TestCase;
|
||||
|
|
@ -14,7 +14,7 @@ import junit.framework.TestCase;
|
|||
public class InMemNestedStateTest extends TestCase {
|
||||
static String messageLog = "";
|
||||
|
||||
final private ActiveObjectConfigurator conf = new ActiveObjectConfigurator();
|
||||
final private TypedActorConfigurator conf = new TypedActorConfigurator();
|
||||
|
||||
public InMemNestedStateTest() {
|
||||
conf.configure(
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import junit.framework.TestCase;
|
|||
|
||||
import se.scalablesolutions.akka.config.Config;
|
||||
import se.scalablesolutions.akka.config.*;
|
||||
import se.scalablesolutions.akka.config.ActiveObjectConfigurator;
|
||||
import se.scalablesolutions.akka.config.TypedActorConfigurator;
|
||||
|
||||
import static se.scalablesolutions.akka.config.JavaConfig.*;
|
||||
|
||||
|
|
@ -17,7 +17,7 @@ import se.scalablesolutions.akka.actor.*;
|
|||
public class InMemoryStateTest extends TestCase {
|
||||
static String messageLog = "";
|
||||
|
||||
final private ActiveObjectConfigurator conf = new ActiveObjectConfigurator();
|
||||
final private TypedActorConfigurator conf = new TypedActorConfigurator();
|
||||
|
||||
public InMemoryStateTest() {
|
||||
Config.config();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package se.scalablesolutions.akka.api;
|
||||
|
||||
import static se.scalablesolutions.akka.actor.ActiveObject.link;
|
||||
import static se.scalablesolutions.akka.actor.ActiveObject.newInstance;
|
||||
import static se.scalablesolutions.akka.actor.TypedActor.link;
|
||||
import static se.scalablesolutions.akka.actor.TypedActor.newInstance;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
|
@ -15,7 +15,7 @@ import junit.framework.TestCase;
|
|||
* @author johanrask
|
||||
*
|
||||
*/
|
||||
public class MiscActiveObjectTest extends TestCase {
|
||||
public class MiscTypedActorTest extends TestCase {
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
package se.scalablesolutions.akka.api;
|
||||
|
||||
import se.scalablesolutions.akka.config.Config;
|
||||
import se.scalablesolutions.akka.actor.ActiveObject;
|
||||
import se.scalablesolutions.akka.config.ActiveObjectConfigurator;
|
||||
import se.scalablesolutions.akka.actor.TypedActor;
|
||||
import se.scalablesolutions.akka.config.TypedActorConfigurator;
|
||||
import se.scalablesolutions.akka.remote.RemoteNode;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
|
@ -23,14 +23,14 @@ public class RemoteInMemoryStateTest extends TestCase {
|
|||
try { Thread.currentThread().sleep(1000); } catch (Exception e) {}
|
||||
Config.config();
|
||||
}
|
||||
final ActiveObjectConfigurator conf = new ActiveObjectConfigurator();
|
||||
final TypedActorConfigurator conf = new TypedActorConfigurator();
|
||||
|
||||
protected void tearDown() {
|
||||
conf.stop();
|
||||
}
|
||||
|
||||
public void testMapShouldNotRollbackStateForStatefulServerInCaseOfSuccess() {
|
||||
InMemStateful stateful = ActiveObject.newRemoteInstance(InMemStateful.class, 1000, "localhost", 9999);
|
||||
InMemStateful stateful = TypedActor.newRemoteInstance(InMemStateful.class, 1000, "localhost", 9999);
|
||||
stateful.init();
|
||||
stateful.setMapState("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "init"); // set init state
|
||||
stateful.success("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state"); // transactionrequired
|
||||
|
|
@ -38,10 +38,10 @@ public class RemoteInMemoryStateTest extends TestCase {
|
|||
}
|
||||
|
||||
public void testMapShouldRollbackStateForStatefulServerInCaseOfFailure() {
|
||||
InMemStateful stateful = ActiveObject.newRemoteInstance(InMemStateful.class, 10000, "localhost", 9999);
|
||||
InMemStateful stateful = TypedActor.newRemoteInstance(InMemStateful.class, 10000, "localhost", 9999);
|
||||
stateful.init();
|
||||
stateful.setMapState("testShouldRollbackStateForStatefulServerInCaseOfFailure", "init"); // set init state
|
||||
InMemFailer failer = ActiveObject.newRemoteInstance(InMemFailer.class, 1000, "localhost", 9999); //conf.getInstance(InMemFailer.class);
|
||||
InMemFailer failer = TypedActor.newRemoteInstance(InMemFailer.class, 1000, "localhost", 9999); //conf.getInstance(InMemFailer.class);
|
||||
try {
|
||||
stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer); // call failing transactionrequired method
|
||||
fail("should have thrown an exception");
|
||||
|
|
@ -51,7 +51,7 @@ public class RemoteInMemoryStateTest extends TestCase {
|
|||
}
|
||||
|
||||
public void testVectorShouldNotRollbackStateForStatefulServerInCaseOfSuccess() {
|
||||
InMemStateful stateful = ActiveObject.newRemoteInstance(InMemStateful.class, 10000, "localhost", 9999);
|
||||
InMemStateful stateful = TypedActor.newRemoteInstance(InMemStateful.class, 10000, "localhost", 9999);
|
||||
stateful.init();
|
||||
stateful.setVectorState("init"); // set init state
|
||||
stateful.success("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state"); // transactionrequired
|
||||
|
|
@ -59,10 +59,10 @@ public class RemoteInMemoryStateTest extends TestCase {
|
|||
}
|
||||
|
||||
public void testVectorShouldRollbackStateForStatefulServerInCaseOfFailure() {
|
||||
InMemStateful stateful = ActiveObject.newRemoteInstance(InMemStateful.class, 10000, "localhost", 9999);
|
||||
InMemStateful stateful = TypedActor.newRemoteInstance(InMemStateful.class, 10000, "localhost", 9999);
|
||||
stateful.init();
|
||||
stateful.setVectorState("init"); // set init state
|
||||
InMemFailer failer = ActiveObject.newRemoteInstance(InMemFailer.class, 10000, "localhost", 9999); //conf.getInstance(InMemFailer.class);
|
||||
InMemFailer failer = TypedActor.newRemoteInstance(InMemFailer.class, 10000, "localhost", 9999); //conf.getInstance(InMemFailer.class);
|
||||
try {
|
||||
stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer); // call failing transactionrequired method
|
||||
fail("should have thrown an exception");
|
||||
|
|
@ -72,7 +72,7 @@ public class RemoteInMemoryStateTest extends TestCase {
|
|||
}
|
||||
|
||||
public void testRefShouldNotRollbackStateForStatefulServerInCaseOfSuccess() {
|
||||
InMemStateful stateful = ActiveObject.newRemoteInstance(InMemStateful.class, 10000, "localhost", 9999);
|
||||
InMemStateful stateful = TypedActor.newRemoteInstance(InMemStateful.class, 10000, "localhost", 9999);
|
||||
stateful.init();
|
||||
stateful.setRefState("init"); // set init state
|
||||
stateful.success("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state"); // transactionrequired
|
||||
|
|
@ -80,10 +80,10 @@ public class RemoteInMemoryStateTest extends TestCase {
|
|||
}
|
||||
|
||||
public void testRefShouldRollbackStateForStatefulServerInCaseOfFailure() {
|
||||
InMemStateful stateful = ActiveObject.newRemoteInstance(InMemStateful.class, 10000, "localhost", 9999);
|
||||
InMemStateful stateful = TypedActor.newRemoteInstance(InMemStateful.class, 10000, "localhost", 9999);
|
||||
stateful.init();
|
||||
stateful.setRefState("init"); // set init state
|
||||
InMemFailer failer = ActiveObject.newRemoteInstance(InMemFailer.class, 10000, "localhost", 9999); //conf.getInstance(InMemFailer.class);
|
||||
InMemFailer failer = TypedActor.newRemoteInstance(InMemFailer.class, 10000, "localhost", 9999); //conf.getInstance(InMemFailer.class);
|
||||
try {
|
||||
stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer); // call failing transactionrequired method
|
||||
fail("should have thrown an exception");
|
||||
|
|
|
|||
|
|
@ -10,14 +10,14 @@ import com.google.inject.Scopes;
|
|||
import junit.framework.TestCase;
|
||||
|
||||
import se.scalablesolutions.akka.config.Config;
|
||||
import se.scalablesolutions.akka.config.ActiveObjectConfigurator;
|
||||
import se.scalablesolutions.akka.config.TypedActorConfigurator;
|
||||
import static se.scalablesolutions.akka.config.JavaConfig.*;
|
||||
import se.scalablesolutions.akka.dispatch.*;
|
||||
|
||||
public class ActiveObjectGuiceConfiguratorTest extends TestCase {
|
||||
public class TypedActorGuiceConfiguratorTest extends TestCase {
|
||||
static String messageLog = "";
|
||||
|
||||
final private ActiveObjectConfigurator conf = new ActiveObjectConfigurator();
|
||||
final private TypedActorConfigurator conf = new TypedActorConfigurator();
|
||||
|
||||
protected void setUp() {
|
||||
Config.config();
|
||||
|
|
@ -46,7 +46,7 @@ public class ActiveObjectGuiceConfiguratorTest extends TestCase {
|
|||
|
||||
}
|
||||
|
||||
public void testGuiceActiveObjectInjection() {
|
||||
public void testGuiceTypedActorInjection() {
|
||||
messageLog = "";
|
||||
Foo foo = conf.getInstance(Foo.class);
|
||||
Bar bar = conf.getInstance(Bar.class);
|
||||
|
|
@ -69,7 +69,7 @@ public class ActiveObjectGuiceConfiguratorTest extends TestCase {
|
|||
}
|
||||
}
|
||||
|
||||
public void testActiveObjectInvocation() throws InterruptedException {
|
||||
public void testTypedActorInvocation() throws InterruptedException {
|
||||
messageLog = "";
|
||||
Foo foo = conf.getInstance(Foo.class);
|
||||
messageLog += foo.foo("foo ");
|
||||
|
|
@ -79,7 +79,7 @@ public class ActiveObjectGuiceConfiguratorTest extends TestCase {
|
|||
assertEquals("foo return_foo before_bar ", messageLog);
|
||||
}
|
||||
|
||||
public void testActiveObjectInvocationsInvocation() throws InterruptedException {
|
||||
public void testTypedActorInvocationsInvocation() throws InterruptedException {
|
||||
messageLog = "";
|
||||
Foo foo = conf.getInstance(Foo.class);
|
||||
Bar bar = conf.getInstance(Bar.class);
|
||||
|
|
@ -1 +1 @@
|
|||
class=se.scalablesolutions.akka.camel.component.ActiveObjectComponent
|
||||
class=se.scalablesolutions.akka.camel.component.TypedActorComponent
|
||||
|
|
@ -9,7 +9,7 @@ import java.util.Map
|
|||
import org.apache.camel.{ProducerTemplate, CamelContext}
|
||||
import org.apache.camel.impl.DefaultCamelContext
|
||||
|
||||
import se.scalablesolutions.akka.camel.component.ActiveObjectComponent
|
||||
import se.scalablesolutions.akka.camel.component.TypedActorComponent
|
||||
import se.scalablesolutions.akka.util.Logging
|
||||
|
||||
/**
|
||||
|
|
@ -29,13 +29,13 @@ trait CamelContextLifecycle extends Logging {
|
|||
private var _started = false
|
||||
|
||||
/**
|
||||
* Camel component for accessing active objects.
|
||||
* Camel component for accessing typed actors.
|
||||
*/
|
||||
private[camel] var activeObjectComponent: ActiveObjectComponent = _
|
||||
private[camel] var activeObjectComponent: TypedActorComponent = _
|
||||
|
||||
/**
|
||||
* Registry in which active objects are TEMPORARILY registered during
|
||||
* creation of Camel routes to active objects.
|
||||
* Registry in which typed actors are TEMPORARILY registered during
|
||||
* creation of Camel routes to typed actors.
|
||||
*/
|
||||
private[camel] var activeObjectRegistry: Map[String, AnyRef] = _
|
||||
|
||||
|
|
@ -93,15 +93,15 @@ trait CamelContextLifecycle extends Logging {
|
|||
* CamelContext stream-caching is enabled. If applications want to disable stream-
|
||||
* caching they can do so after this method returned and prior to calling start.
|
||||
* This method also registers a new
|
||||
* {@link se.scalablesolutions.akka.camel.component.ActiveObjectComponent} at
|
||||
* <code>context</code> under a name defined by ActiveObjectComponent.InternalSchema.
|
||||
* {@link se.scalablesolutions.akka.camel.component.TypedActorComponent} at
|
||||
* <code>context</code> under a name defined by TypedActorComponent.InternalSchema.
|
||||
*/
|
||||
def init(context: CamelContext) {
|
||||
this.activeObjectComponent = new ActiveObjectComponent
|
||||
this.activeObjectComponent = new TypedActorComponent
|
||||
this.activeObjectRegistry = activeObjectComponent.activeObjectRegistry
|
||||
this.context = context
|
||||
this.context.setStreamCaching(true)
|
||||
this.context.addComponent(ActiveObjectComponent.InternalSchema, activeObjectComponent)
|
||||
this.context.addComponent(TypedActorComponent.InternalSchema, activeObjectComponent)
|
||||
this.template = context.createProducerTemplate
|
||||
_initialized = true
|
||||
log.info("Camel context initialized")
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import se.scalablesolutions.akka.actor.{AspectInitRegistry, ActorRegistry}
|
|||
import se.scalablesolutions.akka.util.{Bootable, Logging}
|
||||
|
||||
/**
|
||||
* Used by applications (and the Kernel) to publish consumer actors and active objects via
|
||||
* Used by applications (and the Kernel) to publish consumer actors and typed actors via
|
||||
* Camel endpoints and to manage the life cycle of a a global CamelContext which can be
|
||||
* accessed via <code>se.scalablesolutions.akka.camel.CamelContextManager.context</code>.
|
||||
*
|
||||
|
|
@ -33,8 +33,8 @@ trait CamelService extends Bootable with Logging {
|
|||
* Starts the CamelService. Any started actor that is a consumer actor will be (asynchronously)
|
||||
* published as Camel endpoint. Consumer actors that are started after this method returned will
|
||||
* be published as well. Actor publishing is done asynchronously. A started (loaded) CamelService
|
||||
* also publishes <code>@consume</code> annotated methods of active objects that have been created
|
||||
* with <code>ActiveObject.newInstance(..)</code> (and <code>ActiveObject.newInstance(..)</code>
|
||||
* also publishes <code>@consume</code> annotated methods of typed actors that have been created
|
||||
* with <code>TypedActor.newInstance(..)</code> (and <code>TypedActor.newInstance(..)</code>
|
||||
* on a remote node).
|
||||
*/
|
||||
abstract override def onLoad = {
|
||||
|
|
@ -44,7 +44,7 @@ trait CamelService extends Bootable with Logging {
|
|||
if (!initialized) init
|
||||
if (!started) start
|
||||
|
||||
// start actor that exposes consumer actors and active objects via Camel endpoints
|
||||
// start actor that exposes consumer actors and typed actors via Camel endpoints
|
||||
consumerPublisher.start
|
||||
|
||||
// init publishRequestor so that buffered and future events are delivered to consumerPublisher
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import org.apache.camel.builder.RouteBuilder
|
|||
|
||||
import se.scalablesolutions.akka.actor._
|
||||
import se.scalablesolutions.akka.actor.annotation.consume
|
||||
import se.scalablesolutions.akka.camel.component.ActiveObjectComponent
|
||||
import se.scalablesolutions.akka.camel.component.TypedActorComponent
|
||||
import se.scalablesolutions.akka.util.Logging
|
||||
|
||||
/**
|
||||
|
|
@ -37,7 +37,7 @@ private[camel] object ConsumerPublisher extends Logging {
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates a route to an active object method.
|
||||
* Creates a route to an typed actor method.
|
||||
*/
|
||||
def handleConsumerMethodRegistered(event: ConsumerMethodRegistered) {
|
||||
val targetMethod = event.method.getName
|
||||
|
|
@ -62,7 +62,7 @@ private[camel] object ConsumerPublisher extends Logging {
|
|||
}
|
||||
|
||||
/**
|
||||
* Actor that publishes consumer actors and active object methods at Camel endpoints.
|
||||
* Actor that publishes consumer actors and typed actor methods at Camel endpoints.
|
||||
* The Camel context used for publishing is CamelContextManager.context. This actor
|
||||
* accepts messages of type
|
||||
* se.scalablesolutions.akka.camel.ConsumerRegistered,
|
||||
|
|
@ -111,10 +111,10 @@ private[camel] case class SetExpectedRegistrationCount(num: Int)
|
|||
private[camel] case class SetExpectedUnregistrationCount(num: Int)
|
||||
|
||||
/**
|
||||
* Defines an abstract route to a target which is either an actor or an active object method..
|
||||
* Defines an abstract route to a target which is either an actor or an typed actor method..
|
||||
*
|
||||
* @param endpointUri endpoint URI of the consumer actor or active object method.
|
||||
* @param id actor identifier or active object identifier (registry key).
|
||||
* @param endpointUri endpoint URI of the consumer actor or typed actor method.
|
||||
* @param id actor identifier or typed actor identifier (registry key).
|
||||
*
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
|
|
@ -149,20 +149,20 @@ private[camel] class ConsumerActorRoute(endpointUri: String, uuid: String, block
|
|||
}
|
||||
|
||||
/**
|
||||
* Defines the route to an active object method..
|
||||
* Defines the route to an typed actor method..
|
||||
*
|
||||
* @param endpointUri endpoint URI of the consumer actor method
|
||||
* @param id active object identifier
|
||||
* @param id typed actor identifier
|
||||
* @param method name of the method to invoke.
|
||||
*
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
private[camel] class ConsumerMethodRoute(val endpointUri: String, id: String, method: String) extends ConsumerRoute(endpointUri, id) {
|
||||
protected override def targetUri = "%s:%s?method=%s" format (ActiveObjectComponent.InternalSchema, id, method)
|
||||
protected override def targetUri = "%s:%s?method=%s" format (TypedActorComponent.InternalSchema, id, method)
|
||||
}
|
||||
|
||||
/**
|
||||
* A registration listener that triggers publication of consumer actors and active object
|
||||
* A registration listener that triggers publication of consumer actors and typed actor
|
||||
* methods as well as un-publication of consumer actors. This actor needs to be initialized
|
||||
* with a <code>PublishRequestorInit</code> command message for obtaining a reference to
|
||||
* a <code>publisher</code> actor. Before initialization it buffers all outbound messages
|
||||
|
|
@ -209,7 +209,7 @@ private[camel] class PublishRequestor extends Actor {
|
|||
|
||||
/**
|
||||
* Command message to initialize a PublishRequestor to use <code>consumerPublisher</code>
|
||||
* for publishing actors or active object methods.
|
||||
* for publishing actors or typed actor methods.
|
||||
*/
|
||||
private[camel] case class PublishRequestorInit(consumerPublisher: ActorRef)
|
||||
|
||||
|
|
@ -244,13 +244,13 @@ private[camel] case class ConsumerRegistered(actorRef: ActorRef, uri: String, uu
|
|||
private[camel] case class ConsumerUnregistered(actorRef: ActorRef, uri: String, uuid: String) extends ConsumerEvent
|
||||
|
||||
/**
|
||||
* Event indicating that an active object proxy has been created for a POJO. For each
|
||||
* Event indicating that an typed actor proxy has been created for a POJO. For each
|
||||
* <code>@consume</code> annotated POJO method a separate instance of this class is
|
||||
* created.
|
||||
*
|
||||
* @param activeObject active object (proxy).
|
||||
* @param activeObject typed actor (proxy).
|
||||
* @param init
|
||||
* @param uri endpoint URI of the active object method
|
||||
* @param uri endpoint URI of the typed actor method
|
||||
* @param method method to be published.
|
||||
*
|
||||
* @author Martin Krasser
|
||||
|
|
@ -258,13 +258,13 @@ private[camel] case class ConsumerUnregistered(actorRef: ActorRef, uri: String,
|
|||
private[camel] case class ConsumerMethodRegistered(activeObject: AnyRef, init: AspectInit, uri: String, method: Method) extends ConsumerEvent
|
||||
|
||||
/**
|
||||
* Event indicating that an active object has been stopped. For each
|
||||
* Event indicating that an typed actor has been stopped. For each
|
||||
* <code>@consume</code> annotated POJO method a separate instance of this class is
|
||||
* created.
|
||||
*
|
||||
* @param activeObject active object (proxy).
|
||||
* @param activeObject typed actor (proxy).
|
||||
* @param init
|
||||
* @param uri endpoint URI of the active object method
|
||||
* @param uri endpoint URI of the typed actor method
|
||||
* @param method method to be un-published.
|
||||
*
|
||||
* @author Martin Krasser
|
||||
|
|
@ -308,14 +308,14 @@ private[camel] object ConsumerMethod {
|
|||
/**
|
||||
* Applies a function <code>f</code> to each consumer method of <code>activeObject</code> and
|
||||
* returns the function results as a list. A consumer method is one that is annotated with
|
||||
* <code>@consume</code>. If <code>activeObject</code> is a proxy for a remote active object
|
||||
* <code>@consume</code>. If <code>activeObject</code> is a proxy for a remote typed actor
|
||||
* <code>f</code> is never called and <code>Nil</code> is returned.
|
||||
*/
|
||||
def forConsumer[T](activeObject: AnyRef, init: AspectInit)(f: Method => T): List[T] = {
|
||||
// TODO: support consumer annotation inheritance
|
||||
// - visit overridden methods in superclasses
|
||||
// - visit implemented method declarations in interfaces
|
||||
if (init.remoteAddress.isDefined) Nil // let remote node publish active object methods on endpoints
|
||||
if (init.remoteAddress.isDefined) Nil // let remote node publish typed actor methods on endpoints
|
||||
else for (m <- activeObject.getClass.getMethods.toList; if (m.isAnnotationPresent(classOf[consume])))
|
||||
yield f(m)
|
||||
}
|
||||
|
|
@ -326,8 +326,8 @@ private[camel] object ConsumerMethod {
|
|||
*/
|
||||
private[camel] object ConsumerMethodRegistered {
|
||||
/**
|
||||
* Creates a list of ConsumerMethodRegistered event messages for an active object or an empty
|
||||
* list if the active object is a proxy for an remote active object or the active object doesn't
|
||||
* Creates a list of ConsumerMethodRegistered event messages for an typed actor or an empty
|
||||
* list if the typed actor is a proxy for an remote typed actor or the typed actor doesn't
|
||||
* have any <code>@consume</code> annotated methods.
|
||||
*/
|
||||
def forConsumer(activeObject: AnyRef, init: AspectInit): List[ConsumerMethodRegistered] = {
|
||||
|
|
@ -342,8 +342,8 @@ private[camel] object ConsumerMethodRegistered {
|
|||
*/
|
||||
private[camel] object ConsumerMethodUnregistered {
|
||||
/**
|
||||
* Creates a list of ConsumerMethodUnregistered event messages for an active object or an empty
|
||||
* list if the active object is a proxy for an remote active object or the active object doesn't
|
||||
* Creates a list of ConsumerMethodUnregistered event messages for an typed actor or an empty
|
||||
* list if the typed actor is a proxy for an remote typed actor or the typed actor doesn't
|
||||
* have any <code>@consume</code> annotated methods.
|
||||
*/
|
||||
def forConsumer(activeObject: AnyRef, init: AspectInit): List[ConsumerMethodUnregistered] = {
|
||||
|
|
|
|||
|
|
@ -132,10 +132,8 @@ class ActorProducer(val ep: ActorEndpoint) extends DefaultProducer(ep) with Asyn
|
|||
result match {
|
||||
case Some(msg: Failure) => exchange.fromFailureMessage(msg)
|
||||
case Some(msg) => exchange.fromResponseMessage(Message.canonicalize(msg))
|
||||
case None => {
|
||||
throw new TimeoutException("timeout (%d ms) while waiting response from %s"
|
||||
format (actor.timeout, ep.getEndpointUri))
|
||||
}
|
||||
case None => throw new TimeoutException("timeout (%d ms) while waiting response from %s"
|
||||
format (actor.timeout, ep.getEndpointUri))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,31 +12,31 @@ import org.apache.camel.component.bean._
|
|||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
object ActiveObjectComponent {
|
||||
object TypedActorComponent {
|
||||
/**
|
||||
* Default schema name for active object endpoint URIs.
|
||||
* Default schema name for typed actor endpoint URIs.
|
||||
*/
|
||||
val InternalSchema = "active-object-internal"
|
||||
}
|
||||
|
||||
/**
|
||||
* Camel component for exchanging messages with active objects. This component
|
||||
* tries to obtain the active object from the <code>activeObjectRegistry</code>
|
||||
* Camel component for exchanging messages with typed actors. This component
|
||||
* tries to obtain the typed actor from the <code>activeObjectRegistry</code>
|
||||
* first. If it's not there it tries to obtain it from the CamelContext's registry.
|
||||
*
|
||||
* @see org.apache.camel.component.bean.BeanComponent
|
||||
*
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
class ActiveObjectComponent extends BeanComponent {
|
||||
class TypedActorComponent extends BeanComponent {
|
||||
val activeObjectRegistry = new ConcurrentHashMap[String, AnyRef]
|
||||
|
||||
/**
|
||||
* Creates a {@link org.apache.camel.component.bean.BeanEndpoint} with a custom
|
||||
* bean holder that uses <code>activeObjectRegistry</code> for getting access to
|
||||
* active objects (beans).
|
||||
* typed actors (beans).
|
||||
*
|
||||
* @see se.scalablesolutions.akka.camel.component.ActiveObjectHolder
|
||||
* @see se.scalablesolutions.akka.camel.component.TypedActorHolder
|
||||
*/
|
||||
override def createEndpoint(uri: String, remaining: String, parameters: Map[String, AnyRef]) = {
|
||||
val endpoint = new BeanEndpoint(uri, this)
|
||||
|
|
@ -47,26 +47,26 @@ class ActiveObjectComponent extends BeanComponent {
|
|||
}
|
||||
|
||||
private def createBeanHolder(beanName: String) =
|
||||
new ActiveObjectHolder(activeObjectRegistry, getCamelContext, beanName).createCacheHolder
|
||||
new TypedActorHolder(activeObjectRegistry, getCamelContext, beanName).createCacheHolder
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link org.apache.camel.component.bean.BeanHolder} implementation that uses a custom
|
||||
* registry for getting access to active objects.
|
||||
* registry for getting access to typed actors.
|
||||
*
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
class ActiveObjectHolder(activeObjectRegistry: Map[String, AnyRef], context: CamelContext, name: String)
|
||||
class TypedActorHolder(activeObjectRegistry: Map[String, AnyRef], context: CamelContext, name: String)
|
||||
extends RegistryBean(context, name) {
|
||||
|
||||
/**
|
||||
* Returns an {@link se.scalablesolutions.akka.camel.component.ActiveObjectInfo} instance.
|
||||
* Returns an {@link se.scalablesolutions.akka.camel.component.TypedActorInfo} instance.
|
||||
*/
|
||||
override def getBeanInfo: BeanInfo =
|
||||
new ActiveObjectInfo(getContext, getBean.getClass, getParameterMappingStrategy)
|
||||
new TypedActorInfo(getContext, getBean.getClass, getParameterMappingStrategy)
|
||||
|
||||
/**
|
||||
* Obtains an active object from <code>activeObjectRegistry</code>.
|
||||
* Obtains an typed actor from <code>activeObjectRegistry</code>.
|
||||
*/
|
||||
override def getBean: AnyRef = {
|
||||
val bean = activeObjectRegistry.get(getName)
|
||||
|
|
@ -75,11 +75,11 @@ class ActiveObjectHolder(activeObjectRegistry: Map[String, AnyRef], context: Cam
|
|||
}
|
||||
|
||||
/**
|
||||
* Provides active object meta information.
|
||||
* Provides typed actor meta information.
|
||||
*
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
class ActiveObjectInfo(context: CamelContext, clazz: Class[_], strategy: ParameterMappingStrategy)
|
||||
class TypedActorInfo(context: CamelContext, clazz: Class[_], strategy: ParameterMappingStrategy)
|
||||
extends BeanInfo(context, clazz, strategy) {
|
||||
|
||||
/**
|
||||
|
|
@ -4,11 +4,12 @@ import org.apache.camel.Body;
|
|||
import org.apache.camel.Header;
|
||||
|
||||
import se.scalablesolutions.akka.actor.annotation.consume;
|
||||
import se.scalablesolutions.akka.actor.*;
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
public class PojoBase {
|
||||
public class PojoBase extends TypedActor implements PojoBaseIntf {
|
||||
|
||||
public String m1(String b, String h) {
|
||||
return "m1base: " + b + " " + h;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
package se.scalablesolutions.akka.camel;
|
||||
|
||||
import org.apache.camel.Body;
|
||||
import org.apache.camel.Header;
|
||||
|
||||
import se.scalablesolutions.akka.actor.annotation.consume;
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
public interface PojoBaseIntf {
|
||||
|
||||
public String m1(String b, String h);
|
||||
@consume("direct:m2base")
|
||||
public String m2(@Body String b, @Header("test") String h);
|
||||
@consume("direct:m3base")
|
||||
public String m3(@Body String b, @Header("test") String h);
|
||||
@consume("direct:m4base")
|
||||
public String m4(@Body String b, @Header("test") String h);
|
||||
public void m5(@Body String b, @Header("test") String h);
|
||||
}
|
||||
|
|
@ -4,11 +4,12 @@ import org.apache.camel.Body;
|
|||
import org.apache.camel.Header;
|
||||
|
||||
import se.scalablesolutions.akka.actor.annotation.consume;
|
||||
import se.scalablesolutions.akka.actor.*;
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
public class PojoImpl implements PojoIntf {
|
||||
public class PojoImpl extends TypedActor implements PojoIntf {
|
||||
|
||||
public String m1(String b, String h) {
|
||||
return "m1impl: " + b + " " + h;
|
||||
|
|
@ -18,6 +19,4 @@ public class PojoImpl implements PojoIntf {
|
|||
public String m2(@Body String b, @Header("test") String h) {
|
||||
return "m2impl: " + b + " " + h;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
package se.scalablesolutions.akka.camel;
|
||||
|
||||
import se.scalablesolutions.akka.actor.annotation.consume;
|
||||
import se.scalablesolutions.akka.actor.*;
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
public class Pojo {
|
||||
public class PojoNonConsumer extends TypedActor implements PojoNonConsumerIntf {
|
||||
|
||||
public String foo(String s) {
|
||||
return String.format("foo: %s", s);
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package se.scalablesolutions.akka.camel;
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
public interface PojoNonConsumerIntf {
|
||||
|
||||
public String foo(String s);
|
||||
}
|
||||
|
|
@ -1,15 +1,16 @@
|
|||
package se.scalablesolutions.akka.camel;
|
||||
|
||||
import se.scalablesolutions.akka.actor.annotation.consume;
|
||||
import se.scalablesolutions.akka.actor.*;
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
public class PojoRemote {
|
||||
public class PojoRemote extends TypedActor implements PojoRemoteIntf {
|
||||
|
||||
@consume("direct:remote-active-object")
|
||||
public String foo(String s) {
|
||||
return String.format("remote active object: %s", s);
|
||||
return String.format("remote typed actor: %s", s);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
package se.scalablesolutions.akka.camel;
|
||||
|
||||
import se.scalablesolutions.akka.actor.annotation.consume;
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
public interface PojoRemoteIntf {
|
||||
|
||||
@consume("direct:remote-active-object")
|
||||
public String foo(String s);
|
||||
}
|
||||
|
|
@ -1,11 +1,12 @@
|
|||
package se.scalablesolutions.akka.camel;
|
||||
|
||||
import se.scalablesolutions.akka.actor.annotation.consume;
|
||||
import se.scalablesolutions.akka.actor.*;
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
public class PojoSingle {
|
||||
public class PojoSingle extends TypedActor implements PojoSingleIntf {
|
||||
|
||||
@consume("direct:foo")
|
||||
public void foo(String b) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
package se.scalablesolutions.akka.camel;
|
||||
|
||||
import se.scalablesolutions.akka.actor.annotation.consume;
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
public interface PojoSingleIntf {
|
||||
|
||||
@consume("direct:foo")
|
||||
public void foo(String b);
|
||||
}
|
||||
|
|
@ -4,8 +4,9 @@ import org.apache.camel.Body;
|
|||
import org.apache.camel.Header;
|
||||
|
||||
import se.scalablesolutions.akka.actor.annotation.consume;
|
||||
import se.scalablesolutions.akka.actor.*;
|
||||
|
||||
public class PojoSub extends PojoBase {
|
||||
public class PojoSub extends PojoBase implements PojoSubIntf {
|
||||
|
||||
@Override
|
||||
@consume("direct:m1sub")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
package se.scalablesolutions.akka.camel;
|
||||
|
||||
import org.apache.camel.Body;
|
||||
import org.apache.camel.Header;
|
||||
|
||||
import se.scalablesolutions.akka.actor.annotation.consume;
|
||||
|
||||
public interface PojoSubIntf extends PojoBaseIntf {
|
||||
@consume("direct:m1sub")
|
||||
public String m1(@Body String b, @Header("test") String h);
|
||||
|
||||
@Override
|
||||
public String m2(String b, String h);
|
||||
|
||||
@Override
|
||||
@consume("direct:m3sub")
|
||||
public String m3(@Body String b, @Header("test") String h);
|
||||
}
|
||||
|
|
@ -7,7 +7,7 @@ import org.apache.camel.builder.RouteBuilder
|
|||
import org.scalatest.{GivenWhenThen, BeforeAndAfterAll, FeatureSpec}
|
||||
|
||||
import se.scalablesolutions.akka.actor.Actor._
|
||||
import se.scalablesolutions.akka.actor.{ActiveObject, Actor, ActorRegistry}
|
||||
import se.scalablesolutions.akka.actor.{TypedActor, Actor, ActorRegistry}
|
||||
|
||||
class CamelServiceFeatureTest extends FeatureSpec with BeforeAndAfterAll with GivenWhenThen {
|
||||
import CamelServiceFeatureTest._
|
||||
|
|
@ -116,13 +116,13 @@ class CamelServiceFeatureTest extends FeatureSpec with BeforeAndAfterAll with Gi
|
|||
}
|
||||
}
|
||||
|
||||
feature("Publish active object methods in the global CamelContext") {
|
||||
feature("Publish typed actor methods in the global CamelContext") {
|
||||
|
||||
scenario("access active object methods via Camel direct-endpoints") {
|
||||
scenario("access typed actor methods via Camel direct-endpoints") {
|
||||
|
||||
given("an active object registered after CamelService startup")
|
||||
given("an typed actor registered after CamelService startup")
|
||||
var latch = service.expectEndpointActivationCount(3)
|
||||
val obj = ActiveObject.newInstance(classOf[PojoBase])
|
||||
val obj = TypedActor.newInstance(classOf[PojoBaseIntf], classOf[PojoBase])
|
||||
assert(latch.await(5000, TimeUnit.MILLISECONDS))
|
||||
|
||||
when("requests are sent to published methods")
|
||||
|
|
@ -137,23 +137,23 @@ class CamelServiceFeatureTest extends FeatureSpec with BeforeAndAfterAll with Gi
|
|||
|
||||
// cleanup to avoid conflicts with next test (i.e. avoid multiple consumers on direct-endpoints)
|
||||
latch = service.expectEndpointDeactivationCount(3)
|
||||
ActiveObject.stop(obj)
|
||||
TypedActor.stop(obj)
|
||||
assert(latch.await(5000, TimeUnit.MILLISECONDS))
|
||||
}
|
||||
}
|
||||
|
||||
feature("Unpublish active object method from the global CamelContext") {
|
||||
feature("Unpublish typed actor method from the global CamelContext") {
|
||||
|
||||
scenario("access to unregistered active object method via Camel direct-endpoint fails") {
|
||||
scenario("access to unregistered typed actor method via Camel direct-endpoint fails") {
|
||||
|
||||
given("an active object registered after CamelService startup")
|
||||
given("an typed actor registered after CamelService startup")
|
||||
var latch = service.expectEndpointActivationCount(3)
|
||||
val obj = ActiveObject.newInstance(classOf[PojoBase])
|
||||
val obj = TypedActor.newInstance(classOf[PojoBaseIntf], classOf[PojoBase])
|
||||
assert(latch.await(5000, TimeUnit.MILLISECONDS))
|
||||
|
||||
when("the active object is stopped")
|
||||
when("the typed actor is stopped")
|
||||
latch = service.expectEndpointDeactivationCount(3)
|
||||
ActiveObject.stop(obj)
|
||||
TypedActor.stop(obj)
|
||||
assert(latch.await(5000, TimeUnit.MILLISECONDS))
|
||||
|
||||
then("the associated endpoints aren't accessible any more")
|
||||
|
|
@ -180,7 +180,7 @@ object CamelServiceFeatureTest {
|
|||
}
|
||||
|
||||
class TestBlocker(uri: String) extends Actor with Consumer {
|
||||
self.timeout = 1
|
||||
self.timeout = 1000
|
||||
def endpointUri = uri
|
||||
override def blocking = true
|
||||
protected def receive = {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import java.net.InetSocketAddress
|
|||
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
|
||||
import se.scalablesolutions.akka.actor.{AspectInit, ActiveObject}
|
||||
import se.scalablesolutions.akka.actor.{AspectInit, TypedActor}
|
||||
import se.scalablesolutions.akka.camel.ConsumerMethodRegistered._
|
||||
import org.junit.{AfterClass, Test}
|
||||
|
||||
|
|
@ -12,8 +12,8 @@ class ConsumerMethodRegisteredTest extends JUnitSuite {
|
|||
import ConsumerMethodRegisteredTest._
|
||||
|
||||
val remoteAddress = new InetSocketAddress("localhost", 8888);
|
||||
val remoteAspectInit = AspectInit(classOf[String], null, Some(remoteAddress), 1000)
|
||||
val localAspectInit = AspectInit(classOf[String], null, None, 1000)
|
||||
val remoteAspectInit = AspectInit(classOf[String], null, null, Some(remoteAddress), 1000)
|
||||
val localAspectInit = AspectInit(classOf[String], null, null, None, 1000)
|
||||
|
||||
val ascendingMethodName = (r1: ConsumerMethodRegistered, r2: ConsumerMethodRegistered) =>
|
||||
r1.method.getName < r2.method.getName
|
||||
|
|
@ -44,14 +44,14 @@ class ConsumerMethodRegisteredTest extends JUnitSuite {
|
|||
}
|
||||
|
||||
object ConsumerMethodRegisteredTest {
|
||||
val activePojoBase = ActiveObject.newInstance(classOf[PojoBase])
|
||||
val activePojoSub = ActiveObject.newInstance(classOf[PojoSub])
|
||||
val activePojoIntf = ActiveObject.newInstance(classOf[PojoIntf], new PojoImpl)
|
||||
val activePojoBase = TypedActor.newInstance(classOf[PojoBaseIntf], classOf[PojoBase])
|
||||
val activePojoSub = TypedActor.newInstance(classOf[PojoSubIntf], classOf[PojoSub])
|
||||
val activePojoIntf = TypedActor.newInstance(classOf[PojoIntf], classOf[PojoImpl])
|
||||
|
||||
@AfterClass
|
||||
def afterClass = {
|
||||
ActiveObject.stop(activePojoBase)
|
||||
ActiveObject.stop(activePojoSub)
|
||||
ActiveObject.stop(activePojoIntf)
|
||||
TypedActor.stop(activePojoBase)
|
||||
TypedActor.stop(activePojoSub)
|
||||
TypedActor.stop(activePojoIntf)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,8 +32,8 @@ class PublishRequestorTest extends JUnitSuite {
|
|||
}
|
||||
|
||||
@Test def shouldReceiveConsumerMethodRegisteredEvent = {
|
||||
val obj = ActiveObject.newInstance(classOf[PojoSingle])
|
||||
val init = AspectInit(classOf[PojoSingle], null, None, 1000)
|
||||
val obj = TypedActor.newInstance(classOf[PojoSingleIntf], classOf[PojoSingle])
|
||||
val init = AspectInit(classOf[PojoSingleIntf], null, null, None, 1000)
|
||||
val latch = (publisher !! SetExpectedTestMessageCount(1)).as[CountDownLatch].get
|
||||
requestor ! AspectInitRegistered(obj, init)
|
||||
assert(latch.await(5000, TimeUnit.MILLISECONDS))
|
||||
|
|
@ -45,8 +45,8 @@ class PublishRequestorTest extends JUnitSuite {
|
|||
}
|
||||
|
||||
@Test def shouldReceiveConsumerMethodUnregisteredEvent = {
|
||||
val obj = ActiveObject.newInstance(classOf[PojoSingle])
|
||||
val init = AspectInit(classOf[PojoSingle], null, None, 1000)
|
||||
val obj = TypedActor.newInstance(classOf[PojoSingleIntf], classOf[PojoSingle])
|
||||
val init = AspectInit(classOf[PojoSingleIntf], null, null, None, 1000)
|
||||
val latch = (publisher !! SetExpectedTestMessageCount(1)).as[CountDownLatch].get
|
||||
requestor ! AspectInitUnregistered(obj, init)
|
||||
assert(latch.await(5000, TimeUnit.MILLISECONDS))
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import java.util.concurrent.{CountDownLatch, TimeUnit}
|
|||
import org.scalatest.{GivenWhenThen, BeforeAndAfterAll, FeatureSpec}
|
||||
|
||||
import se.scalablesolutions.akka.actor.Actor._
|
||||
import se.scalablesolutions.akka.actor.{ActiveObject, ActorRegistry, RemoteActor}
|
||||
import se.scalablesolutions.akka.actor.{TypedActor, ActorRegistry, RemoteActor}
|
||||
import se.scalablesolutions.akka.remote.{RemoteClient, RemoteServer}
|
||||
|
||||
/**
|
||||
|
|
@ -55,10 +55,10 @@ class RemoteConsumerTest extends FeatureSpec with BeforeAndAfterAll with GivenWh
|
|||
}
|
||||
}
|
||||
|
||||
feature("Client-initiated remote consumer active object") {
|
||||
feature("Client-initiated remote consumer typed actor") {
|
||||
scenario("access published remote consumer method") {
|
||||
given("a client-initiated remote consumer active object")
|
||||
val consumer = ActiveObject.newRemoteInstance(classOf[PojoRemote], host, port)
|
||||
given("a client-initiated remote consumer typed actor")
|
||||
val consumer = TypedActor.newRemoteInstance(classOf[PojoRemoteIntf], classOf[PojoRemote], host, port)
|
||||
|
||||
when("remote consumer publication is triggered")
|
||||
var latch = service.expectEndpointActivationCount(1)
|
||||
|
|
@ -67,7 +67,7 @@ class RemoteConsumerTest extends FeatureSpec with BeforeAndAfterAll with GivenWh
|
|||
|
||||
then("the published method is accessible via its endpoint URI")
|
||||
val response = CamelContextManager.template.requestBody("direct:remote-active-object", "test")
|
||||
assert(response === "remote active object: test")
|
||||
assert(response === "remote typed actor: test")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import org.scalatest.{BeforeAndAfterEach, BeforeAndAfterAll, FeatureSpec}
|
|||
|
||||
import org.apache.camel.builder.RouteBuilder
|
||||
import se.scalablesolutions.akka.actor.Actor._
|
||||
import se.scalablesolutions.akka.actor.{ActorRegistry, ActiveObject}
|
||||
import se.scalablesolutions.akka.actor.{ActorRegistry, TypedActor}
|
||||
import se.scalablesolutions.akka.camel._
|
||||
import org.apache.camel.impl.{DefaultCamelContext, SimpleRegistry}
|
||||
import org.apache.camel.{ResolveEndpointFailedException, ExchangePattern, Exchange, Processor}
|
||||
|
|
@ -12,14 +12,14 @@ import org.apache.camel.{ResolveEndpointFailedException, ExchangePattern, Exchan
|
|||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
class ActiveObjectComponentFeatureTest extends FeatureSpec with BeforeAndAfterAll with BeforeAndAfterEach {
|
||||
import ActiveObjectComponentFeatureTest._
|
||||
class TypedActorComponentFeatureTest extends FeatureSpec with BeforeAndAfterAll with BeforeAndAfterEach {
|
||||
import TypedActorComponentFeatureTest._
|
||||
import CamelContextManager.template
|
||||
|
||||
override protected def beforeAll = {
|
||||
val activePojo = ActiveObject.newInstance(classOf[Pojo]) // not a consumer
|
||||
val activePojoBase = ActiveObject.newInstance(classOf[PojoBase])
|
||||
val activePojoIntf = ActiveObject.newInstance(classOf[PojoIntf], new PojoImpl)
|
||||
val activePojo = TypedActor.newInstance(classOf[PojoNonConsumerIntf], classOf[PojoNonConsumer]) // not a consumer
|
||||
val activePojoBase = TypedActor.newInstance(classOf[PojoBaseIntf], classOf[PojoBase])
|
||||
val activePojoIntf = TypedActor.newInstance(classOf[PojoIntf], classOf[PojoImpl])
|
||||
|
||||
val registry = new SimpleRegistry
|
||||
registry.put("pojo", activePojo)
|
||||
|
|
@ -37,8 +37,8 @@ class ActiveObjectComponentFeatureTest extends FeatureSpec with BeforeAndAfterAl
|
|||
ActorRegistry.shutdownAll
|
||||
}
|
||||
|
||||
feature("Communicate with an active object from a Camel application using active object endpoint URIs") {
|
||||
import ActiveObjectComponent.InternalSchema
|
||||
feature("Communicate with an typed actor from a Camel application using typed actor endpoint URIs") {
|
||||
import TypedActorComponent.InternalSchema
|
||||
import ExchangePattern._
|
||||
|
||||
scenario("in-out exchange with proxy created from interface and method returning String") {
|
||||
|
|
@ -81,14 +81,14 @@ class ActiveObjectComponentFeatureTest extends FeatureSpec with BeforeAndAfterAl
|
|||
}
|
||||
}
|
||||
|
||||
feature("Communicate with an active object from a Camel application from a custom Camel route") {
|
||||
feature("Communicate with an typed actor from a Camel application from a custom Camel route") {
|
||||
|
||||
scenario("in-out exchange with externally registered active object") {
|
||||
scenario("in-out exchange with externally registered typed actor") {
|
||||
val result = template.requestBody("direct:test", "test")
|
||||
assert(result === "foo: test")
|
||||
}
|
||||
|
||||
scenario("in-out exchange with internally registered active object not possible") {
|
||||
scenario("in-out exchange with internally registered typed actor not possible") {
|
||||
intercept[ResolveEndpointFailedException] {
|
||||
template.requestBodyAndHeader("active-object:intf?method=m2", "x", "test", "y")
|
||||
}
|
||||
|
|
@ -96,7 +96,7 @@ class ActiveObjectComponentFeatureTest extends FeatureSpec with BeforeAndAfterAl
|
|||
}
|
||||
}
|
||||
|
||||
object ActiveObjectComponentFeatureTest {
|
||||
object TypedActorComponentFeatureTest {
|
||||
class CustomRouteBuilder extends RouteBuilder {
|
||||
def configure = {
|
||||
from("direct:test").to("active-object:pojo?method=foo")
|
||||
|
|
@ -13,10 +13,10 @@ import com.google.inject.Singleton;
|
|||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
public class ActiveObjectGuiceModule extends AbstractModule {
|
||||
public class TypedActorGuiceModule extends AbstractModule {
|
||||
private final List<DependencyBinding> bindings;
|
||||
|
||||
public ActiveObjectGuiceModule(final List<DependencyBinding> bindings) {
|
||||
public TypedActorGuiceModule(final List<DependencyBinding> bindings) {
|
||||
this.bindings = bindings;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -52,21 +52,35 @@ message MessageProtocol {
|
|||
optional bytes messageManifest = 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the actor info.
|
||||
*/
|
||||
message ActorInfoProtocol {
|
||||
required string uuid = 1;
|
||||
required string target = 2;
|
||||
required uint64 timeout = 3;
|
||||
required ActorType actorType = 4;
|
||||
optional TypedActorInfoProtocol typedActorInfo = 5;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the typed actor extra info.
|
||||
*/
|
||||
message TypedActorInfoProtocol {
|
||||
required string interface = 1;
|
||||
required string method = 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a remote message request.
|
||||
*/
|
||||
message RemoteRequestProtocol {
|
||||
required uint64 id = 1;
|
||||
required MessageProtocol message = 2;
|
||||
optional string method = 3;
|
||||
required string target = 4;
|
||||
required string uuid = 5;
|
||||
required uint64 timeout = 6;
|
||||
optional string supervisorUuid = 7;
|
||||
required bool isActor = 8;
|
||||
required bool isOneWay = 9;
|
||||
required bool isEscaped = 10;
|
||||
optional RemoteActorRefProtocol sender = 11;
|
||||
required ActorInfoProtocol actorInfo = 3;
|
||||
required bool isOneWay = 4;
|
||||
optional string supervisorUuid = 5;
|
||||
optional RemoteActorRefProtocol sender = 6;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -81,6 +95,15 @@ message RemoteReplyProtocol {
|
|||
required bool isSuccessful = 6;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the actor type.
|
||||
*/
|
||||
enum ActorType {
|
||||
SCALA_ACTOR = 1;
|
||||
JAVA_ACTOR = 2;
|
||||
TYPED_ACTOR = 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the serialization scheme used to serialize the message and/or Actor instance.
|
||||
*/
|
||||
|
|
@ -117,6 +140,8 @@ message LifeCycleProtocol {
|
|||
required LifeCycleType lifeCycle = 1;
|
||||
optional string preRestart = 2;
|
||||
optional string postRestart = 3;
|
||||
optional string init = 4;
|
||||
optional string shutdown = 5;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<aspectwerkz>
|
||||
<system id="akka">
|
||||
<package name="se.scalablesolutions.akka.actor">
|
||||
<aspect class="ActiveObjectAspect" />
|
||||
<aspect class="TypedActorAspect" />
|
||||
</package>
|
||||
</system>
|
||||
</aspectwerkz>
|
||||
|
|
|
|||
|
|
@ -1,853 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
|
||||
package se.scalablesolutions.akka.actor
|
||||
|
||||
import Actor._
|
||||
import se.scalablesolutions.akka.config.FaultHandlingStrategy
|
||||
import se.scalablesolutions.akka.remote.protocol.RemoteProtocol.RemoteRequestProtocol
|
||||
import se.scalablesolutions.akka.remote.{MessageSerializer, RemoteClient, RemoteRequestProtocolIdFactory}
|
||||
import se.scalablesolutions.akka.dispatch.{MessageDispatcher, Future, CompletableFuture}
|
||||
import se.scalablesolutions.akka.config.ScalaConfig._
|
||||
import se.scalablesolutions.akka.serialization.Serializer
|
||||
import se.scalablesolutions.akka.util._
|
||||
|
||||
import org.codehaus.aspectwerkz.joinpoint.{MethodRtti, JoinPoint}
|
||||
import org.codehaus.aspectwerkz.proxy.Proxy
|
||||
import org.codehaus.aspectwerkz.annotation.{Aspect, Around}
|
||||
|
||||
import java.net.InetSocketAddress
|
||||
import java.lang.reflect.{InvocationTargetException, Method}
|
||||
|
||||
object Annotations {
|
||||
import se.scalablesolutions.akka.actor.annotation._
|
||||
val transactionrequired = classOf[transactionrequired]
|
||||
val prerestart = classOf[prerestart]
|
||||
val postrestart = classOf[postrestart]
|
||||
val shutdown = classOf[shutdown]
|
||||
val inittransactionalstate = classOf[inittransactionalstate]
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration factory for Active Objects.
|
||||
*
|
||||
* FIXDOC: document ActiveObjectConfiguration
|
||||
*/
|
||||
final class ActiveObjectConfiguration {
|
||||
private[akka] var _timeout: Long = Actor.TIMEOUT
|
||||
private[akka] var _restartCallbacks: Option[RestartCallbacks] = None
|
||||
private[akka] var _shutdownCallback: Option[ShutdownCallback] = None
|
||||
private[akka] var _transactionRequired = false
|
||||
private[akka] var _host: Option[InetSocketAddress] = None
|
||||
private[akka] var _messageDispatcher: Option[MessageDispatcher] = None
|
||||
|
||||
def timeout = _timeout
|
||||
def timeout(timeout: Duration) : ActiveObjectConfiguration = {
|
||||
_timeout = timeout.toMillis
|
||||
this
|
||||
}
|
||||
|
||||
def restartCallbacks(pre: String, post: String) : ActiveObjectConfiguration = {
|
||||
_restartCallbacks = Some(new RestartCallbacks(pre, post))
|
||||
this
|
||||
}
|
||||
|
||||
def shutdownCallback(down: String) : ActiveObjectConfiguration = {
|
||||
_shutdownCallback = Some(new ShutdownCallback(down))
|
||||
this
|
||||
}
|
||||
|
||||
def makeTransactionRequired() : ActiveObjectConfiguration = {
|
||||
_transactionRequired = true;
|
||||
this
|
||||
}
|
||||
|
||||
def makeRemote(hostname: String, port: Int) : ActiveObjectConfiguration = {
|
||||
_host = Some(new InetSocketAddress(hostname, port))
|
||||
this
|
||||
}
|
||||
|
||||
def dispatcher(messageDispatcher: MessageDispatcher) : ActiveObjectConfiguration = {
|
||||
_messageDispatcher = Some(messageDispatcher)
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds RTTI (runtime type information) for the Active Object, f.e. current 'sender'
|
||||
* reference, the 'senderFuture' reference etc.
|
||||
* <p/>
|
||||
* In order to make use of this context you have to create a member field in your
|
||||
* Active Object that has the type 'ActiveObjectContext', then an instance will
|
||||
* be injected for you to use.
|
||||
* <p/>
|
||||
* This class does not contain static information but is updated by the runtime system
|
||||
* at runtime.
|
||||
* <p/>
|
||||
* Here is an example of usage:
|
||||
* <pre>
|
||||
* class Ping {
|
||||
* // This context will be injected, holds RTTI (runtime type information)
|
||||
* // for the current message send
|
||||
* private ActiveObjectContext context = null;
|
||||
*
|
||||
* public void hit(int count) {
|
||||
* Pong pong = (Pong) context.getSender();
|
||||
* pong.hit(count++)
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
final class ActiveObjectContext {
|
||||
private[akka] var _sender: AnyRef = _
|
||||
private[akka] var _senderFuture: CompletableFuture[Any] = _
|
||||
|
||||
/**
|
||||
* Returns the current sender Active Object reference.
|
||||
* Scala style getter.
|
||||
*/
|
||||
def sender: AnyRef = {
|
||||
if (_sender eq null) throw new IllegalActorStateException("Sender reference should not be null.")
|
||||
else _sender
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current sender Active Object reference.
|
||||
* Java style getter.
|
||||
*/
|
||||
def getSender: AnyRef = {
|
||||
if (_sender eq null) throw new IllegalActorStateException("Sender reference should not be null.")
|
||||
else _sender
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current sender future Active Object reference.
|
||||
* Scala style getter.
|
||||
*/
|
||||
def senderFuture: Option[CompletableFuture[Any]] = if (_senderFuture eq null) None else Some(_senderFuture)
|
||||
|
||||
/**
|
||||
* Returns the current sender future Active Object reference.
|
||||
* Java style getter.
|
||||
* This method returns 'null' if the sender future is not available.
|
||||
*/
|
||||
def getSenderFuture = _senderFuture
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal helper class to help pass the contextual information between threads.
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
private[akka] object ActiveObjectContext {
|
||||
import scala.util.DynamicVariable
|
||||
private[actor] val sender = new DynamicVariable[AnyRef](null)
|
||||
private[actor] val senderFuture = new DynamicVariable[CompletableFuture[Any]](null)
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory class for creating Active Objects out of plain POJOs and/or POJOs with interfaces.
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
object ActiveObject extends Logging {
|
||||
import Actor.actorOf
|
||||
|
||||
val AKKA_CAMEL_ROUTING_SCHEME = "akka"
|
||||
private[actor] val AW_PROXY_PREFIX = "$$ProxiedByAW".intern
|
||||
|
||||
def newInstance[T](target: Class[T], timeout: Long): T =
|
||||
newInstance(target, actorOf(new Dispatcher(false)), None, timeout)
|
||||
|
||||
def newInstance[T](target: Class[T]): T =
|
||||
newInstance(target, actorOf(new Dispatcher(false)), None, Actor.TIMEOUT)
|
||||
|
||||
def newInstance[T](intf: Class[T], target: AnyRef, timeout: Long): T =
|
||||
newInstance(intf, target, actorOf(new Dispatcher(false)), None, timeout)
|
||||
|
||||
def newInstance[T](intf: Class[T], target: AnyRef): T =
|
||||
newInstance(intf, target, actorOf(new Dispatcher(false)), None, Actor.TIMEOUT)
|
||||
|
||||
def newRemoteInstance[T](target: Class[T], timeout: Long, hostname: String, port: Int): T =
|
||||
newInstance(target, actorOf(new Dispatcher(false)), Some(new InetSocketAddress(hostname, port)), timeout)
|
||||
|
||||
def newRemoteInstance[T](target: Class[T], hostname: String, port: Int): T =
|
||||
newInstance(target, actorOf(new Dispatcher(false)), Some(new InetSocketAddress(hostname, port)), Actor.TIMEOUT)
|
||||
|
||||
def newInstance[T](target: Class[T], config: ActiveObjectConfiguration): T = {
|
||||
val actor = actorOf(new Dispatcher(config._transactionRequired, config._restartCallbacks, config._shutdownCallback))
|
||||
if (config._messageDispatcher.isDefined) {
|
||||
actor.dispatcher = config._messageDispatcher.get
|
||||
}
|
||||
newInstance(target, actor, config._host, config.timeout)
|
||||
}
|
||||
|
||||
def newInstance[T](intf: Class[T], target: AnyRef, config: ActiveObjectConfiguration): T = {
|
||||
val actor = actorOf(new Dispatcher(config._transactionRequired, config._restartCallbacks, config._shutdownCallback))
|
||||
if (config._messageDispatcher.isDefined) {
|
||||
actor.dispatcher = config._messageDispatcher.get
|
||||
}
|
||||
newInstance(intf, target, actor, config._host, config.timeout)
|
||||
}
|
||||
|
||||
@deprecated("use newInstance(target: Class[T], config: ActiveObjectConfiguration) instead")
|
||||
def newInstance[T](target: Class[T], timeout: Long, restartCallbacks: Option[RestartCallbacks]): T =
|
||||
newInstance(target, actorOf(new Dispatcher(false, restartCallbacks)), None, timeout)
|
||||
|
||||
@deprecated("use newInstance(intf: Class[T], target: AnyRef, config: ActiveObjectConfiguration) instead")
|
||||
def newInstance[T](intf: Class[T], target: AnyRef, timeout: Long, restartCallbacks: Option[RestartCallbacks]): T =
|
||||
newInstance(intf, target, actorOf(new Dispatcher(false, restartCallbacks)), None, timeout)
|
||||
|
||||
@deprecated("use newInstance(target: Class[T], config: ActiveObjectConfiguration) instead")
|
||||
def newInstance[T](target: Class[T], timeout: Long, transactionRequired: Boolean): T =
|
||||
newInstance(target, actorOf(new Dispatcher(transactionRequired, None)), None, timeout)
|
||||
|
||||
@deprecated("use newInstance(target: Class[T], config: ActiveObjectConfiguration) instead")
|
||||
def newInstance[T](target: Class[T], timeout: Long, transactionRequired: Boolean, restartCallbacks: Option[RestartCallbacks]): T =
|
||||
newInstance(target, actorOf(new Dispatcher(transactionRequired, restartCallbacks)), None, timeout)
|
||||
|
||||
@deprecated("use newInstance(intf: Class[T], target: AnyRef, config: ActiveObjectConfiguration) instead")
|
||||
def newInstance[T](intf: Class[T], target: AnyRef, timeout: Long, transactionRequired: Boolean): T =
|
||||
newInstance(intf, target, actorOf(new Dispatcher(transactionRequired, None)), None, timeout)
|
||||
|
||||
@deprecated("use newInstance(intf: Class[T], target: AnyRef, config: ActiveObjectConfiguration) instead")
|
||||
def newInstance[T](intf: Class[T], target: AnyRef, timeout: Long, transactionRequired: Boolean, restartCallbacks: Option[RestartCallbacks]): T =
|
||||
newInstance(intf, target, actorOf(new Dispatcher(transactionRequired, restartCallbacks)), None, timeout)
|
||||
|
||||
@deprecated("use newInstance(intf: Class[T], target: AnyRef, config: ActiveObjectConfiguration) instead")
|
||||
def newRemoteInstance[T](intf: Class[T], target: AnyRef, timeout: Long, hostname: String, port: Int): T =
|
||||
newInstance(intf, target, actorOf(new Dispatcher(false, None)), Some(new InetSocketAddress(hostname, port)), timeout)
|
||||
|
||||
@deprecated("use newInstance(intf: Class[T], target: AnyRef, config: ActiveObjectConfiguration) instead")
|
||||
def newRemoteInstance[T](intf: Class[T], target: AnyRef, timeout: Long, hostname: String, port: Int, restartCallbacks: Option[RestartCallbacks]): T =
|
||||
newInstance(intf, target, actorOf(new Dispatcher(false, restartCallbacks)), Some(new InetSocketAddress(hostname, port)), timeout)
|
||||
|
||||
@deprecated("use newInstance(target: Class[T], config: ActiveObjectConfiguration) instead")
|
||||
def newRemoteInstance[T](target: Class[T], timeout: Long, transactionRequired: Boolean, hostname: String, port: Int): T =
|
||||
newInstance(target, actorOf(new Dispatcher(transactionRequired, None)), Some(new InetSocketAddress(hostname, port)), timeout)
|
||||
|
||||
@deprecated("use newInstance(target: Class[T], config: ActiveObjectConfiguration) instead")
|
||||
def newRemoteInstance[T](target: Class[T], timeout: Long, transactionRequired: Boolean, hostname: String, port: Int, restartCallbacks: Option[RestartCallbacks]): T =
|
||||
newInstance(target, actorOf(new Dispatcher(transactionRequired, restartCallbacks)), Some(new InetSocketAddress(hostname, port)), timeout)
|
||||
|
||||
@deprecated("use newInstance(intf: Class[T], target: AnyRef, config: ActiveObjectConfiguration) instead")
|
||||
def newRemoteInstance[T](intf: Class[T], target: AnyRef, timeout: Long, transactionRequired: Boolean, hostname: String, port: Int): T =
|
||||
newInstance(intf, target, actorOf(new Dispatcher(transactionRequired, None)), Some(new InetSocketAddress(hostname, port)), timeout)
|
||||
|
||||
@deprecated("use newInstance(intf: Class[T], target: AnyRef, config: ActiveObjectConfiguration) instead")
|
||||
def newRemoteInstance[T](intf: Class[T], target: AnyRef, timeout: Long, transactionRequired: Boolean, hostname: String, port: Int, restartCallbacks: Option[RestartCallbacks]): T =
|
||||
newInstance(intf, target, actorOf(new Dispatcher(transactionRequired, restartCallbacks)), Some(new InetSocketAddress(hostname, port)), timeout)
|
||||
|
||||
@deprecated("use newInstance(target: Class[T], config: ActiveObjectConfiguration) instead")
|
||||
def newInstance[T](target: Class[T], timeout: Long, dispatcher: MessageDispatcher): T = {
|
||||
val actor = actorOf(new Dispatcher(false, None))
|
||||
actor.dispatcher = dispatcher
|
||||
newInstance(target, actor, None, timeout)
|
||||
}
|
||||
|
||||
@deprecated("use newInstance(target: Class[T], config: ActiveObjectConfiguration) instead")
|
||||
def newInstance[T](target: Class[T], timeout: Long, dispatcher: MessageDispatcher, restartCallbacks: Option[RestartCallbacks]): T = {
|
||||
val actor = actorOf(new Dispatcher(false, restartCallbacks))
|
||||
actor.dispatcher = dispatcher
|
||||
newInstance(target, actor, None, timeout)
|
||||
}
|
||||
|
||||
@deprecated("use newInstance(intf: Class[T], target: AnyRef, config: ActiveObjectConfiguration) instead")
|
||||
def newInstance[T](intf: Class[T], target: AnyRef, timeout: Long, dispatcher: MessageDispatcher): T = {
|
||||
val actor = actorOf(new Dispatcher(false, None))
|
||||
actor.dispatcher = dispatcher
|
||||
newInstance(intf, target, actor, None, timeout)
|
||||
}
|
||||
|
||||
@deprecated("use newInstance(intf: Class[T], target: AnyRef, config: ActiveObjectConfiguration) instead")
|
||||
def newInstance[T](intf: Class[T], target: AnyRef, timeout: Long,
|
||||
dispatcher: MessageDispatcher, restartCallbacks: Option[RestartCallbacks]): T = {
|
||||
val actor = actorOf(new Dispatcher(false, restartCallbacks))
|
||||
actor.dispatcher = dispatcher
|
||||
newInstance(intf, target, actor, None, timeout)
|
||||
}
|
||||
|
||||
@deprecated("use newInstance(target: Class[T], config: ActiveObjectConfiguration) instead")
|
||||
def newInstance[T](target: Class[T], timeout: Long, transactionRequired: Boolean, dispatcher: MessageDispatcher): T = {
|
||||
val actor = actorOf(new Dispatcher(transactionRequired, None))
|
||||
actor.dispatcher = dispatcher
|
||||
newInstance(target, actor, None, timeout)
|
||||
}
|
||||
|
||||
@deprecated("use newInstance(target: Class[T], config: ActiveObjectConfiguration) instead")
|
||||
def newInstance[T](target: Class[T], timeout: Long, transactionRequired: Boolean,
|
||||
dispatcher: MessageDispatcher, restartCallbacks: Option[RestartCallbacks]): T = {
|
||||
val actor = actorOf(new Dispatcher(transactionRequired, restartCallbacks))
|
||||
actor.dispatcher = dispatcher
|
||||
newInstance(target, actor, None, timeout)
|
||||
}
|
||||
|
||||
@deprecated("use newInstance(intf: Class[T], target: AnyRef, config: ActiveObjectConfiguration) instead")
|
||||
def newInstance[T](intf: Class[T], target: AnyRef, timeout: Long, transactionRequired: Boolean, dispatcher: MessageDispatcher): T = {
|
||||
val actor = actorOf(new Dispatcher(transactionRequired, None))
|
||||
actor.dispatcher = dispatcher
|
||||
newInstance(intf, target, actor, None, timeout)
|
||||
}
|
||||
|
||||
@deprecated("use newInstance(intf: Class[T], target: AnyRef, config: ActiveObjectConfiguration) instead")
|
||||
def newInstance[T](intf: Class[T], target: AnyRef, timeout: Long, transactionRequired: Boolean,
|
||||
dispatcher: MessageDispatcher, restartCallbacks: Option[RestartCallbacks]): T = {
|
||||
val actor = actorOf(new Dispatcher(transactionRequired, restartCallbacks))
|
||||
actor.dispatcher = dispatcher
|
||||
newInstance(intf, target, actor, None, timeout)
|
||||
}
|
||||
|
||||
@deprecated("use newInstance(target: Class[T], config: ActiveObjectConfiguration) instead")
|
||||
def newRemoteInstance[T](target: Class[T], timeout: Long, dispatcher: MessageDispatcher, hostname: String, port: Int): T = {
|
||||
val actor = actorOf(new Dispatcher(false, None))
|
||||
actor.dispatcher = dispatcher
|
||||
newInstance(target, actor, Some(new InetSocketAddress(hostname, port)), timeout)
|
||||
}
|
||||
|
||||
@deprecated("use newInstance(target: Class[T], config: ActiveObjectConfiguration) instead")
|
||||
def newRemoteInstance[T](target: Class[T], timeout: Long, dispatcher: MessageDispatcher,
|
||||
hostname: String, port: Int, restartCallbacks: Option[RestartCallbacks]): T = {
|
||||
val actor = actorOf(new Dispatcher(false, restartCallbacks))
|
||||
actor.dispatcher = dispatcher
|
||||
newInstance(target, actor, Some(new InetSocketAddress(hostname, port)), timeout)
|
||||
}
|
||||
|
||||
@deprecated("use newInstance(intf: Class[T], target: AnyRef, config: ActiveObjectConfiguration) instead")
|
||||
def newRemoteInstance[T](intf: Class[T], target: AnyRef, timeout: Long, dispatcher: MessageDispatcher, hostname: String, port: Int): T = {
|
||||
val actor = actorOf(new Dispatcher(false, None))
|
||||
actor.dispatcher = dispatcher
|
||||
newInstance(intf, target, actor, Some(new InetSocketAddress(hostname, port)), timeout)
|
||||
}
|
||||
|
||||
@deprecated("use newInstance(intf: Class[T], target: AnyRef, config: ActiveObjectConfiguration) instead")
|
||||
def newRemoteInstance[T](intf: Class[T], target: AnyRef, timeout: Long, dispatcher: MessageDispatcher,
|
||||
hostname: String, port: Int, restartCallbacks: Option[RestartCallbacks]): T = {
|
||||
val actor = actorOf(new Dispatcher(false, restartCallbacks))
|
||||
actor.dispatcher = dispatcher
|
||||
newInstance(intf, target, actor, Some(new InetSocketAddress(hostname, port)), timeout)
|
||||
}
|
||||
|
||||
@deprecated("use newInstance(target: Class[T], config: ActiveObjectConfiguration) instead")
|
||||
def newRemoteInstance[T](target: Class[T], timeout: Long, transactionRequired: Boolean,
|
||||
dispatcher: MessageDispatcher, hostname: String, port: Int): T = {
|
||||
val actor = actorOf(new Dispatcher(transactionRequired, None))
|
||||
actor.dispatcher = dispatcher
|
||||
newInstance(target, actor, Some(new InetSocketAddress(hostname, port)), timeout)
|
||||
}
|
||||
|
||||
@deprecated("use newInstance(target: Class[T], config: ActiveObjectConfiguration) instead")
|
||||
def newRemoteInstance[T](target: Class[T], timeout: Long, transactionRequired: Boolean, dispatcher: MessageDispatcher,
|
||||
hostname: String, port: Int, restartCallbacks: Option[RestartCallbacks]): T = {
|
||||
val actor = actorOf(new Dispatcher(transactionRequired, restartCallbacks))
|
||||
actor.dispatcher = dispatcher
|
||||
newInstance(target, actor, Some(new InetSocketAddress(hostname, port)), timeout)
|
||||
}
|
||||
|
||||
@deprecated("use newInstance(intf: Class[T], target: AnyRef, config: ActiveObjectConfiguration) instead")
|
||||
def newRemoteInstance[T](intf: Class[T], target: AnyRef, timeout: Long, transactionRequired: Boolean,
|
||||
dispatcher: MessageDispatcher, hostname: String, port: Int): T = {
|
||||
val actor = actorOf(new Dispatcher(transactionRequired, None))
|
||||
actor.dispatcher = dispatcher
|
||||
newInstance(intf, target, actor, Some(new InetSocketAddress(hostname, port)), timeout)
|
||||
}
|
||||
|
||||
@deprecated("use newInstance(intf: Class[T], target: AnyRef, config: ActiveObjectConfiguration) instead")
|
||||
def newRemoteInstance[T](intf: Class[T], target: AnyRef, timeout: Long, transactionRequired: Boolean,
|
||||
dispatcher: MessageDispatcher, hostname: String, port: Int, restartCallbacks: Option[RestartCallbacks]): T = {
|
||||
val actor = actorOf(new Dispatcher(transactionRequired, restartCallbacks))
|
||||
actor.dispatcher = dispatcher
|
||||
newInstance(intf, target, actor, Some(new InetSocketAddress(hostname, port)), timeout)
|
||||
}
|
||||
|
||||
private[akka] def newInstance[T](target: Class[T], actorRef: ActorRef, remoteAddress: Option[InetSocketAddress], timeout: Long): T = {
|
||||
val proxy = Proxy.newInstance(target, true, false)
|
||||
val context = injectActiveObjectContext(proxy)
|
||||
actorRef.actor.asInstanceOf[Dispatcher].initialize(target, proxy, context)
|
||||
actorRef.timeout = timeout
|
||||
if (remoteAddress.isDefined) actorRef.makeRemote(remoteAddress.get)
|
||||
AspectInitRegistry.register(proxy, AspectInit(target, actorRef, remoteAddress, timeout))
|
||||
actorRef.start
|
||||
proxy.asInstanceOf[T]
|
||||
}
|
||||
|
||||
private[akka] def newInstance[T](intf: Class[T], target: AnyRef, actorRef: ActorRef,
|
||||
remoteAddress: Option[InetSocketAddress], timeout: Long): T = {
|
||||
val context = injectActiveObjectContext(target)
|
||||
val proxy = Proxy.newInstance(Array(intf), Array(target), true, false)
|
||||
actorRef.actor.asInstanceOf[Dispatcher].initialize(target.getClass, target, context)
|
||||
actorRef.timeout = timeout
|
||||
if (remoteAddress.isDefined) actorRef.makeRemote(remoteAddress.get)
|
||||
AspectInitRegistry.register(proxy, AspectInit(intf, actorRef, remoteAddress, timeout))
|
||||
actorRef.start
|
||||
proxy.asInstanceOf[T]
|
||||
}
|
||||
|
||||
def stop(obj: AnyRef): Unit = {
|
||||
val init = AspectInitRegistry.initFor(obj)
|
||||
init.actorRef.stop
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the underlying dispatcher actor for the given active object.
|
||||
*/
|
||||
def actorFor(obj: AnyRef): Option[ActorRef] =
|
||||
ActorRegistry.actorsFor(classOf[Dispatcher]).find(a => a.actor.asInstanceOf[Dispatcher].target == Some(obj))
|
||||
|
||||
/**
|
||||
* Links an other active object to this active object.
|
||||
* @param supervisor the supervisor active object
|
||||
* @param supervised the active object to link
|
||||
*/
|
||||
def link(supervisor: AnyRef, supervised: AnyRef) = {
|
||||
val supervisorActor = actorFor(supervisor).getOrElse(
|
||||
throw new IllegalActorStateException("Can't link when the supervisor is not an active object"))
|
||||
val supervisedActor = actorFor(supervised).getOrElse(
|
||||
throw new IllegalActorStateException("Can't link when the supervised is not an active object"))
|
||||
supervisorActor.link(supervisedActor)
|
||||
}
|
||||
|
||||
/**
|
||||
* Links an other active object to this active object and sets the fault handling for the supervisor.
|
||||
* @param supervisor the supervisor active object
|
||||
* @param supervised the active object to link
|
||||
* @param handler fault handling strategy
|
||||
* @param trapExceptions array of exceptions that should be handled by the supervisor
|
||||
*/
|
||||
def link(supervisor: AnyRef, supervised: AnyRef, handler: FaultHandlingStrategy, trapExceptions: Array[Class[_ <: Throwable]]) = {
|
||||
val supervisorActor = actorFor(supervisor).getOrElse(
|
||||
throw new IllegalActorStateException("Can't link when the supervisor is not an active object"))
|
||||
val supervisedActor = actorFor(supervised).getOrElse(
|
||||
throw new IllegalActorStateException("Can't link when the supervised is not an active object"))
|
||||
supervisorActor.trapExit = trapExceptions.toList
|
||||
supervisorActor.faultHandler = Some(handler)
|
||||
supervisorActor.link(supervisedActor)
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlink the supervised active object from the supervisor.
|
||||
* @param supervisor the supervisor active object
|
||||
* @param supervised the active object to unlink
|
||||
*/
|
||||
def unlink(supervisor: AnyRef, supervised: AnyRef) = {
|
||||
val supervisorActor = actorFor(supervisor).getOrElse(
|
||||
throw new IllegalActorStateException("Can't unlink when the supervisor is not an active object"))
|
||||
val supervisedActor = actorFor(supervised).getOrElse(
|
||||
throw new IllegalActorStateException("Can't unlink when the supervised is not an active object"))
|
||||
supervisorActor.unlink(supervisedActor)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the trap exit for the given supervisor active object.
|
||||
* @param supervisor the supervisor active object
|
||||
* @param trapExceptions array of exceptions that should be handled by the supervisor
|
||||
*/
|
||||
def trapExit(supervisor: AnyRef, trapExceptions: Array[Class[_ <: Throwable]]) = {
|
||||
val supervisorActor = actorFor(supervisor).getOrElse(
|
||||
throw new IllegalActorStateException("Can't set trap exceptions when the supervisor is not an active object"))
|
||||
supervisorActor.trapExit = trapExceptions.toList
|
||||
this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the fault handling strategy for the given supervisor active object.
|
||||
* @param supervisor the supervisor active object
|
||||
* @param handler fault handling strategy
|
||||
*/
|
||||
def faultHandler(supervisor: AnyRef, handler: FaultHandlingStrategy) = {
|
||||
val supervisorActor = actorFor(supervisor).getOrElse(
|
||||
throw new IllegalActorStateException("Can't set fault handler when the supervisor is not an active object"))
|
||||
supervisorActor.faultHandler = Some(handler)
|
||||
this
|
||||
}
|
||||
|
||||
private def injectActiveObjectContext(activeObject: AnyRef): Option[ActiveObjectContext] = {
|
||||
def injectActiveObjectContext0(activeObject: AnyRef, clazz: Class[_]): Option[ActiveObjectContext] = {
|
||||
val contextField = clazz.getDeclaredFields.toList.find(_.getType == classOf[ActiveObjectContext])
|
||||
if (contextField.isDefined) {
|
||||
contextField.get.setAccessible(true)
|
||||
val context = new ActiveObjectContext
|
||||
contextField.get.set(activeObject, context)
|
||||
Some(context)
|
||||
} else {
|
||||
val parent = clazz.getSuperclass
|
||||
if (parent != null) injectActiveObjectContext0(activeObject, parent)
|
||||
else {
|
||||
log.ifTrace("Can't set 'ActiveObjectContext' for ActiveObject [" +
|
||||
activeObject.getClass.getName +
|
||||
"] since no field of this type could be found.")
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
injectActiveObjectContext0(activeObject, activeObject.getClass)
|
||||
}
|
||||
|
||||
private[akka] def supervise(restartStrategy: RestartStrategy, components: List[Supervise]): Supervisor =
|
||||
Supervisor(SupervisorConfig(restartStrategy, components))
|
||||
}
|
||||
|
||||
private[akka] object AspectInitRegistry extends ListenerManagement {
|
||||
private val initializations = new java.util.concurrent.ConcurrentHashMap[AnyRef, AspectInit]
|
||||
|
||||
def initFor(target: AnyRef) = {
|
||||
initializations.get(target)
|
||||
}
|
||||
|
||||
def register(target: AnyRef, init: AspectInit) = {
|
||||
val res = initializations.put(target, init)
|
||||
foreachListener(_ ! AspectInitRegistered(target, init))
|
||||
res
|
||||
}
|
||||
|
||||
def unregister(target: AnyRef) = {
|
||||
val res = initializations.remove(target)
|
||||
foreachListener(_ ! AspectInitUnregistered(target, res))
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
private[akka] sealed trait AspectInitRegistryEvent
|
||||
private[akka] case class AspectInitRegistered(proxy: AnyRef, init: AspectInit) extends AspectInitRegistryEvent
|
||||
private[akka] case class AspectInitUnregistered(proxy: AnyRef, init: AspectInit) extends AspectInitRegistryEvent
|
||||
|
||||
private[akka] sealed case class AspectInit(
|
||||
val target: Class[_],
|
||||
val actorRef: ActorRef,
|
||||
val remoteAddress: Option[InetSocketAddress],
|
||||
val timeout: Long) {
|
||||
def this(target: Class[_], actorRef: ActorRef, timeout: Long) = this(target, actorRef, None, timeout)
|
||||
}
|
||||
|
||||
/**
|
||||
* AspectWerkz Aspect that is turning POJOs into Active Object.
|
||||
* Is deployed on a 'per-instance' basis.
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
@Aspect("perInstance")
|
||||
private[akka] sealed class ActiveObjectAspect {
|
||||
@volatile private var isInitialized = false
|
||||
@volatile private var isStopped = false
|
||||
private var target: Class[_] = _
|
||||
private var actorRef: ActorRef = _
|
||||
private var remoteAddress: Option[InetSocketAddress] = _
|
||||
private var timeout: Long = _
|
||||
@volatile private var instance: AnyRef = _
|
||||
|
||||
@Around("execution(* *.*(..))")
|
||||
def invoke(joinPoint: JoinPoint): AnyRef = {
|
||||
if (!isInitialized) {
|
||||
val init = AspectInitRegistry.initFor(joinPoint.getThis)
|
||||
target = init.target
|
||||
actorRef = init.actorRef
|
||||
remoteAddress = init.remoteAddress
|
||||
timeout = init.timeout
|
||||
isInitialized = true
|
||||
|
||||
}
|
||||
dispatch(joinPoint)
|
||||
}
|
||||
|
||||
private def dispatch(joinPoint: JoinPoint) = {
|
||||
if (remoteAddress.isDefined) remoteDispatch(joinPoint)
|
||||
else localDispatch(joinPoint)
|
||||
}
|
||||
|
||||
private def localDispatch(joinPoint: JoinPoint): AnyRef = {
|
||||
val rtti = joinPoint.getRtti.asInstanceOf[MethodRtti]
|
||||
val isOneWay = isVoid(rtti)
|
||||
val sender = ActiveObjectContext.sender.value
|
||||
val senderFuture = ActiveObjectContext.senderFuture.value
|
||||
|
||||
if (!actorRef.isRunning && !isStopped) {
|
||||
isStopped = true
|
||||
joinPoint.proceed
|
||||
} else if (isOneWay) {
|
||||
actorRef ! Invocation(joinPoint, true, true, sender, senderFuture)
|
||||
null.asInstanceOf[AnyRef]
|
||||
} else {
|
||||
val result = (actorRef !! (Invocation(joinPoint, false, isOneWay, sender, senderFuture), timeout)).as[AnyRef]
|
||||
if (result.isDefined) result.get
|
||||
else throw new IllegalActorStateException("No result defined for invocation [" + joinPoint + "]")
|
||||
}
|
||||
}
|
||||
|
||||
private def remoteDispatch(joinPoint: JoinPoint): AnyRef = {
|
||||
val rtti = joinPoint.getRtti.asInstanceOf[MethodRtti]
|
||||
val isOneWay = isVoid(rtti)
|
||||
val (message: Array[AnyRef], isEscaped) = escapeArguments(rtti.getParameterValues)
|
||||
val requestBuilder = RemoteRequestProtocol.newBuilder
|
||||
.setId(RemoteRequestProtocolIdFactory.nextId)
|
||||
.setMessage(MessageSerializer.serialize(message))
|
||||
.setMethod(rtti.getMethod.getName)
|
||||
.setTarget(target.getName)
|
||||
.setUuid(actorRef.uuid)
|
||||
.setTimeout(timeout)
|
||||
.setIsActor(false)
|
||||
.setIsOneWay(isOneWay)
|
||||
.setIsEscaped(false)
|
||||
val id = actorRef.registerSupervisorAsRemoteActor
|
||||
if (id.isDefined) requestBuilder.setSupervisorUuid(id.get)
|
||||
val remoteMessage = requestBuilder.build
|
||||
val future = RemoteClient.clientFor(remoteAddress.get).send(remoteMessage, None)
|
||||
if (isOneWay) null // for void methods
|
||||
else {
|
||||
if (future.isDefined) {
|
||||
future.get.await
|
||||
val result = getResultOrThrowException(future.get)
|
||||
if (result.isDefined) result.get
|
||||
else throw new IllegalActorStateException("No result returned from call to [" + joinPoint + "]")
|
||||
} else throw new IllegalActorStateException("No future returned from call to [" + joinPoint + "]")
|
||||
}
|
||||
}
|
||||
|
||||
private def getResultOrThrowException[T](future: Future[T]): Option[T] =
|
||||
if (future.exception.isDefined) {
|
||||
val (_, cause) = future.exception.get
|
||||
throw cause
|
||||
} else future.result
|
||||
|
||||
private def isVoid(rtti: MethodRtti) = rtti.getMethod.getReturnType == java.lang.Void.TYPE
|
||||
|
||||
private def escapeArguments(args: Array[AnyRef]): Tuple2[Array[AnyRef], Boolean] = {
|
||||
var isEscaped = false
|
||||
val escapedArgs = for (arg <- args) yield {
|
||||
val clazz = arg.getClass
|
||||
if (clazz.getName.contains(ActiveObject.AW_PROXY_PREFIX)) {
|
||||
isEscaped = true
|
||||
ActiveObject.AW_PROXY_PREFIX + clazz.getSuperclass.getName
|
||||
} else arg
|
||||
}
|
||||
(escapedArgs, isEscaped)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a snapshot of the current invocation.
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
@serializable private[akka] case class Invocation(
|
||||
joinPoint: JoinPoint, isOneWay: Boolean, isVoid: Boolean, sender: AnyRef, senderFuture: CompletableFuture[Any]) {
|
||||
|
||||
override def toString: String = synchronized {
|
||||
"Invocation [" +
|
||||
"\n\t\tmethod = " + joinPoint.getRtti.asInstanceOf[MethodRtti].getMethod.getName + " @ " + joinPoint.getTarget.getClass.getName +
|
||||
"\n\t\tisOneWay = " + isOneWay +
|
||||
"\n\t\tisVoid = " + isVoid +
|
||||
"\n\t\tsender = " + sender +
|
||||
"\n\t\tsenderFuture = " + senderFuture +
|
||||
"]"
|
||||
}
|
||||
|
||||
override def hashCode: Int = synchronized {
|
||||
var result = HashCode.SEED
|
||||
result = HashCode.hash(result, joinPoint)
|
||||
result = HashCode.hash(result, isOneWay)
|
||||
result = HashCode.hash(result, isVoid)
|
||||
result = HashCode.hash(result, sender)
|
||||
result = HashCode.hash(result, senderFuture)
|
||||
result
|
||||
}
|
||||
|
||||
override def equals(that: Any): Boolean = synchronized {
|
||||
that != null &&
|
||||
that.isInstanceOf[Invocation] &&
|
||||
that.asInstanceOf[Invocation].joinPoint == joinPoint &&
|
||||
that.asInstanceOf[Invocation].isOneWay == isOneWay &&
|
||||
that.asInstanceOf[Invocation].isVoid == isVoid &&
|
||||
that.asInstanceOf[Invocation].sender == sender &&
|
||||
that.asInstanceOf[Invocation].senderFuture == senderFuture
|
||||
}
|
||||
}
|
||||
|
||||
object Dispatcher {
|
||||
val ZERO_ITEM_CLASS_ARRAY = Array[Class[_]]()
|
||||
val ZERO_ITEM_OBJECT_ARRAY = Array[Object]()
|
||||
var crashedActorTl:ThreadLocal[Dispatcher] = new ThreadLocal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic Actor managing Invocation dispatch, transaction and error management.
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
private[akka] class Dispatcher(transactionalRequired: Boolean,
|
||||
var restartCallbacks: Option[RestartCallbacks] = None,
|
||||
var shutdownCallback: Option[ShutdownCallback] = None) extends Actor {
|
||||
import Dispatcher._
|
||||
|
||||
private[actor] var target: Option[AnyRef] = None
|
||||
private var zhutdown: Option[Method] = None
|
||||
private var preRestart: Option[Method] = None
|
||||
private var postRestart: Option[Method] = None
|
||||
private var initTxState: Option[Method] = None
|
||||
private var context: Option[ActiveObjectContext] = None
|
||||
private var targetClass:Class[_] = _
|
||||
|
||||
def this(transactionalRequired: Boolean) = this(transactionalRequired,None)
|
||||
|
||||
private[actor] def initialize(targetClass: Class[_], targetInstance: AnyRef, ctx: Option[ActiveObjectContext]) = {
|
||||
|
||||
if (transactionalRequired || targetClass.isAnnotationPresent(Annotations.transactionrequired))
|
||||
self.makeTransactionRequired
|
||||
self.id = targetClass.getName
|
||||
this.targetClass = targetClass
|
||||
target = Some(targetInstance)
|
||||
context = ctx
|
||||
val methods = targetInstance.getClass.getDeclaredMethods.toList
|
||||
|
||||
if (self.lifeCycle.isEmpty) self.lifeCycle = Some(LifeCycle(Permanent))
|
||||
|
||||
// See if we have any config define restart callbacks
|
||||
restartCallbacks match {
|
||||
case None => {}
|
||||
case Some(RestartCallbacks(pre, post)) =>
|
||||
preRestart = Some(try {
|
||||
targetInstance.getClass.getDeclaredMethod(pre, ZERO_ITEM_CLASS_ARRAY: _*)
|
||||
} catch { case e => throw new IllegalActorStateException(
|
||||
"Could not find pre restart method [" + pre + "] \nin [" +
|
||||
targetClass.getName + "]. \nIt must have a zero argument definition.") })
|
||||
postRestart = Some(try {
|
||||
targetInstance.getClass.getDeclaredMethod(post, ZERO_ITEM_CLASS_ARRAY: _*)
|
||||
} catch { case e => throw new IllegalActorStateException(
|
||||
"Could not find post restart method [" + post + "] \nin [" +
|
||||
targetClass.getName + "]. \nIt must have a zero argument definition.") })
|
||||
}
|
||||
// See if we have any config define a shutdown callback
|
||||
shutdownCallback match {
|
||||
case None => {}
|
||||
case Some(ShutdownCallback(down)) =>
|
||||
zhutdown = Some(try {
|
||||
targetInstance.getClass.getDeclaredMethod(down, ZERO_ITEM_CLASS_ARRAY: _*)
|
||||
} catch { case e => throw new IllegalStateException(
|
||||
"Could not find shutdown method [" + down + "] \nin [" +
|
||||
targetClass.getName + "]. \nIt must have a zero argument definition.") })
|
||||
}
|
||||
|
||||
// See if we have any annotation defined restart callbacks
|
||||
if (!preRestart.isDefined) preRestart = methods.find(m => m.isAnnotationPresent(Annotations.prerestart))
|
||||
if (!postRestart.isDefined) postRestart = methods.find(m => m.isAnnotationPresent(Annotations.postrestart))
|
||||
// See if we have an annotation defined shutdown callback
|
||||
if (!zhutdown.isDefined) zhutdown = methods.find(m => m.isAnnotationPresent(Annotations.shutdown))
|
||||
|
||||
if (preRestart.isDefined && preRestart.get.getParameterTypes.length != 0)
|
||||
throw new IllegalActorStateException(
|
||||
"Method annotated with @prerestart or defined as a restart callback in \n[" +
|
||||
targetClass.getName + "] must have a zero argument definition")
|
||||
if (postRestart.isDefined && postRestart.get.getParameterTypes.length != 0)
|
||||
throw new IllegalActorStateException(
|
||||
"Method annotated with @postrestart or defined as a restart callback in \n[" +
|
||||
targetClass.getName + "] must have a zero argument definition")
|
||||
if (zhutdown.isDefined && zhutdown.get.getParameterTypes.length != 0)
|
||||
throw new IllegalStateException(
|
||||
"Method annotated with @shutdown or defined as a shutdown callback in \n[" +
|
||||
targetClass.getName + "] must have a zero argument definition")
|
||||
|
||||
if (preRestart.isDefined) preRestart.get.setAccessible(true)
|
||||
if (postRestart.isDefined) postRestart.get.setAccessible(true)
|
||||
if (zhutdown.isDefined) zhutdown.get.setAccessible(true)
|
||||
|
||||
// see if we have a method annotated with @inittransactionalstate, if so invoke it
|
||||
initTxState = methods.find(m => m.isAnnotationPresent(Annotations.inittransactionalstate))
|
||||
if (initTxState.isDefined && initTxState.get.getParameterTypes.length != 0)
|
||||
throw new IllegalActorStateException("Method annotated with @inittransactionalstate must have a zero argument definition")
|
||||
if (initTxState.isDefined) initTxState.get.setAccessible(true)
|
||||
}
|
||||
|
||||
def receive = {
|
||||
case invocation @ Invocation(joinPoint, isOneWay, _, sender, senderFuture) =>
|
||||
ActiveObject.log.ifTrace("Invoking active object with message:\n" + invocation)
|
||||
context.foreach { ctx =>
|
||||
if (sender ne null) ctx._sender = sender
|
||||
if (senderFuture ne null) ctx._senderFuture = senderFuture
|
||||
}
|
||||
ActiveObjectContext.sender.value = joinPoint.getThis // set next sender
|
||||
self.senderFuture.foreach(ActiveObjectContext.senderFuture.value = _)
|
||||
if (Actor.SERIALIZE_MESSAGES) serializeArguments(joinPoint)
|
||||
if (isOneWay) joinPoint.proceed
|
||||
else self.reply(joinPoint.proceed)
|
||||
|
||||
// Jan Kronquist: started work on issue 121
|
||||
case Link(target) => self.link(target)
|
||||
case Unlink(target) => self.unlink(target)
|
||||
case unexpected => throw new IllegalActorStateException(
|
||||
"Unexpected message [" + unexpected + "] sent to [" + this + "]")
|
||||
}
|
||||
|
||||
override def preRestart(reason: Throwable) {
|
||||
try {
|
||||
// Since preRestart is called we know that this dispatcher
|
||||
// is about to be restarted. Put the instance in a thread
|
||||
// local so the new dispatcher can be initialized with the
|
||||
// contents of the old.
|
||||
//FIXME - This should be considered as a workaround.
|
||||
crashedActorTl.set(this)
|
||||
preRestart.foreach(_.invoke(target.get, ZERO_ITEM_OBJECT_ARRAY: _*))
|
||||
} catch { case e: InvocationTargetException => throw e.getCause }
|
||||
}
|
||||
|
||||
override def postRestart(reason: Throwable) {
|
||||
try {
|
||||
postRestart.foreach(_.invoke(target.get, ZERO_ITEM_OBJECT_ARRAY: _*))
|
||||
} catch { case e: InvocationTargetException => throw e.getCause }
|
||||
}
|
||||
|
||||
override def init = {
|
||||
// Get the crashed dispatcher from thread local and intitialize this actor with the
|
||||
// contents of the old dispatcher
|
||||
val oldActor = crashedActorTl.get();
|
||||
if (oldActor != null) {
|
||||
initialize(oldActor.targetClass, oldActor.target.get, oldActor.context)
|
||||
crashedActorTl.set(null)
|
||||
}
|
||||
}
|
||||
|
||||
override def shutdown = {
|
||||
try {
|
||||
zhutdown.foreach(_.invoke(target.get, ZERO_ITEM_OBJECT_ARRAY: _*))
|
||||
} catch { case e: InvocationTargetException => throw e.getCause
|
||||
} finally {
|
||||
AspectInitRegistry.unregister(target.get);
|
||||
}
|
||||
}
|
||||
|
||||
override def initTransactionalState = {
|
||||
try {
|
||||
if (initTxState.isDefined && target.isDefined) initTxState.get.invoke(target.get, ZERO_ITEM_OBJECT_ARRAY: _*)
|
||||
} catch { case e: InvocationTargetException => throw e.getCause }
|
||||
}
|
||||
|
||||
private def serializeArguments(joinPoint: JoinPoint) = {
|
||||
val args = joinPoint.getRtti.asInstanceOf[MethodRtti].getParameterValues
|
||||
var unserializable = false
|
||||
var hasMutableArgument = false
|
||||
for (arg <- args.toList) {
|
||||
if (!arg.isInstanceOf[String] &&
|
||||
!arg.isInstanceOf[Byte] &&
|
||||
!arg.isInstanceOf[Int] &&
|
||||
!arg.isInstanceOf[Long] &&
|
||||
!arg.isInstanceOf[Float] &&
|
||||
!arg.isInstanceOf[Double] &&
|
||||
!arg.isInstanceOf[Boolean] &&
|
||||
!arg.isInstanceOf[Char] &&
|
||||
!arg.isInstanceOf[java.lang.Byte] &&
|
||||
!arg.isInstanceOf[java.lang.Integer] &&
|
||||
!arg.isInstanceOf[java.lang.Long] &&
|
||||
!arg.isInstanceOf[java.lang.Float] &&
|
||||
!arg.isInstanceOf[java.lang.Double] &&
|
||||
!arg.isInstanceOf[java.lang.Boolean] &&
|
||||
!arg.isInstanceOf[java.lang.Character]) {
|
||||
hasMutableArgument = true
|
||||
}
|
||||
if (arg.getClass.getName.contains(ActiveObject.AW_PROXY_PREFIX)) unserializable = true
|
||||
}
|
||||
if (!unserializable && hasMutableArgument) {
|
||||
val copyOfArgs = Serializer.Java.deepClone(args)
|
||||
joinPoint.getRtti.asInstanceOf[MethodRtti].setParameterValues(copyOfArgs.asInstanceOf[Array[AnyRef]])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -436,12 +436,12 @@ trait Actor extends Logging {
|
|||
}
|
||||
|
||||
private val lifeCycles: Receive = {
|
||||
case HotSwap(code) => become(code)
|
||||
case Exit(dead, reason) => self.handleTrapExit(dead, reason)
|
||||
case Link(child) => self.link(child)
|
||||
case Unlink(child) => self.unlink(child)
|
||||
case HotSwap(code) => become(code)
|
||||
case Exit(dead, reason) => self.handleTrapExit(dead, reason)
|
||||
case Link(child) => self.link(child)
|
||||
case Unlink(child) => self.unlink(child)
|
||||
case UnlinkAndStop(child) => self.unlink(child); child.stop
|
||||
case Restart(reason) => throw reason
|
||||
case Restart(reason) => throw reason
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -291,15 +291,15 @@ trait ActorRef extends TransactionManagement {
|
|||
def !!(message: Any, timeout: Long = this.timeout)(implicit sender: Option[ActorRef] = None): Option[Any] = {
|
||||
if (isRunning) {
|
||||
val future = postMessageToMailboxAndCreateFutureResultWithTimeout[Any](message, timeout, sender, None)
|
||||
val isActiveObject = message.isInstanceOf[Invocation]
|
||||
if (isActiveObject && message.asInstanceOf[Invocation].isVoid) {
|
||||
val isTypedActor = message.isInstanceOf[Invocation]
|
||||
if (isTypedActor && message.asInstanceOf[Invocation].isVoid) {
|
||||
future.asInstanceOf[CompletableFuture[Option[_]]].completeWithResult(None)
|
||||
}
|
||||
try {
|
||||
future.await
|
||||
} catch {
|
||||
case e: FutureTimeoutException =>
|
||||
if (isActiveObject) throw e
|
||||
if (isTypedActor) throw e
|
||||
else None
|
||||
}
|
||||
if (future.exception.isDefined) throw future.exception.get._2
|
||||
|
|
@ -347,7 +347,7 @@ trait ActorRef extends TransactionManagement {
|
|||
"\n\tNo sender in scope, can't reply. " +
|
||||
"\n\tYou have probably: " +
|
||||
"\n\t\t1. Sent a message to an Actor from an instance that is NOT an Actor." +
|
||||
"\n\t\t2. Invoked a method on an Active Object from an instance NOT an Active Object." +
|
||||
"\n\t\t2. Invoked a method on an TypedActor from an instance NOT an TypedActor." +
|
||||
"\n\tElse you might want to use 'reply_?' which returns Boolean(true) if succes and Boolean(false) if no sender in scope")
|
||||
|
||||
/**
|
||||
|
|
@ -1012,20 +1012,14 @@ sealed class LocalActorRef private[akka](
|
|||
guard.withGuard {
|
||||
lifeCycle match {
|
||||
case Some(LifeCycle(Temporary, _, _)) => shutDownTemporaryActor(this)
|
||||
case _ =>
|
||||
case _ =>
|
||||
// either permanent or none where default is permanent
|
||||
Actor.log.info("Restarting actor [%s] configured as PERMANENT.", id)
|
||||
Actor.log.debug("Restarting linked actors for actor [%s].", id)
|
||||
restartLinkedActors(reason, maxNrOfRetries, withinTimeRange)
|
||||
Actor.log.debug("Invoking 'preRestart' for failed actor instance [%s].", id)
|
||||
failedActor.preRestart(reason)
|
||||
nullOutActorRefReferencesFor(failedActor)
|
||||
val freshActor = newActor
|
||||
freshActor.init
|
||||
freshActor.initTransactionalState
|
||||
actorInstance.set(freshActor)
|
||||
Actor.log.debug("Invoking 'postRestart' for new actor instance [%s].", id)
|
||||
freshActor.postRestart(reason)
|
||||
if (isTypedActorDispatcher(failedActor)) restartTypedActorDispatcher(failedActor, reason)
|
||||
else restartActor(failedActor, reason)
|
||||
_isBeingRestarted = false
|
||||
}
|
||||
}
|
||||
|
|
@ -1062,6 +1056,24 @@ sealed class LocalActorRef private[akka](
|
|||
|
||||
// ========= PRIVATE FUNCTIONS =========
|
||||
|
||||
private def isTypedActorDispatcher(a: Actor): Boolean = a.isInstanceOf[Dispatcher]
|
||||
|
||||
private def restartTypedActorDispatcher(failedActor: Actor, reason: Throwable) = {
|
||||
failedActor.preRestart(reason)
|
||||
failedActor.postRestart(reason)
|
||||
}
|
||||
|
||||
private def restartActor(failedActor: Actor, reason: Throwable) = {
|
||||
failedActor.preRestart(reason)
|
||||
nullOutActorRefReferencesFor(failedActor)
|
||||
val freshActor = newActor
|
||||
freshActor.init
|
||||
freshActor.initTransactionalState
|
||||
actorInstance.set(freshActor)
|
||||
Actor.log.debug("Invoking 'postRestart' for new actor instance [%s].", id)
|
||||
freshActor.postRestart(reason)
|
||||
}
|
||||
|
||||
private def spawnButDoNotStart[T <: Actor: Manifest]: ActorRef = guard.withGuard {
|
||||
val actorRef = Actor.actorOf(manifest[T].erasure.asInstanceOf[Class[T]].newInstance)
|
||||
if (!dispatcher.isInstanceOf[ThreadBasedDispatcher]) actorRef.dispatcher = dispatcher
|
||||
|
|
@ -1102,7 +1114,8 @@ sealed class LocalActorRef private[akka](
|
|||
createNewTransactionSet
|
||||
} else oldTxSet
|
||||
Actor.log.ifTrace("Joining transaction set [" + currentTxSet +
|
||||
"];\n\tactor " + toString + "\n\twith message [" + message + "]")
|
||||
"];\n\tactor " + toString +
|
||||
"\n\twith message [" + message + "]")
|
||||
val mtx = ThreadLocalTransaction.getThreadLocalTransaction
|
||||
if ((mtx eq null) || mtx.getStatus.isDead) currentTxSet.incParties
|
||||
else currentTxSet.incParties(mtx, 1)
|
||||
|
|
@ -1142,8 +1155,7 @@ sealed class LocalActorRef private[akka](
|
|||
new TransactionSetAbortedException("Transaction set has been aborted by another participant"),
|
||||
message, topLevelTransaction)
|
||||
case e: InterruptedException => {} // received message while actor is shutting down, ignore
|
||||
case e =>
|
||||
handleExceptionInDispatch(e, message, topLevelTransaction)
|
||||
case e => handleExceptionInDispatch(e, message, topLevelTransaction)
|
||||
} finally {
|
||||
clearTransaction
|
||||
if (topLevelTransaction) clearTransactionSet
|
||||
|
|
|
|||
|
|
@ -122,7 +122,8 @@ object ActorSerialization {
|
|||
private def fromBinaryToLocalActorRef[T <: Actor](bytes: Array[Byte], format: Format[T]): ActorRef =
|
||||
fromProtobufToLocalActorRef(SerializedActorRefProtocol.newBuilder.mergeFrom(bytes).build, format, None)
|
||||
|
||||
private def fromProtobufToLocalActorRef[T <: Actor](protocol: SerializedActorRefProtocol, format: Format[T], loader: Option[ClassLoader]): ActorRef = {
|
||||
private def fromProtobufToLocalActorRef[T <: Actor](
|
||||
protocol: SerializedActorRefProtocol, format: Format[T], loader: Option[ClassLoader]): ActorRef = {
|
||||
Actor.log.debug("Deserializing SerializedActorRefProtocol to LocalActorRef:\n" + protocol)
|
||||
|
||||
val serializer =
|
||||
|
|
@ -225,26 +226,30 @@ object RemoteActorSerialization {
|
|||
.build
|
||||
}
|
||||
|
||||
def createRemoteRequestProtocolBuilder(ar: ActorRef,
|
||||
message: Any, isOneWay: Boolean, senderOption: Option[ActorRef]): RemoteRequestProtocol.Builder = {
|
||||
import ar._
|
||||
val protocol = RemoteRequestProtocol.newBuilder
|
||||
.setId(RemoteRequestProtocolIdFactory.nextId)
|
||||
.setMessage(MessageSerializer.serialize(message))
|
||||
def createRemoteRequestProtocolBuilder(actorRef: ActorRef, message: Any, isOneWay: Boolean, senderOption: Option[ActorRef]):
|
||||
RemoteRequestProtocol.Builder = {
|
||||
import actorRef._
|
||||
|
||||
val actorInfo = ActorInfoProtocol.newBuilder
|
||||
.setUuid(uuid)
|
||||
.setTarget(actorClassName)
|
||||
.setTimeout(timeout)
|
||||
.setUuid(uuid)
|
||||
.setIsActor(true)
|
||||
.setActorType(ActorType.SCALA_ACTOR)
|
||||
.build
|
||||
|
||||
val request = RemoteRequestProtocol.newBuilder
|
||||
.setId(RemoteRequestProtocolIdFactory.nextId)
|
||||
.setMessage(MessageSerializer.serialize(message))
|
||||
.setActorInfo(actorInfo)
|
||||
.setIsOneWay(isOneWay)
|
||||
.setIsEscaped(false)
|
||||
|
||||
val id = registerSupervisorAsRemoteActor
|
||||
if (id.isDefined) protocol.setSupervisorUuid(id.get)
|
||||
if (id.isDefined) request.setSupervisorUuid(id.get)
|
||||
|
||||
senderOption.foreach { sender =>
|
||||
RemoteServer.getOrCreateServer(sender.homeAddress).register(sender.uuid, sender)
|
||||
protocol.setSender(toRemoteActorRefProtocol(sender))
|
||||
request.setSender(toRemoteActorRefProtocol(sender))
|
||||
}
|
||||
protocol
|
||||
request
|
||||
}
|
||||
}
|
||||
|
|
|
|||
839
akka-core/src/main/scala/actor/TypedActor.scala
Normal file
839
akka-core/src/main/scala/actor/TypedActor.scala
Normal file
|
|
@ -0,0 +1,839 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
|
||||
package se.scalablesolutions.akka.actor
|
||||
|
||||
import Actor._
|
||||
import se.scalablesolutions.akka.config.FaultHandlingStrategy
|
||||
import se.scalablesolutions.akka.remote.protocol.RemoteProtocol._
|
||||
import se.scalablesolutions.akka.remote.{MessageSerializer, RemoteClient, RemoteRequestProtocolIdFactory}
|
||||
import se.scalablesolutions.akka.dispatch.{MessageDispatcher, Future, CompletableFuture}
|
||||
import se.scalablesolutions.akka.config.ScalaConfig._
|
||||
import se.scalablesolutions.akka.serialization.Serializer
|
||||
import se.scalablesolutions.akka.util._
|
||||
import se.scalablesolutions.akka.actor.annotation._
|
||||
|
||||
import org.codehaus.aspectwerkz.joinpoint.{MethodRtti, JoinPoint}
|
||||
import org.codehaus.aspectwerkz.proxy.Proxy
|
||||
import org.codehaus.aspectwerkz.annotation.{Aspect, Around}
|
||||
|
||||
import java.net.InetSocketAddress
|
||||
import java.lang.reflect.{InvocationTargetException, Method, Field}
|
||||
|
||||
import scala.reflect.BeanProperty
|
||||
|
||||
/**
|
||||
* FIXME: document TypedActor
|
||||
*
|
||||
* Here is an example of usage (in Java):
|
||||
* <pre>
|
||||
* class PingImpl extends TypedActor implements Ping {
|
||||
* public void hit(int count) {
|
||||
* Pong pong = (Pong) getContext().getSender();
|
||||
* pong.hit(count++);
|
||||
* }
|
||||
*
|
||||
* @Override
|
||||
* public void init() {
|
||||
* ... // optional initialization on start
|
||||
* }
|
||||
*
|
||||
* @Override
|
||||
* public void shutdown() {
|
||||
* ... // optional cleanup on stop
|
||||
* }
|
||||
*
|
||||
* ... // more life-cycle callbacks if needed
|
||||
* }
|
||||
*
|
||||
* // create the ping actor
|
||||
* Ping ping = TypedActor.newInstance(Ping.class, PingImpl.class);
|
||||
*
|
||||
* ping.hit(1); // use the actor
|
||||
* ping.hit(1);
|
||||
*
|
||||
* // stop the actor
|
||||
* TypedActor.stop(ping);
|
||||
* </pre>
|
||||
*
|
||||
* Here is an example of usage (in Scala):
|
||||
* <pre>
|
||||
* class PingImpl extends TypedActor with Ping {
|
||||
* def hit(count: Int) = {
|
||||
* val pong = context.sender.asInstanceOf[Pong]
|
||||
* pong.hit(count += 1)
|
||||
* }
|
||||
*
|
||||
* override def init = {
|
||||
* ... // optional initialization on start
|
||||
* }
|
||||
*
|
||||
* override def shutdown = {
|
||||
* ... // optional cleanup on stop
|
||||
* }
|
||||
*
|
||||
* ... // more life-cycle callbacks if needed
|
||||
* }
|
||||
*
|
||||
* // create the ping actor
|
||||
* val ping = TypedActor.newInstance(classOf[Ping], classOf[PingImpl])
|
||||
*
|
||||
* ping.hit(1) // use the actor
|
||||
* ping.hit(1)
|
||||
*
|
||||
* // stop the actor
|
||||
* TypedActor.stop(ping)
|
||||
* </pre>
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
abstract class TypedActor extends Logging {
|
||||
|
||||
/**
|
||||
* Holds RTTI (runtime type information) for the TypedActor, f.e. current 'sender'
|
||||
* reference, the 'senderFuture' reference etc.
|
||||
* <p/>
|
||||
* This class does not contain static information but is updated by the runtime system
|
||||
* at runtime.
|
||||
* <p/>
|
||||
* You can get a hold of the context using either the 'getContext()' or 'context'
|
||||
* methods from the 'TypedActor' base class.
|
||||
* <p/>
|
||||
*
|
||||
* Here is an example of usage (in Java):
|
||||
* <pre>
|
||||
* class PingImpl extends TypedActor implements Ping {
|
||||
* public void hit(int count) {
|
||||
* Pong pong = (Pong) getContext().getSender();
|
||||
* pong.hit(count++);
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* Here is an example of usage (in Scala):
|
||||
* <pre>
|
||||
* class PingImpl extends TypedActor with Ping {
|
||||
* def hit(count: Int) = {
|
||||
* val pong = context.sender.asInstanceOf[Pong]
|
||||
* pong.hit(count += 1)
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
@BeanProperty protected var context: TypedActorContext = _
|
||||
|
||||
/**
|
||||
* The uuid for the Typed Actor.
|
||||
*/
|
||||
@BeanProperty @volatile var uuid = UUID.newUuid.toString
|
||||
|
||||
/**
|
||||
* Identifier for actor, does not have to be a unique one. Default is the 'uuid'.
|
||||
* <p/>
|
||||
* This field is used for logging, AspectRegistry.actorsFor(id), identifier for remote
|
||||
* actor in RemoteServer etc.But also as the identifier for persistence, which means
|
||||
* that you can use a custom name to be able to retrieve the "correct" persisted state
|
||||
* upon restart, remote restart etc.
|
||||
* <p/>
|
||||
* This property can be set to a custom ID.
|
||||
*/
|
||||
@BeanProperty @volatile protected var id: String = uuid
|
||||
|
||||
/**
|
||||
* Defines the default timeout for '!!' and '!!!' invocations,
|
||||
* e.g. the timeout for the future returned by the call to '!!' and '!!!'.
|
||||
* <p/>
|
||||
* This property can be set to a custom timeout.
|
||||
*/
|
||||
@BeanProperty @volatile protected var timeout: Long = Actor.TIMEOUT
|
||||
|
||||
/**
|
||||
* User overridable callback.
|
||||
* <p/>
|
||||
* Is called when an Actor is started by invoking 'actor.start'.
|
||||
*/
|
||||
def init {}
|
||||
|
||||
/**
|
||||
* User overridable callback.
|
||||
* <p/>
|
||||
* Is called when 'actor.stop' is invoked.
|
||||
*/
|
||||
def shutdown {}
|
||||
|
||||
/**
|
||||
* User overridable callback.
|
||||
* <p/>
|
||||
* Is called on a crashed Actor right BEFORE it is restarted to allow clean up of resources before Actor is terminated.
|
||||
*/
|
||||
def preRestart(reason: Throwable) {}
|
||||
|
||||
/**
|
||||
* User overridable callback.
|
||||
* <p/>
|
||||
* Is called right AFTER restart on the newly created Actor to allow reinitialization after an Actor crash.
|
||||
*/
|
||||
def postRestart(reason: Throwable) {}
|
||||
|
||||
/**
|
||||
* User overridable callback.
|
||||
* <p/>
|
||||
* Is called during initialization. Can be used to initialize transactional state. Will be invoked within a transaction.
|
||||
*/
|
||||
def initTransactionalState {}
|
||||
}
|
||||
|
||||
/**
|
||||
* FIXME: document TypedTransactor
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
@transactionrequired
|
||||
abstract class TypedTransactor extends TypedActor
|
||||
|
||||
/**
|
||||
* Configuration factory for TypedActors.
|
||||
*
|
||||
* FIXDOC: document TypedActorConfiguration
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
final class TypedActorConfiguration {
|
||||
private[akka] var _timeout: Long = Actor.TIMEOUT
|
||||
private[akka] var _restartCallbacks: Option[RestartCallbacks] = None
|
||||
private[akka] var _shutdownCallback: Option[ShutdownCallback] = None
|
||||
private[akka] var _transactionRequired = false
|
||||
private[akka] var _host: Option[InetSocketAddress] = None
|
||||
private[akka] var _messageDispatcher: Option[MessageDispatcher] = None
|
||||
|
||||
def timeout = _timeout
|
||||
def timeout(timeout: Duration) : TypedActorConfiguration = {
|
||||
_timeout = timeout.toMillis
|
||||
this
|
||||
}
|
||||
|
||||
def restartCallbacks(pre: String, post: String) : TypedActorConfiguration = {
|
||||
_restartCallbacks = Some(new RestartCallbacks(pre, post))
|
||||
this
|
||||
}
|
||||
|
||||
def shutdownCallback(down: String) : TypedActorConfiguration = {
|
||||
_shutdownCallback = Some(new ShutdownCallback(down))
|
||||
this
|
||||
}
|
||||
|
||||
def makeTransactionRequired() : TypedActorConfiguration = {
|
||||
_transactionRequired = true;
|
||||
this
|
||||
}
|
||||
|
||||
def makeRemote(hostname: String, port: Int) : TypedActorConfiguration = {
|
||||
_host = Some(new InetSocketAddress(hostname, port))
|
||||
this
|
||||
}
|
||||
|
||||
def dispatcher(messageDispatcher: MessageDispatcher) : TypedActorConfiguration = {
|
||||
_messageDispatcher = Some(messageDispatcher)
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds RTTI (runtime type information) for the TypedActor, f.e. current 'sender'
|
||||
* reference, the 'senderFuture' reference etc.
|
||||
* <p/>
|
||||
* This class does not contain static information but is updated by the runtime system
|
||||
* at runtime.
|
||||
* <p/>
|
||||
* You can get a hold of the context using either the 'getContext()' or 'context'
|
||||
* methods from the 'TypedActor' base class.
|
||||
* <p/>
|
||||
* Here is an example of usage (from Java):
|
||||
* <pre>
|
||||
* class PingImpl extends TypedActor implements Ping {
|
||||
* public void hit(int count) {
|
||||
* Pong pong = (Pong) getContext().getSender();
|
||||
* pong.hit(count++);
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* Here is an example of usage (in Scala):
|
||||
* <pre>
|
||||
* class PingImpl extends TypedActor with Ping {
|
||||
* def hit(count: Int) = {
|
||||
* val pong = context.sender.asInstanceOf[Pong]
|
||||
* pong.hit(count += 1)
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
final class TypedActorContext {
|
||||
private[akka] var _self: AnyRef = _
|
||||
private[akka] var _sender: AnyRef = _
|
||||
private[akka] var _senderFuture: CompletableFuture[Any] = _
|
||||
|
||||
/**
|
||||
* Returns the current sender reference.
|
||||
* Scala style getter.
|
||||
*/
|
||||
def sender: AnyRef = {
|
||||
if (_sender eq null) throw new IllegalActorStateException("Sender reference should not be null.")
|
||||
else _sender
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current sender reference.
|
||||
* Java style getter.
|
||||
*/
|
||||
def getSender: AnyRef = {
|
||||
if (_sender eq null) throw new IllegalActorStateException("Sender reference should not be null.")
|
||||
else _sender
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current sender future TypedActor reference.
|
||||
* Scala style getter.
|
||||
*/
|
||||
def senderFuture: Option[CompletableFuture[Any]] = if (_senderFuture eq null) None else Some(_senderFuture)
|
||||
|
||||
/**
|
||||
* Returns the current sender future TypedActor reference.
|
||||
* Java style getter.
|
||||
* This method returns 'null' if the sender future is not available.
|
||||
*/
|
||||
def getSenderFuture = _senderFuture
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory class for creating TypedActors out of plain POJOs and/or POJOs with interfaces.
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
object TypedActor extends Logging {
|
||||
import Actor.actorOf
|
||||
|
||||
val AKKA_CAMEL_ROUTING_SCHEME = "akka".intern
|
||||
private[actor] val AW_PROXY_PREFIX = "$$ProxiedByAW".intern
|
||||
|
||||
def newInstance[T](intfClass: Class[T], targetClass: Class[_], timeout: Long): T = {
|
||||
newInstance(intfClass, newTypedActor(targetClass), actorOf(new Dispatcher(false)), None, timeout)
|
||||
}
|
||||
|
||||
def newInstance[T](intfClass: Class[T], targetClass: Class[_]): T = {
|
||||
newInstance(intfClass, newTypedActor(targetClass), actorOf(new Dispatcher(false)), None, Actor.TIMEOUT)
|
||||
}
|
||||
|
||||
def newRemoteInstance[T](intfClass: Class[T], targetClass: Class[_], timeout: Long, hostname: String, port: Int): T = {
|
||||
newInstance(intfClass, newTypedActor(targetClass), actorOf(new Dispatcher(false)), Some(new InetSocketAddress(hostname, port)), timeout)
|
||||
}
|
||||
|
||||
def newRemoteInstance[T](intfClass: Class[T], targetClass: Class[_], hostname: String, port: Int): T = {
|
||||
newInstance(intfClass, newTypedActor(targetClass), actorOf(new Dispatcher(false)), Some(new InetSocketAddress(hostname, port)), Actor.TIMEOUT)
|
||||
}
|
||||
|
||||
def newInstance[T](intfClass: Class[T], targetClass: Class[_], config: TypedActorConfiguration): T = {
|
||||
val actor = actorOf(new Dispatcher(config._transactionRequired))
|
||||
if (config._messageDispatcher.isDefined) actor.dispatcher = config._messageDispatcher.get
|
||||
newInstance(intfClass, newTypedActor(targetClass), actor, config._host, config.timeout)
|
||||
}
|
||||
|
||||
private[akka] def newInstance[T](intfClass: Class[T], targetInstance: TypedActor, actorRef: ActorRef,
|
||||
remoteAddress: Option[InetSocketAddress], timeout: Long): T = {
|
||||
val context = injectTypedActorContext(targetInstance)
|
||||
val proxy = Proxy.newInstance(Array(intfClass), Array(targetInstance), true, false)
|
||||
actorRef.actor.asInstanceOf[Dispatcher].initialize(targetInstance.getClass, targetInstance, proxy, context)
|
||||
actorRef.timeout = timeout
|
||||
if (remoteAddress.isDefined) actorRef.makeRemote(remoteAddress.get)
|
||||
AspectInitRegistry.register(proxy, AspectInit(intfClass, targetInstance, actorRef, remoteAddress, timeout))
|
||||
actorRef.start
|
||||
proxy.asInstanceOf[T]
|
||||
}
|
||||
|
||||
// NOTE: currently not used - but keep it around
|
||||
private[akka] def newInstance[T <: TypedActor](
|
||||
targetClass: Class[T], actorRef: ActorRef, remoteAddress: Option[InetSocketAddress], timeout: Long): T = {
|
||||
val proxy = {
|
||||
val instance = Proxy.newInstance(targetClass, true, false)
|
||||
if (instance.isInstanceOf[TypedActor]) instance.asInstanceOf[TypedActor]
|
||||
else throw new IllegalActorStateException("Actor [" + targetClass.getName + "] is not a sub class of 'TypedActor'")
|
||||
}
|
||||
val context = injectTypedActorContext(proxy)
|
||||
actorRef.actor.asInstanceOf[Dispatcher].initialize(targetClass, proxy, proxy, context)
|
||||
actorRef.timeout = timeout
|
||||
if (remoteAddress.isDefined) actorRef.makeRemote(remoteAddress.get)
|
||||
AspectInitRegistry.register(proxy, AspectInit(targetClass, proxy, actorRef, remoteAddress, timeout))
|
||||
actorRef.start
|
||||
proxy.asInstanceOf[T]
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the current Typed Actor.
|
||||
*/
|
||||
def stop(proxy: AnyRef): Unit = AspectInitRegistry.initFor(proxy).actorRef.stop
|
||||
|
||||
/**
|
||||
* Get the underlying dispatcher actor for the given Typed Actor.
|
||||
*/
|
||||
def actorFor(proxy: AnyRef): Option[ActorRef] =
|
||||
ActorRegistry.actorsFor(classOf[Dispatcher]).find(a => a.actor.asInstanceOf[Dispatcher].proxy == proxy)
|
||||
|
||||
/**
|
||||
* Links an other Typed Actor to this Typed Actor.
|
||||
* @param supervisor the supervisor Typed Actor
|
||||
* @param supervised the Typed Actor to link
|
||||
*/
|
||||
def link(supervisor: AnyRef, supervised: AnyRef) = {
|
||||
val supervisorActor = actorFor(supervisor).getOrElse(
|
||||
throw new IllegalActorStateException("Can't link when the supervisor is not an Typed Actor"))
|
||||
val supervisedActor = actorFor(supervised).getOrElse(
|
||||
throw new IllegalActorStateException("Can't link when the supervised is not an Typed Actor"))
|
||||
supervisorActor.link(supervisedActor)
|
||||
}
|
||||
|
||||
/**
|
||||
* Links an other Typed Actor to this Typed Actor and sets the fault handling for the supervisor.
|
||||
* @param supervisor the supervisor Typed Actor
|
||||
* @param supervised the Typed Actor to link
|
||||
* @param handler fault handling strategy
|
||||
* @param trapExceptions array of exceptions that should be handled by the supervisor
|
||||
*/
|
||||
def link(supervisor: AnyRef, supervised: AnyRef,
|
||||
handler: FaultHandlingStrategy, trapExceptions: Array[Class[_ <: Throwable]]) = {
|
||||
val supervisorActor = actorFor(supervisor).getOrElse(
|
||||
throw new IllegalActorStateException("Can't link when the supervisor is not an Typed Actor"))
|
||||
val supervisedActor = actorFor(supervised).getOrElse(
|
||||
throw new IllegalActorStateException("Can't link when the supervised is not an Typed Actor"))
|
||||
supervisorActor.trapExit = trapExceptions.toList
|
||||
supervisorActor.faultHandler = Some(handler)
|
||||
supervisorActor.link(supervisedActor)
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlink the supervised Typed Actor from the supervisor.
|
||||
* @param supervisor the supervisor Typed Actor
|
||||
* @param supervised the Typed Actor to unlink
|
||||
*/
|
||||
def unlink(supervisor: AnyRef, supervised: AnyRef) = {
|
||||
val supervisorActor = actorFor(supervisor).getOrElse(
|
||||
throw new IllegalActorStateException("Can't unlink when the supervisor is not an Typed Actor"))
|
||||
val supervisedActor = actorFor(supervised).getOrElse(
|
||||
throw new IllegalActorStateException("Can't unlink when the supervised is not an Typed Actor"))
|
||||
supervisorActor.unlink(supervisedActor)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the trap exit for the given supervisor Typed Actor.
|
||||
* @param supervisor the supervisor Typed Actor
|
||||
* @param trapExceptions array of exceptions that should be handled by the supervisor
|
||||
*/
|
||||
def trapExit(supervisor: AnyRef, trapExceptions: Array[Class[_ <: Throwable]]) = {
|
||||
val supervisorActor = actorFor(supervisor).getOrElse(
|
||||
throw new IllegalActorStateException("Can't set trap exceptions when the supervisor is not an Typed Actor"))
|
||||
supervisorActor.trapExit = trapExceptions.toList
|
||||
this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the fault handling strategy for the given supervisor Typed Actor.
|
||||
* @param supervisor the supervisor Typed Actor
|
||||
* @param handler fault handling strategy
|
||||
*/
|
||||
def faultHandler(supervisor: AnyRef, handler: FaultHandlingStrategy) = {
|
||||
val supervisorActor = actorFor(supervisor).getOrElse(
|
||||
throw new IllegalActorStateException("Can't set fault handler when the supervisor is not an Typed Actor"))
|
||||
supervisorActor.faultHandler = Some(handler)
|
||||
this
|
||||
}
|
||||
|
||||
private def injectTypedActorContext(activeObject: AnyRef): Option[TypedActorContext] = {
|
||||
def injectTypedActorContext0(activeObject: AnyRef, clazz: Class[_]): Option[TypedActorContext] = {
|
||||
val contextField = clazz.getDeclaredFields.toList.find(_.getType == classOf[TypedActorContext])
|
||||
if (contextField.isDefined) {
|
||||
contextField.get.setAccessible(true)
|
||||
val context = new TypedActorContext
|
||||
contextField.get.set(activeObject, context)
|
||||
Some(context)
|
||||
} else {
|
||||
val parent = clazz.getSuperclass
|
||||
if (parent != null) injectTypedActorContext0(activeObject, parent)
|
||||
else {
|
||||
log.ifTrace("Can't set 'TypedActorContext' for TypedActor [" +
|
||||
activeObject.getClass.getName +
|
||||
"] since no field of this type could be found.")
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
injectTypedActorContext0(activeObject, activeObject.getClass)
|
||||
}
|
||||
|
||||
private[akka] def newTypedActor(targetClass: Class[_]): TypedActor = {
|
||||
val instance = targetClass.newInstance
|
||||
val typedActor =
|
||||
if (instance.isInstanceOf[TypedActor]) instance.asInstanceOf[TypedActor]
|
||||
else throw new IllegalArgumentException("Actor [" + targetClass.getName + "] is not a sub class of 'TypedActor'")
|
||||
typedActor.init
|
||||
import se.scalablesolutions.akka.stm.local.atomic
|
||||
atomic {
|
||||
typedActor.initTransactionalState
|
||||
}
|
||||
typedActor
|
||||
}
|
||||
|
||||
private[akka] def supervise(restartStrategy: RestartStrategy, components: List[Supervise]): Supervisor =
|
||||
Supervisor(SupervisorConfig(restartStrategy, components))
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal helper class to help pass the contextual information between threads.
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
private[akka] object TypedActorContext {
|
||||
import scala.util.DynamicVariable
|
||||
private[actor] val sender = new DynamicVariable[AnyRef](null)
|
||||
private[actor] val senderFuture = new DynamicVariable[CompletableFuture[Any]](null)
|
||||
}
|
||||
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
object Annotations {
|
||||
val transactionrequired = classOf[transactionrequired]
|
||||
val prerestart = classOf[prerestart]
|
||||
val postrestart = classOf[postrestart]
|
||||
val shutdown = classOf[shutdown]
|
||||
val inittransactionalstate = classOf[inittransactionalstate]
|
||||
}
|
||||
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
private[akka] object AspectInitRegistry extends ListenerManagement {
|
||||
private val initializations = new java.util.concurrent.ConcurrentHashMap[AnyRef, AspectInit]
|
||||
|
||||
def initFor(proxy: AnyRef) = initializations.get(proxy)
|
||||
|
||||
def register(proxy: AnyRef, init: AspectInit) = {
|
||||
val res = initializations.put(proxy, init)
|
||||
foreachListener(_ ! AspectInitRegistered(proxy, init))
|
||||
res
|
||||
}
|
||||
|
||||
def unregister(proxy: AnyRef) = {
|
||||
val res = initializations.remove(proxy)
|
||||
foreachListener(_ ! AspectInitUnregistered(proxy, res))
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
private[akka] sealed trait AspectInitRegistryEvent
|
||||
private[akka] case class AspectInitRegistered(proxy: AnyRef, init: AspectInit) extends AspectInitRegistryEvent
|
||||
private[akka] case class AspectInitUnregistered(proxy: AnyRef, init: AspectInit) extends AspectInitRegistryEvent
|
||||
|
||||
/**
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
private[akka] sealed case class AspectInit(
|
||||
val interfaceClass: Class[_],
|
||||
val targetInstance: TypedActor,
|
||||
val actorRef: ActorRef,
|
||||
val remoteAddress: Option[InetSocketAddress],
|
||||
val timeout: Long) {
|
||||
def this(interfaceClass: Class[_], targetInstance: TypedActor, actorRef: ActorRef, timeout: Long) =
|
||||
this(interfaceClass, targetInstance, actorRef, None, timeout)
|
||||
}
|
||||
|
||||
/**
|
||||
* AspectWerkz Aspect that is turning POJO into TypedActor.
|
||||
* <p/>
|
||||
* Is deployed on a 'perInstance' basis with the pointcut 'execution(* *.*(..))',
|
||||
* e.g. all methods on the instance.
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
@Aspect("perInstance")
|
||||
private[akka] sealed class TypedActorAspect {
|
||||
@volatile private var isInitialized = false
|
||||
@volatile private var isStopped = false
|
||||
private var interfaceClass: Class[_] = _
|
||||
private var targetInstance: TypedActor = _
|
||||
private var actorRef: ActorRef = _
|
||||
private var remoteAddress: Option[InetSocketAddress] = _
|
||||
private var timeout: Long = _
|
||||
private var uuid: String = _
|
||||
@volatile private var instance: TypedActor = _
|
||||
|
||||
@Around("execution(* *.*(..))")
|
||||
def invoke(joinPoint: JoinPoint): AnyRef = {
|
||||
if (!isInitialized) {
|
||||
val init = AspectInitRegistry.initFor(joinPoint.getThis)
|
||||
interfaceClass = init.interfaceClass
|
||||
targetInstance = init.targetInstance
|
||||
uuid = targetInstance.uuid
|
||||
actorRef = init.actorRef
|
||||
remoteAddress = init.remoteAddress
|
||||
timeout = init.timeout
|
||||
isInitialized = true
|
||||
}
|
||||
dispatch(joinPoint)
|
||||
}
|
||||
|
||||
private def dispatch(joinPoint: JoinPoint) = {
|
||||
if (remoteAddress.isDefined) remoteDispatch(joinPoint)
|
||||
else localDispatch(joinPoint)
|
||||
}
|
||||
|
||||
private def localDispatch(joinPoint: JoinPoint): AnyRef = {
|
||||
val rtti = joinPoint.getRtti.asInstanceOf[MethodRtti]
|
||||
val isOneWay = isVoid(rtti)
|
||||
val sender = TypedActorContext.sender.value
|
||||
val senderFuture = TypedActorContext.senderFuture.value
|
||||
|
||||
if (!actorRef.isRunning && !isStopped) {
|
||||
isStopped = true
|
||||
joinPoint.proceed
|
||||
|
||||
} else if (isOneWay) {
|
||||
actorRef ! Invocation(joinPoint, true, true, sender, senderFuture)
|
||||
null.asInstanceOf[AnyRef]
|
||||
|
||||
} else {
|
||||
val result = (actorRef !! (Invocation(joinPoint, false, isOneWay, sender, senderFuture), timeout)).as[AnyRef]
|
||||
if (result.isDefined) result.get
|
||||
else throw new IllegalActorStateException("No result defined for invocation [" + joinPoint + "]")
|
||||
}
|
||||
}
|
||||
|
||||
private def remoteDispatch(joinPoint: JoinPoint): AnyRef = {
|
||||
val rtti = joinPoint.getRtti.asInstanceOf[MethodRtti]
|
||||
val isOneWay = isVoid(rtti)
|
||||
val (message: Array[AnyRef], isEscaped) = escapeArguments(rtti.getParameterValues)
|
||||
|
||||
val typedActorInfo = TypedActorInfoProtocol.newBuilder
|
||||
.setInterface(interfaceClass.getName)
|
||||
.setMethod(rtti.getMethod.getName)
|
||||
.build
|
||||
|
||||
val actorInfo = ActorInfoProtocol.newBuilder
|
||||
.setUuid(uuid)
|
||||
.setTarget(targetInstance.getClass.getName)
|
||||
.setTimeout(timeout)
|
||||
.setActorType(ActorType.TYPED_ACTOR)
|
||||
.setTypedActorInfo(typedActorInfo)
|
||||
.build
|
||||
|
||||
val requestBuilder = RemoteRequestProtocol.newBuilder
|
||||
.setId(RemoteRequestProtocolIdFactory.nextId)
|
||||
.setMessage(MessageSerializer.serialize(message))
|
||||
.setActorInfo(actorInfo)
|
||||
.setIsOneWay(isOneWay)
|
||||
|
||||
val id = actorRef.registerSupervisorAsRemoteActor
|
||||
if (id.isDefined) requestBuilder.setSupervisorUuid(id.get)
|
||||
|
||||
val remoteMessage = requestBuilder.build
|
||||
|
||||
val future = RemoteClient.clientFor(remoteAddress.get).send(remoteMessage, None)
|
||||
|
||||
if (isOneWay) null // for void methods
|
||||
else {
|
||||
if (future.isDefined) {
|
||||
future.get.await
|
||||
val result = getResultOrThrowException(future.get)
|
||||
if (result.isDefined) result.get
|
||||
else throw new IllegalActorStateException("No result returned from call to [" + joinPoint + "]")
|
||||
} else throw new IllegalActorStateException("No future returned from call to [" + joinPoint + "]")
|
||||
}
|
||||
}
|
||||
|
||||
private def getResultOrThrowException[T](future: Future[T]): Option[T] =
|
||||
if (future.exception.isDefined) {
|
||||
val (_, cause) = future.exception.get
|
||||
throw cause
|
||||
} else future.result
|
||||
|
||||
private def isVoid(rtti: MethodRtti) = rtti.getMethod.getReturnType == java.lang.Void.TYPE
|
||||
|
||||
private def escapeArguments(args: Array[AnyRef]): Tuple2[Array[AnyRef], Boolean] = {
|
||||
var isEscaped = false
|
||||
val escapedArgs = for (arg <- args) yield {
|
||||
val clazz = arg.getClass
|
||||
if (clazz.getName.contains(TypedActor.AW_PROXY_PREFIX)) {
|
||||
isEscaped = true
|
||||
TypedActor.AW_PROXY_PREFIX + clazz.getSuperclass.getName
|
||||
} else arg
|
||||
}
|
||||
(escapedArgs, isEscaped)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a snapshot of the current invocation.
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
@serializable private[akka] case class Invocation(
|
||||
joinPoint: JoinPoint, isOneWay: Boolean, isVoid: Boolean, sender: AnyRef, senderFuture: CompletableFuture[Any]) {
|
||||
|
||||
override def toString: String = synchronized {
|
||||
"Invocation [" +
|
||||
"\n\t\tmethod = " + joinPoint.getRtti.asInstanceOf[MethodRtti].getMethod.getName + " @ " + joinPoint.getTarget.getClass.getName +
|
||||
"\n\t\tisOneWay = " + isOneWay +
|
||||
"\n\t\tisVoid = " + isVoid +
|
||||
"\n\t\tsender = " + sender +
|
||||
"\n\t\tsenderFuture = " + senderFuture +
|
||||
"]"
|
||||
}
|
||||
|
||||
override def hashCode: Int = synchronized {
|
||||
var result = HashCode.SEED
|
||||
result = HashCode.hash(result, joinPoint)
|
||||
result = HashCode.hash(result, isOneWay)
|
||||
result = HashCode.hash(result, isVoid)
|
||||
result = HashCode.hash(result, sender)
|
||||
result = HashCode.hash(result, senderFuture)
|
||||
result
|
||||
}
|
||||
|
||||
override def equals(that: Any): Boolean = synchronized {
|
||||
that != null &&
|
||||
that.isInstanceOf[Invocation] &&
|
||||
that.asInstanceOf[Invocation].joinPoint == joinPoint &&
|
||||
that.asInstanceOf[Invocation].isOneWay == isOneWay &&
|
||||
that.asInstanceOf[Invocation].isVoid == isVoid &&
|
||||
that.asInstanceOf[Invocation].sender == sender &&
|
||||
that.asInstanceOf[Invocation].senderFuture == senderFuture
|
||||
}
|
||||
}
|
||||
|
||||
object Dispatcher {
|
||||
val ZERO_ITEM_CLASS_ARRAY = Array[Class[_]]()
|
||||
val ZERO_ITEM_OBJECT_ARRAY = Array[Object]()
|
||||
// var crashedActorTl: ThreadLocal[Dispatcher] = new ThreadLocal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic Actor managing Invocation dispatch, transaction and error management.
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
private[akka] class Dispatcher(transactionalRequired: Boolean) extends Actor {
|
||||
import Dispatcher._
|
||||
|
||||
private[actor] var proxy: AnyRef = _
|
||||
private var context: Option[TypedActorContext] = None
|
||||
private var targetClass: Class[_] = _
|
||||
@volatile private[akka] var targetInstance: TypedActor = _
|
||||
private var proxyDelegate: Field = _
|
||||
|
||||
private[actor] def initialize(
|
||||
targetClass: Class[_], targetInstance: TypedActor, proxy: AnyRef, ctx: Option[TypedActorContext]) = {
|
||||
if (transactionalRequired || isTransactional(targetClass)) self.makeTransactionRequired
|
||||
|
||||
self.id = targetClass.getName
|
||||
this.targetClass = targetClass
|
||||
this.proxy = proxy
|
||||
this.targetInstance = targetInstance
|
||||
this.context = ctx
|
||||
|
||||
proxyDelegate = {
|
||||
val field = proxy.getClass.getDeclaredField("DELEGATE_0")
|
||||
field.setAccessible(true)
|
||||
field
|
||||
}
|
||||
|
||||
if (self.lifeCycle.isEmpty) self.lifeCycle = Some(LifeCycle(Permanent))
|
||||
}
|
||||
|
||||
def receive = {
|
||||
case invocation @ Invocation(joinPoint, isOneWay, _, sender, senderFuture) =>
|
||||
TypedActor.log.ifTrace("Invoking Typed Actor with message:\n" + invocation)
|
||||
context.foreach { ctx =>
|
||||
if (sender ne null) ctx._sender = sender
|
||||
if (senderFuture ne null) ctx._senderFuture = senderFuture
|
||||
}
|
||||
TypedActorContext.sender.value = joinPoint.getThis // set next sender
|
||||
self.senderFuture.foreach(TypedActorContext.senderFuture.value = _)
|
||||
if (Actor.SERIALIZE_MESSAGES) serializeArguments(joinPoint)
|
||||
if (isOneWay) joinPoint.proceed
|
||||
else self.reply(joinPoint.proceed)
|
||||
|
||||
// Jan Kronquist: started work on issue 121
|
||||
case Link(proxy) => self.link(proxy)
|
||||
case Unlink(proxy) => self.unlink(proxy)
|
||||
case unexpected => throw new IllegalActorStateException(
|
||||
"Unexpected message [" + unexpected + "] sent to [" + this + "]")
|
||||
}
|
||||
|
||||
override def preRestart(reason: Throwable) {
|
||||
// crashedActorTl.set(this)
|
||||
targetInstance.preRestart(reason)
|
||||
|
||||
// rewrite target instance in Dispatcher and AspectWerkz Proxy
|
||||
targetInstance = TypedActor.newTypedActor(targetClass)
|
||||
proxyDelegate.set(proxy, targetInstance)
|
||||
}
|
||||
|
||||
override def postRestart(reason: Throwable) {
|
||||
targetInstance.postRestart(reason)
|
||||
}
|
||||
|
||||
override def init {
|
||||
// Get the crashed dispatcher from thread local and intitialize this actor with the
|
||||
// contents of the old dispatcher
|
||||
// val oldActor = crashedActorTl.get
|
||||
// if (oldActor != null) {
|
||||
// initialize(oldActor.targetClass, oldActor.targetInstance, oldActor.proxy, oldActor.context)
|
||||
// crashedActorTl.set(null)
|
||||
// }
|
||||
}
|
||||
|
||||
override def shutdown {
|
||||
targetInstance.shutdown
|
||||
AspectInitRegistry.unregister(proxy);
|
||||
}
|
||||
|
||||
override def initTransactionalState {
|
||||
targetInstance.initTransactionalState
|
||||
}
|
||||
|
||||
def isTransactional(clazz: Class[_]): Boolean =
|
||||
if (clazz == null) false
|
||||
else if (clazz.isAnnotationPresent(Annotations.transactionrequired)) true
|
||||
else isTransactional(clazz.getSuperclass)
|
||||
|
||||
private def serializeArguments(joinPoint: JoinPoint) = {
|
||||
val args = joinPoint.getRtti.asInstanceOf[MethodRtti].getParameterValues
|
||||
var unserializable = false
|
||||
var hasMutableArgument = false
|
||||
for (arg <- args.toList) {
|
||||
if (!arg.isInstanceOf[String] &&
|
||||
!arg.isInstanceOf[Byte] &&
|
||||
!arg.isInstanceOf[Int] &&
|
||||
!arg.isInstanceOf[Long] &&
|
||||
!arg.isInstanceOf[Float] &&
|
||||
!arg.isInstanceOf[Double] &&
|
||||
!arg.isInstanceOf[Boolean] &&
|
||||
!arg.isInstanceOf[Char] &&
|
||||
!arg.isInstanceOf[java.lang.Byte] &&
|
||||
!arg.isInstanceOf[java.lang.Integer] &&
|
||||
!arg.isInstanceOf[java.lang.Long] &&
|
||||
!arg.isInstanceOf[java.lang.Float] &&
|
||||
!arg.isInstanceOf[java.lang.Double] &&
|
||||
!arg.isInstanceOf[java.lang.Boolean] &&
|
||||
!arg.isInstanceOf[java.lang.Character]) {
|
||||
hasMutableArgument = true
|
||||
}
|
||||
if (arg.getClass.getName.contains(TypedActor.AW_PROXY_PREFIX)) unserializable = true
|
||||
}
|
||||
if (!unserializable && hasMutableArgument) {
|
||||
val copyOfArgs = Serializer.Java.deepClone(args)
|
||||
joinPoint.getRtti.asInstanceOf[MethodRtti].setParameterValues(copyOfArgs.asInstanceOf[Array[AnyRef]])
|
||||
}
|
||||
}
|
||||
}
|
||||
34
akka-core/src/main/scala/actor/UntypedActor.scala
Normal file
34
akka-core/src/main/scala/actor/UntypedActor.scala
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
|
||||
package se.scalablesolutions.akka.actor
|
||||
|
||||
/**
|
||||
* FIXME: document
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
abstract class UntypedActor extends Actor {
|
||||
protected[akka] var context: Option[ActorContext] = None
|
||||
|
||||
protected def receive = {
|
||||
case msg =>
|
||||
if (context.isEmpty) {
|
||||
val ctx = new ActorContext(self)
|
||||
context = Some(ctx)
|
||||
onReceive(msg, ctx)
|
||||
} else onReceive(msg, context.get)
|
||||
}
|
||||
|
||||
def onReceive(message: Any, context: ActorContext): Unit
|
||||
}
|
||||
|
||||
/**
|
||||
* FIXME: document
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
class ActorContext(self: ActorRef) {
|
||||
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
package se.scalablesolutions.akka.config
|
||||
|
||||
/*
|
||||
import se.scalablesolutions.akka.kernel.{ActiveObject, ActiveObjectProxy}
|
||||
import se.scalablesolutions.akka.kernel.{TypedActor, TypedActorProxy}
|
||||
import com.google.inject.{AbstractModule}
|
||||
import java.util.{List => JList, ArrayList}
|
||||
import scala.reflect.BeanProperty
|
||||
|
|
@ -55,6 +55,6 @@ class Component(@BeanProperty val intf: Class[_],
|
|||
@BeanProperty val target: Class[_],
|
||||
@BeanProperty val lifeCycle: LifeCycle,
|
||||
@BeanProperty val timeout: Int) extends Server {
|
||||
def newWorker(proxy: ActiveObjectProxy) = se.scalablesolutions.akka.kernel.Supervise(proxy.server, lifeCycle.transform)
|
||||
def newWorker(proxy: TypedActorProxy) = se.scalablesolutions.akka.kernel.Supervise(proxy.server, lifeCycle.transform)
|
||||
}
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -6,14 +6,14 @@ package se.scalablesolutions.akka.config
|
|||
|
||||
import ScalaConfig.{RestartStrategy, Component}
|
||||
|
||||
private[akka] trait ActiveObjectConfiguratorBase {
|
||||
private[akka] trait TypedActorConfiguratorBase {
|
||||
def getExternalDependency[T](clazz: Class[T]): T
|
||||
|
||||
def configure(restartStrategy: RestartStrategy, components: List[Component]): ActiveObjectConfiguratorBase
|
||||
def configure(restartStrategy: RestartStrategy, components: List[Component]): TypedActorConfiguratorBase
|
||||
|
||||
def inject: ActiveObjectConfiguratorBase
|
||||
def inject: TypedActorConfiguratorBase
|
||||
|
||||
def supervise: ActiveObjectConfiguratorBase
|
||||
def supervise: TypedActorConfiguratorBase
|
||||
|
||||
def reset
|
||||
|
||||
|
|
|
|||
|
|
@ -12,54 +12,55 @@ import java.util.{ArrayList}
|
|||
import com.google.inject._
|
||||
|
||||
/**
|
||||
* Configurator for the Active Objects. Used to do declarative configuration of supervision.
|
||||
* It also does dependency injection with and into Active Objects using dependency injection
|
||||
* Configurator for the TypedActors. Used to do declarative configuration of supervision.
|
||||
* It also does dependency injection with and into TypedActors using dependency injection
|
||||
* frameworks such as Google Guice or Spring.
|
||||
* <p/>
|
||||
* If you don't want declarative configuration then you should use the <code>ActiveObject</code>
|
||||
* If you don't want declarative configuration then you should use the <code>TypedActor</code>
|
||||
* factory methods.
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
class ActiveObjectConfigurator {
|
||||
class TypedActorConfigurator {
|
||||
import scala.collection.JavaConversions._
|
||||
// TODO: make pluggable once we have f.e a SpringConfigurator
|
||||
private val INSTANCE = new ActiveObjectGuiceConfigurator
|
||||
private val INSTANCE = new TypedActorGuiceConfigurator
|
||||
|
||||
/**
|
||||
* Returns the a list with all active objects that has been put under supervision for the class specified.
|
||||
* Returns the a list with all typed actors that has been put under supervision for the class specified.
|
||||
*
|
||||
* @param clazz the class for the active object
|
||||
* @return a list with all the active objects for the class
|
||||
* @param clazz the class for the typed actor
|
||||
* @return a list with all the typed actors for the class
|
||||
*/
|
||||
def getInstances[T](clazz: Class[T]): JList[T] = INSTANCE.getInstance(clazz).foldLeft(new ArrayList[T]){ (l, i) => l add i ; l }
|
||||
def getInstances[T](clazz: Class[T]): JList[T] =
|
||||
INSTANCE.getInstance(clazz).foldLeft(new ArrayList[T]){ (l, i) => l add i ; l }
|
||||
|
||||
/**
|
||||
* Returns the first item in a list of all active objects that has been put under supervision for the class specified.
|
||||
* Returns the first item in a list of all typed actors that has been put under supervision for the class specified.
|
||||
*
|
||||
* @param clazz the class for the active object
|
||||
* @return the active object for the class
|
||||
* @param clazz the class for the typed actor
|
||||
* @return the typed actor for the class
|
||||
*/
|
||||
def getInstance[T](clazz: Class[T]): T = INSTANCE.getInstance(clazz).head
|
||||
|
||||
def configure(restartStrategy: RestartStrategy, components: Array[Component]): ActiveObjectConfigurator = {
|
||||
def configure(restartStrategy: RestartStrategy, components: Array[Component]): TypedActorConfigurator = {
|
||||
INSTANCE.configure(
|
||||
restartStrategy.transform,
|
||||
components.toList.asInstanceOf[scala.List[Component]].map(_.transform))
|
||||
this
|
||||
}
|
||||
|
||||
def inject: ActiveObjectConfigurator = {
|
||||
def inject: TypedActorConfigurator = {
|
||||
INSTANCE.inject
|
||||
this
|
||||
}
|
||||
|
||||
def supervise: ActiveObjectConfigurator = {
|
||||
def supervise: TypedActorConfigurator = {
|
||||
INSTANCE.supervise
|
||||
this
|
||||
}
|
||||
|
||||
def addExternalGuiceModule(module: Module): ActiveObjectConfigurator = {
|
||||
def addExternalGuiceModule(module: Module): TypedActorConfigurator = {
|
||||
INSTANCE.addExternalGuiceModule(module)
|
||||
this
|
||||
}
|
||||
|
|
@ -7,7 +7,7 @@ package se.scalablesolutions.akka.config
|
|||
import com.google.inject._
|
||||
|
||||
import se.scalablesolutions.akka.config.ScalaConfig._
|
||||
import se.scalablesolutions.akka.actor.{Supervisor, ActiveObject, Dispatcher, ActorRef, Actor, IllegalActorStateException}
|
||||
import se.scalablesolutions.akka.actor.{Supervisor, TypedActor, Dispatcher, ActorRef, Actor, IllegalActorStateException}
|
||||
import se.scalablesolutions.akka.remote.RemoteServer
|
||||
import se.scalablesolutions.akka.util.Logging
|
||||
|
||||
|
|
@ -17,12 +17,12 @@ import java.net.InetSocketAddress
|
|||
import java.lang.reflect.Method
|
||||
|
||||
/**
|
||||
* This is an class for internal usage. Instead use the <code>se.scalablesolutions.akka.config.ActiveObjectConfigurator</code>
|
||||
* class for creating ActiveObjects.
|
||||
* This is an class for internal usage. Instead use the <code>se.scalablesolutions.akka.config.TypedActorConfigurator</code>
|
||||
* class for creating TypedActors.
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
private[akka] class ActiveObjectGuiceConfigurator extends ActiveObjectConfiguratorBase with Logging {
|
||||
private[akka] class TypedActorGuiceConfigurator extends TypedActorConfiguratorBase with Logging {
|
||||
private var injector: Injector = _
|
||||
private var supervisor: Option[Supervisor] = None
|
||||
private var restartStrategy: RestartStrategy = _
|
||||
|
|
@ -37,11 +37,11 @@ private[akka] class ActiveObjectGuiceConfigurator extends ActiveObjectConfigurat
|
|||
/**
|
||||
* Returns the active abject that has been put under supervision for the class specified.
|
||||
*
|
||||
* @param clazz the class for the active object
|
||||
* @return the active objects for the class
|
||||
* @param clazz the class for the typed actor
|
||||
* @return the typed actors for the class
|
||||
*/
|
||||
def getInstance[T](clazz: Class[T]): List[T] = synchronized {
|
||||
log.debug("Retrieving active object [%s]", clazz.getName)
|
||||
log.debug("Retrieving typed actor [%s]", clazz.getName)
|
||||
if (injector eq null) throw new IllegalActorStateException(
|
||||
"inject() and/or supervise() must be called before invoking getInstance(clazz)")
|
||||
val (proxy, targetInstance, component) =
|
||||
|
|
@ -67,7 +67,7 @@ private[akka] class ActiveObjectGuiceConfigurator extends ActiveObjectConfigurat
|
|||
}
|
||||
|
||||
override def configure(restartStrategy: RestartStrategy, components: List[Component]):
|
||||
ActiveObjectConfiguratorBase = synchronized {
|
||||
TypedActorConfiguratorBase = synchronized {
|
||||
this.restartStrategy = restartStrategy
|
||||
this.components = components.toArray.toList.asInstanceOf[List[Component]]
|
||||
bindings = for (component <- this.components) yield {
|
||||
|
|
@ -76,56 +76,65 @@ private[akka] class ActiveObjectGuiceConfigurator extends ActiveObjectConfigurat
|
|||
}
|
||||
val deps = new java.util.ArrayList[DependencyBinding](bindings.size)
|
||||
for (b <- bindings) deps.add(b)
|
||||
modules.add(new ActiveObjectGuiceModule(deps))
|
||||
modules.add(new TypedActorGuiceModule(deps))
|
||||
this
|
||||
}
|
||||
|
||||
private def newSubclassingProxy(component: Component): DependencyBinding = {
|
||||
val targetClass = component.target
|
||||
val actorRef = Actor.actorOf(new Dispatcher(component.transactionRequired,
|
||||
component.lifeCycle.restartCallbacks,
|
||||
component.lifeCycle.shutdownCallback))
|
||||
val targetClass =
|
||||
if (component.target.isInstanceOf[Class[_ <: TypedActor]]) component.target.asInstanceOf[Class[_ <: TypedActor]]
|
||||
else throw new IllegalArgumentException("TypedActor [" + component.target.getName + "] must be a subclass of TypedActor")
|
||||
val actorRef = Actor.actorOf(new Dispatcher(component.transactionRequired))
|
||||
if (component.dispatcher.isDefined) actorRef.dispatcher = component.dispatcher.get
|
||||
val remoteAddress =
|
||||
if (component.remoteAddress.isDefined)
|
||||
Some(new InetSocketAddress(component.remoteAddress.get.hostname, component.remoteAddress.get.port))
|
||||
else None
|
||||
val proxy = ActiveObject.newInstance(targetClass, actorRef, remoteAddress, component.timeout).asInstanceOf[AnyRef]
|
||||
remoteAddress.foreach(address => RemoteServer.registerActiveObject(address, targetClass.getName, proxy))
|
||||
val proxy = TypedActor.newInstance(targetClass, actorRef, remoteAddress, component.timeout).asInstanceOf[AnyRef]
|
||||
remoteAddress.foreach(address => RemoteServer.registerTypedActor(address, targetClass.getName, proxy))
|
||||
supervised ::= Supervise(actorRef, component.lifeCycle)
|
||||
activeObjectRegistry.put(targetClass, (proxy, proxy, component))
|
||||
new DependencyBinding(targetClass, proxy)
|
||||
}
|
||||
|
||||
private def newDelegatingProxy(component: Component): DependencyBinding = {
|
||||
val targetClass = component.intf.get
|
||||
val targetInstance = component.target.newInstance.asInstanceOf[AnyRef] // TODO: perhaps need to put in registry
|
||||
component.target.getConstructor(Array[Class[_]](): _*).setAccessible(true)
|
||||
val actorRef = Actor.actorOf(new Dispatcher(component.transactionRequired,
|
||||
component.lifeCycle.restartCallbacks,
|
||||
component.lifeCycle.shutdownCallback))
|
||||
|
||||
val targetClass = component.intf.get
|
||||
val instance = component.target.newInstance.asInstanceOf[AnyRef] // TODO: perhaps need to put in registry
|
||||
|
||||
val targetInstance =
|
||||
if (instance.isInstanceOf[TypedActor]) instance.asInstanceOf[TypedActor]
|
||||
else throw new IllegalArgumentException("TypedActor [" + component.target.getName + "] must be a subclass of TypedActor")
|
||||
|
||||
val actorRef = Actor.actorOf(new Dispatcher(component.transactionRequired))
|
||||
|
||||
if (component.dispatcher.isDefined) actorRef.dispatcher = component.dispatcher.get
|
||||
|
||||
val remoteAddress =
|
||||
if (component.remoteAddress.isDefined)
|
||||
Some(new InetSocketAddress(component.remoteAddress.get.hostname, component.remoteAddress.get.port))
|
||||
else None
|
||||
val proxy = ActiveObject.newInstance(
|
||||
|
||||
val proxy = TypedActor.newInstance(
|
||||
targetClass, targetInstance, actorRef, remoteAddress, component.timeout).asInstanceOf[AnyRef]
|
||||
remoteAddress.foreach(address => RemoteServer.registerActiveObject(address, targetClass.getName, proxy))
|
||||
|
||||
remoteAddress.foreach(address => RemoteServer.registerTypedActor(address, targetClass.getName, proxy))
|
||||
supervised ::= Supervise(actorRef, component.lifeCycle)
|
||||
|
||||
activeObjectRegistry.put(targetClass, (proxy, targetInstance, component))
|
||||
new DependencyBinding(targetClass, proxy)
|
||||
}
|
||||
|
||||
override def inject: ActiveObjectConfiguratorBase = synchronized {
|
||||
override def inject: TypedActorConfiguratorBase = synchronized {
|
||||
if (injector ne null) throw new IllegalActorStateException("inject() has already been called on this configurator")
|
||||
injector = Guice.createInjector(modules)
|
||||
this
|
||||
}
|
||||
|
||||
override def supervise: ActiveObjectConfiguratorBase = synchronized {
|
||||
override def supervise: TypedActorConfiguratorBase = synchronized {
|
||||
if (injector eq null) inject
|
||||
supervisor = Some(ActiveObject.supervise(restartStrategy, supervised))
|
||||
supervisor = Some(TypedActor.supervise(restartStrategy, supervised))
|
||||
this
|
||||
}
|
||||
|
||||
|
|
@ -141,7 +150,7 @@ private[akka] class ActiveObjectGuiceConfigurator extends ActiveObjectConfigurat
|
|||
* }})
|
||||
* </pre>
|
||||
*/
|
||||
def addExternalGuiceModule(module: Module): ActiveObjectConfiguratorBase = synchronized {
|
||||
def addExternalGuiceModule(module: Module): TypedActorConfiguratorBase = synchronized {
|
||||
modules.add(module)
|
||||
this
|
||||
}
|
||||
|
|
@ -137,7 +137,7 @@ object RemoteClient extends Logging {
|
|||
actorsFor(RemoteServer.Address(hostname, port)) += uuid
|
||||
}
|
||||
|
||||
// TODO: add RemoteClient.unregister for ActiveObject, but first need a @shutdown callback
|
||||
// TODO: add RemoteClient.unregister for TypedActor, but first need a @shutdown callback
|
||||
private[akka] def unregister(hostname: String, port: Int, uuid: String) = synchronized {
|
||||
val set = actorsFor(RemoteServer.Address(hostname, port))
|
||||
set -= uuid
|
||||
|
|
@ -217,7 +217,7 @@ class RemoteClient private[akka] (val hostname: String, val port: Int, loader: O
|
|||
} else {
|
||||
futures.synchronized {
|
||||
val futureResult = if (senderFuture.isDefined) senderFuture.get
|
||||
else new DefaultCompletableFuture[T](request.getTimeout)
|
||||
else new DefaultCompletableFuture[T](request.getActorInfo.getTimeout)
|
||||
futures.put(request.getId, futureResult)
|
||||
connection.getChannel.write(request)
|
||||
Some(futureResult)
|
||||
|
|
@ -230,11 +230,13 @@ class RemoteClient private[akka] (val hostname: String, val port: Int, loader: O
|
|||
}
|
||||
|
||||
private[akka] def registerSupervisorForActor(actorRef: ActorRef) =
|
||||
if (!actorRef.supervisor.isDefined) throw new IllegalActorStateException("Can't register supervisor for " + actorRef + " since it is not under supervision")
|
||||
if (!actorRef.supervisor.isDefined) throw new IllegalActorStateException(
|
||||
"Can't register supervisor for " + actorRef + " since it is not under supervision")
|
||||
else supervisors.putIfAbsent(actorRef.supervisor.get.uuid, actorRef)
|
||||
|
||||
private[akka] def deregisterSupervisorForActor(actorRef: ActorRef) =
|
||||
if (!actorRef.supervisor.isDefined) throw new IllegalActorStateException("Can't unregister supervisor for " + actorRef + " since it is not under supervision")
|
||||
if (!actorRef.supervisor.isDefined) throw new IllegalActorStateException(
|
||||
"Can't unregister supervisor for " + actorRef + " since it is not under supervision")
|
||||
else supervisors.remove(actorRef.supervisor.get.uuid)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ object RemoteNode extends RemoteServer
|
|||
|
||||
/**
|
||||
* For internal use only.
|
||||
* Holds configuration variables, remote actors, remote active objects and remote servers.
|
||||
* Holds configuration variables, remote actors, remote typed actors and remote servers.
|
||||
*
|
||||
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
||||
*/
|
||||
|
|
@ -104,7 +104,7 @@ object RemoteServer {
|
|||
actorsFor(RemoteServer.Address(address.getHostName, address.getPort)).actors.put(uuid, actor)
|
||||
}
|
||||
|
||||
private[akka] def registerActiveObject(address: InetSocketAddress, name: String, activeObject: AnyRef) = guard.withWriteGuard {
|
||||
private[akka] def registerTypedActor(address: InetSocketAddress, name: String, activeObject: AnyRef) = guard.withWriteGuard {
|
||||
actorsFor(RemoteServer.Address(address.getHostName, address.getPort)).activeObjects.put(name, activeObject)
|
||||
}
|
||||
|
||||
|
|
@ -228,7 +228,7 @@ class RemoteServer extends Logging {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: register active object in RemoteServer as well
|
||||
// TODO: register typed actor in RemoteServer as well
|
||||
|
||||
/**
|
||||
* Register Remote Actor by the Actor's 'id' field. It starts the Actor if it is not started already.
|
||||
|
|
@ -360,139 +360,88 @@ class RemoteServerHandler(
|
|||
|
||||
private def handleRemoteRequestProtocol(request: RemoteRequestProtocol, channel: Channel) = {
|
||||
log.debug("Received RemoteRequestProtocol[\n%s]", request.toString)
|
||||
if (request.getIsActor) dispatchToActor(request, channel)
|
||||
else dispatchToActiveObject(request, channel)
|
||||
val actorType = request.getActorInfo.getActorType
|
||||
if (actorType == ActorType.SCALA_ACTOR) dispatchToActor(request, channel)
|
||||
else if (actorType == ActorType.JAVA_ACTOR) throw new IllegalActorStateException("ActorType JAVA_ACTOR is currently not supported")
|
||||
else if (actorType == ActorType.TYPED_ACTOR) dispatchToTypedActor(request, channel)
|
||||
else throw new IllegalActorStateException("Unknown ActorType [" + actorType + "]")
|
||||
}
|
||||
|
||||
private def dispatchToActor(request: RemoteRequestProtocol, channel: Channel) = {
|
||||
log.debug("Dispatching to remote actor [%s:%s]", request.getTarget, request.getUuid)
|
||||
val actorRef = createActor(request.getTarget, request.getUuid, request.getTimeout)
|
||||
val actorInfo = request.getActorInfo
|
||||
log.debug("Dispatching to remote actor [%s:%s]", actorInfo.getTarget, actorInfo.getUuid)
|
||||
|
||||
val actorRef = createActor(actorInfo)
|
||||
actorRef.start
|
||||
|
||||
val message = MessageSerializer.deserialize(request.getMessage)
|
||||
val sender =
|
||||
if (request.hasSender) Some(RemoteActorSerialization.fromProtobufToRemoteActorRef(request.getSender, applicationLoader))
|
||||
else None
|
||||
|
||||
if (request.getIsOneWay) actorRef.!(message)(sender)
|
||||
else {
|
||||
try {
|
||||
val resultOrNone = (actorRef.!!(message)(sender)).as[AnyRef]
|
||||
val result = if (resultOrNone.isDefined) resultOrNone.get else null
|
||||
|
||||
log.debug("Returning result from actor invocation [%s]", result)
|
||||
val replyBuilder = RemoteReplyProtocol.newBuilder
|
||||
.setId(request.getId)
|
||||
.setMessage(MessageSerializer.serialize(result))
|
||||
.setIsSuccessful(true)
|
||||
.setIsActor(true)
|
||||
|
||||
if (request.hasSupervisorUuid) replyBuilder.setSupervisorUuid(request.getSupervisorUuid)
|
||||
val replyMessage = replyBuilder.build
|
||||
channel.write(replyMessage)
|
||||
channel.write(replyBuilder.build)
|
||||
|
||||
} catch {
|
||||
case e: Throwable =>
|
||||
log.error(e, "Could not invoke remote actor [%s]", request.getTarget)
|
||||
val replyBuilder = RemoteReplyProtocol.newBuilder
|
||||
.setId(request.getId)
|
||||
.setException(ExceptionProtocol.newBuilder.setClassname(e.getClass.getName).setMessage(e.getMessage).build)
|
||||
.setIsSuccessful(false)
|
||||
.setIsActor(true)
|
||||
if (request.hasSupervisorUuid) replyBuilder.setSupervisorUuid(request.getSupervisorUuid)
|
||||
val replyMessage = replyBuilder.build
|
||||
channel.write(replyMessage)
|
||||
case e: Throwable => channel.write(createErrorReplyMessage(e, request, true))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def dispatchToActiveObject(request: RemoteRequestProtocol, channel: Channel) = {
|
||||
log.debug("Dispatching to remote active object [%s :: %s]", request.getMethod, request.getTarget)
|
||||
val activeObject = createActiveObject(request.getTarget, request.getTimeout)
|
||||
private def dispatchToTypedActor(request: RemoteRequestProtocol, channel: Channel) = {
|
||||
val actorInfo = request.getActorInfo
|
||||
val typedActorInfo = actorInfo.getTypedActorInfo
|
||||
log.debug("Dispatching to remote typed actor [%s :: %s]", typedActorInfo.getMethod, typedActorInfo.getInterface)
|
||||
val activeObject = createTypedActor(actorInfo)
|
||||
|
||||
val args = MessageSerializer.deserialize(request.getMessage).asInstanceOf[Array[AnyRef]].toList
|
||||
val argClasses = args.map(_.getClass)
|
||||
val (unescapedArgs, unescapedArgClasses) = unescapeArgs(args, argClasses, request.getTimeout)
|
||||
|
||||
try {
|
||||
val messageReceiver = activeObject.getClass.getDeclaredMethod(
|
||||
request.getMethod, unescapedArgClasses: _*)
|
||||
if (request.getIsOneWay) messageReceiver.invoke(activeObject, unescapedArgs: _*)
|
||||
val messageReceiver = activeObject.getClass.getDeclaredMethod(typedActorInfo.getMethod, argClasses: _*)
|
||||
if (request.getIsOneWay) messageReceiver.invoke(activeObject, args: _*)
|
||||
else {
|
||||
val result = messageReceiver.invoke(activeObject, unescapedArgs: _*)
|
||||
log.debug("Returning result from remote active object invocation [%s]", result)
|
||||
val result = messageReceiver.invoke(activeObject, args: _*)
|
||||
log.debug("Returning result from remote typed actor invocation [%s]", result)
|
||||
val replyBuilder = RemoteReplyProtocol.newBuilder
|
||||
.setId(request.getId)
|
||||
.setMessage(MessageSerializer.serialize(result))
|
||||
.setIsSuccessful(true)
|
||||
.setIsActor(false)
|
||||
if (request.hasSupervisorUuid) replyBuilder.setSupervisorUuid(request.getSupervisorUuid)
|
||||
val replyMessage = replyBuilder.build
|
||||
channel.write(replyMessage)
|
||||
channel.write(replyBuilder.build)
|
||||
}
|
||||
} catch {
|
||||
case e: InvocationTargetException =>
|
||||
log.error(e.getCause, "Could not invoke remote active object [%s :: %s]", request.getMethod, request.getTarget)
|
||||
val replyBuilder = RemoteReplyProtocol.newBuilder
|
||||
.setId(request.getId)
|
||||
.setException(ExceptionProtocol.newBuilder.setClassname(e.getCause.getClass.getName).setMessage(e.getCause.getMessage).build)
|
||||
.setIsSuccessful(false)
|
||||
.setIsActor(false)
|
||||
if (request.hasSupervisorUuid) replyBuilder.setSupervisorUuid(request.getSupervisorUuid)
|
||||
val replyMessage = replyBuilder.build
|
||||
channel.write(replyMessage)
|
||||
case e: Throwable =>
|
||||
log.error(e, "Could not invoke remote active object [%s :: %s]", request.getMethod, request.getTarget)
|
||||
val replyBuilder = RemoteReplyProtocol.newBuilder
|
||||
.setId(request.getId)
|
||||
.setException(ExceptionProtocol.newBuilder.setClassname(e.getClass.getName).setMessage(e.getMessage).build)
|
||||
.setIsSuccessful(false)
|
||||
.setIsActor(false)
|
||||
if (request.hasSupervisorUuid) replyBuilder.setSupervisorUuid(request.getSupervisorUuid)
|
||||
val replyMessage = replyBuilder.build
|
||||
channel.write(replyMessage)
|
||||
case e: InvocationTargetException => channel.write(createErrorReplyMessage(e.getCause, request, false))
|
||||
case e: Throwable => channel.write(createErrorReplyMessage(e, request, false))
|
||||
}
|
||||
}
|
||||
|
||||
private def unescapeArgs(args: scala.List[AnyRef], argClasses: scala.List[Class[_]], timeout: Long) = {
|
||||
val unescapedArgs = new Array[AnyRef](args.size)
|
||||
val unescapedArgClasses = new Array[Class[_]](args.size)
|
||||
|
||||
val escapedArgs = for (i <- 0 until args.size) {
|
||||
val arg = args(i)
|
||||
if (arg.isInstanceOf[String] && arg.asInstanceOf[String].startsWith(AW_PROXY_PREFIX)) {
|
||||
val argString = arg.asInstanceOf[String]
|
||||
val proxyName = argString.replace(AW_PROXY_PREFIX, "")
|
||||
val activeObject = createActiveObject(proxyName, timeout)
|
||||
unescapedArgs(i) = activeObject
|
||||
unescapedArgClasses(i) = Class.forName(proxyName)
|
||||
} else {
|
||||
unescapedArgs(i) = args(i)
|
||||
unescapedArgClasses(i) = argClasses(i)
|
||||
}
|
||||
}
|
||||
(unescapedArgs, unescapedArgClasses)
|
||||
}
|
||||
|
||||
private def createActiveObject(name: String, timeout: Long): AnyRef = {
|
||||
val activeObjectOrNull = activeObjects.get(name)
|
||||
if (activeObjectOrNull eq null) {
|
||||
try {
|
||||
log.info("Creating a new remote active object [%s]", name)
|
||||
val clazz = if (applicationLoader.isDefined) applicationLoader.get.loadClass(name)
|
||||
else Class.forName(name)
|
||||
val newInstance = ActiveObject.newInstance(clazz, timeout).asInstanceOf[AnyRef]
|
||||
activeObjects.put(name, newInstance)
|
||||
newInstance
|
||||
} catch {
|
||||
case e =>
|
||||
log.error(e, "Could not create remote active object instance")
|
||||
throw e
|
||||
}
|
||||
} else activeObjectOrNull
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of the actor with name, uuid and timeout specified as arguments.
|
||||
*
|
||||
* If actor already created then just return it from the registry.
|
||||
*
|
||||
* Does not start the actor.
|
||||
*/
|
||||
private def createActor(name: String, uuid: String, timeout: Long): ActorRef = {
|
||||
private def createActor(actorInfo: ActorInfoProtocol): ActorRef = {
|
||||
val name = actorInfo.getTarget
|
||||
val uuid = actorInfo.getUuid
|
||||
val timeout = actorInfo.getTimeout
|
||||
|
||||
val actorRefOrNull = actors.get(uuid)
|
||||
if (actorRefOrNull eq null) {
|
||||
try {
|
||||
|
|
@ -512,4 +461,43 @@ class RemoteServerHandler(
|
|||
}
|
||||
} else actorRefOrNull
|
||||
}
|
||||
|
||||
private def createTypedActor(actorInfo: ActorInfoProtocol): AnyRef = {
|
||||
val uuid = actorInfo.getUuid
|
||||
val activeObjectOrNull = activeObjects.get(uuid)
|
||||
|
||||
if (activeObjectOrNull eq null) {
|
||||
val typedActorInfo = actorInfo.getTypedActorInfo
|
||||
val interfaceClassname = typedActorInfo.getInterface
|
||||
val targetClassname = actorInfo.getTarget
|
||||
|
||||
try {
|
||||
log.info("Creating a new remote typed actor:\n\t[%s :: %s]", interfaceClassname, targetClassname)
|
||||
|
||||
val (interfaceClass, targetClass) =
|
||||
if (applicationLoader.isDefined) (applicationLoader.get.loadClass(interfaceClassname),
|
||||
applicationLoader.get.loadClass(targetClassname))
|
||||
else (Class.forName(interfaceClassname), Class.forName(targetClassname))
|
||||
|
||||
val newInstance = TypedActor.newInstance(
|
||||
interfaceClass, targetClass.asInstanceOf[Class[_ <: TypedActor]], actorInfo.getTimeout).asInstanceOf[AnyRef]
|
||||
activeObjects.put(uuid, newInstance)
|
||||
newInstance
|
||||
} catch {
|
||||
case e => log.error(e, "Could not create remote typed actor instance"); throw e
|
||||
}
|
||||
} else activeObjectOrNull
|
||||
}
|
||||
|
||||
private def createErrorReplyMessage(e: Throwable, request: RemoteRequestProtocol, isActor: Boolean): RemoteReplyProtocol = {
|
||||
val actorInfo = request.getActorInfo
|
||||
log.error(e, "Could not invoke remote typed actor [%s :: %s]", actorInfo.getTypedActorInfo.getMethod, actorInfo.getTarget)
|
||||
val replyBuilder = RemoteReplyProtocol.newBuilder
|
||||
.setId(request.getId)
|
||||
.setException(ExceptionProtocol.newBuilder.setClassname(e.getClass.getName).setMessage(e.getMessage).build)
|
||||
.setIsSuccessful(false)
|
||||
.setIsActor(isActor)
|
||||
if (request.hasSupervisorUuid) replyBuilder.setSupervisorUuid(request.getSupervisorUuid)
|
||||
replyBuilder.build
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
package se.scalablesolutions.akka.actor;
|
||||
|
||||
public class ActiveObjectFailer implements java.io.Serializable {
|
||||
public int fail() {
|
||||
throw new RuntimeException("expected");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +1,16 @@
|
|||
package se.scalablesolutions.akka.actor;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import se.scalablesolutions.akka.actor.*;
|
||||
|
||||
public class BarImpl implements Bar {
|
||||
public class BarImpl extends TypedActor implements Bar {
|
||||
@Inject
|
||||
private Ext ext;
|
||||
|
||||
public Ext getExt() {
|
||||
return ext;
|
||||
}
|
||||
|
||||
public void bar(String msg) {
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,34 +1,14 @@
|
|||
package se.scalablesolutions.akka.actor;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
public class Foo extends se.scalablesolutions.akka.serialization.Serializable.JavaJSON {
|
||||
@Inject
|
||||
private Bar bar;
|
||||
public Foo body() { return this; }
|
||||
public Bar getBar() {
|
||||
return bar;
|
||||
}
|
||||
public String foo(String msg) {
|
||||
return msg + "return_foo ";
|
||||
}
|
||||
public void bar(String msg) {
|
||||
bar.bar(msg);
|
||||
}
|
||||
public String longRunning() {
|
||||
try {
|
||||
Thread.sleep(1200);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
return "test";
|
||||
}
|
||||
public String throwsException() {
|
||||
if (true) throw new RuntimeException("Expected exception; to test fault-tolerance");
|
||||
return "test";
|
||||
}
|
||||
public interface Foo {
|
||||
public Foo body();
|
||||
public Bar getBar();
|
||||
|
||||
public int $tag() throws java.rmi.RemoteException
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
public String foo(String msg);
|
||||
public void bar(String msg);
|
||||
|
||||
public String longRunning();
|
||||
public String throwsException();
|
||||
|
||||
public int $tag() throws java.rmi.RemoteException;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
package se.scalablesolutions.akka.actor;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import se.scalablesolutions.akka.actor.*;
|
||||
|
||||
public class FooImpl extends TypedActor implements Foo {
|
||||
@Inject
|
||||
private Bar bar;
|
||||
|
||||
public Foo body() { return this; }
|
||||
|
||||
public Bar getBar() {
|
||||
return bar;
|
||||
}
|
||||
|
||||
public String foo(String msg) {
|
||||
return msg + "return_foo ";
|
||||
}
|
||||
|
||||
public void bar(String msg) {
|
||||
bar.bar(msg);
|
||||
}
|
||||
|
||||
public String longRunning() {
|
||||
try {
|
||||
Thread.sleep(1200);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
return "test";
|
||||
}
|
||||
|
||||
public String throwsException() {
|
||||
if (true) throw new RuntimeException("Expected exception; to test fault-tolerance");
|
||||
return "test";
|
||||
}
|
||||
|
||||
public int $tag() throws java.rmi.RemoteException {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package se.scalablesolutions.akka.actor;
|
||||
|
||||
public interface NestedTransactionalTypedActor {
|
||||
public String getMapState(String key);
|
||||
public String getVectorState();
|
||||
public String getRefState();
|
||||
public void setMapState(String key, String msg);
|
||||
public void setVectorState(String msg);
|
||||
public void setRefState(String msg);
|
||||
public void success(String key, String msg);
|
||||
public String failure(String key, String msg, TypedActorFailer failer);
|
||||
}
|
||||
|
|
@ -1,17 +1,15 @@
|
|||
package se.scalablesolutions.akka.actor;
|
||||
|
||||
import se.scalablesolutions.akka.actor.annotation.transactionrequired;
|
||||
import se.scalablesolutions.akka.actor.annotation.inittransactionalstate;
|
||||
import se.scalablesolutions.akka.actor.*;
|
||||
import se.scalablesolutions.akka.stm.*;
|
||||
|
||||
@transactionrequired
|
||||
public class NestedTransactionalActiveObject {
|
||||
public class NestedTransactionalTypedActorImpl extends TypedTransactor implements NestedTransactionalTypedActor {
|
||||
private TransactionalMap<String, String> mapState;
|
||||
private TransactionalVector<String> vectorState;
|
||||
private Ref<String> refState;
|
||||
private boolean isInitialized = false;
|
||||
|
||||
@inittransactionalstate
|
||||
@Override
|
||||
public void init() {
|
||||
if (!isInitialized) {
|
||||
mapState = new TransactionalMap();
|
||||
|
|
@ -25,62 +23,37 @@ public class NestedTransactionalActiveObject {
|
|||
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);
|
||||
}
|
||||
|
||||
|
||||
public void success(String key, String msg) {
|
||||
mapState.put(key, msg);
|
||||
vectorState.add(msg);
|
||||
refState.swap(msg);
|
||||
}
|
||||
|
||||
|
||||
public String failure(String key, String msg, ActiveObjectFailer failer) {
|
||||
public String failure(String key, String msg, TypedActorFailer failer) {
|
||||
mapState.put(key, msg);
|
||||
vectorState.add(msg);
|
||||
refState.swap(msg);
|
||||
failer.fail();
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
||||
public void thisMethodHangs(String key, String msg, ActiveObjectFailer failer) {
|
||||
setMapState(key, msg);
|
||||
}
|
||||
|
||||
/*
|
||||
public void clashOk(String key, String msg, InMemClasher clasher) {
|
||||
mapState.put(key, msg);
|
||||
clasher.clash();
|
||||
}
|
||||
|
||||
public void clashNotOk(String key, String msg, InMemClasher clasher) {
|
||||
mapState.put(key, msg);
|
||||
clasher.clash();
|
||||
this.success("clash", "clash");
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
|
@ -2,36 +2,7 @@ package se.scalablesolutions.akka.actor;
|
|||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
public class SamplePojo {
|
||||
|
||||
private CountDownLatch latch;
|
||||
|
||||
public boolean _pre = false;
|
||||
public boolean _post = false;
|
||||
public boolean _down = false;
|
||||
|
||||
public CountDownLatch newCountdownLatch(int count) {
|
||||
latch = new CountDownLatch(count);
|
||||
return latch;
|
||||
}
|
||||
|
||||
public String fail() {
|
||||
throw new RuntimeException("expected");
|
||||
}
|
||||
|
||||
public void pre() {
|
||||
_pre = true;
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
public void post() {
|
||||
_post = true;
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
public void down() {
|
||||
_down = true;
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
}
|
||||
public interface SamplePojo {
|
||||
public String greet(String s);
|
||||
public String fail();
|
||||
}
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
package se.scalablesolutions.akka.actor;
|
||||
|
||||
import se.scalablesolutions.akka.actor.annotation.postrestart;
|
||||
import se.scalablesolutions.akka.actor.annotation.prerestart;
|
||||
import se.scalablesolutions.akka.actor.annotation.shutdown;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
public class SamplePojoAnnotated {
|
||||
|
||||
private CountDownLatch latch;
|
||||
|
||||
public boolean _pre = false;
|
||||
public boolean _post = false;
|
||||
public boolean _down = false;
|
||||
|
||||
public SamplePojoAnnotated() {
|
||||
latch = new CountDownLatch(1);
|
||||
}
|
||||
|
||||
public CountDownLatch newCountdownLatch(int count) {
|
||||
latch = new CountDownLatch(count);
|
||||
return latch;
|
||||
}
|
||||
|
||||
public String greet(String s) {
|
||||
return "hello " + s;
|
||||
}
|
||||
|
||||
public String fail() {
|
||||
throw new RuntimeException("expected");
|
||||
}
|
||||
|
||||
@prerestart
|
||||
public void pre() {
|
||||
_pre = true;
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@postrestart
|
||||
public void post() {
|
||||
_post = true;
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@shutdown
|
||||
public void down() {
|
||||
_down = true;
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package se.scalablesolutions.akka.actor;
|
||||
|
||||
import se.scalablesolutions.akka.actor.*;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
public class SamplePojoImpl extends TypedActor implements SamplePojo {
|
||||
|
||||
public static CountDownLatch latch = new CountDownLatch(1);
|
||||
|
||||
public static boolean _pre = false;
|
||||
public static boolean _post = false;
|
||||
public static boolean _down = false;
|
||||
public static void reset() {
|
||||
_pre = false;
|
||||
_post = false;
|
||||
_down = false;
|
||||
}
|
||||
|
||||
public String greet(String s) {
|
||||
return "hello " + s;
|
||||
}
|
||||
|
||||
public String fail() {
|
||||
throw new RuntimeException("expected");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRestart(Throwable e) {
|
||||
_pre = true;
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postRestart(Throwable e) {
|
||||
_post = true;
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
_down = true;
|
||||
latch.countDown();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,48 +1,11 @@
|
|||
package se.scalablesolutions.akka.actor;
|
||||
|
||||
import se.scalablesolutions.akka.actor.annotation.prerestart;
|
||||
import se.scalablesolutions.akka.actor.annotation.postrestart;
|
||||
import se.scalablesolutions.akka.actor.ActiveObjectContext;
|
||||
import se.scalablesolutions.akka.dispatch.CompletableFuture;
|
||||
|
||||
public class SimpleJavaPojo {
|
||||
|
||||
ActiveObjectContext context;
|
||||
|
||||
public boolean pre = false;
|
||||
public boolean post = false;
|
||||
|
||||
private String name;
|
||||
|
||||
public Object getSender() {
|
||||
return context.getSender();
|
||||
}
|
||||
|
||||
public CompletableFuture<Object> getSenderFuture() {
|
||||
return context.getSenderFuture();
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@prerestart
|
||||
public void pre() {
|
||||
System.out.println("** pre()");
|
||||
pre = true;
|
||||
}
|
||||
|
||||
@postrestart
|
||||
public void post() {
|
||||
System.out.println("** post()");
|
||||
post = true;
|
||||
}
|
||||
|
||||
public void throwException() {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
public interface SimpleJavaPojo {
|
||||
public Object getSender();
|
||||
public CompletableFuture<Object> getSenderFuture();
|
||||
public void setName(String name);
|
||||
public String getName();
|
||||
public void throwException();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,19 +2,8 @@ package se.scalablesolutions.akka.actor;
|
|||
|
||||
import se.scalablesolutions.akka.dispatch.CompletableFuture;
|
||||
|
||||
public class SimpleJavaPojoCaller {
|
||||
|
||||
SimpleJavaPojo pojo;
|
||||
|
||||
public void setPojo(SimpleJavaPojo pojo) {
|
||||
this.pojo = pojo;
|
||||
}
|
||||
|
||||
public Object getSenderFromSimpleJavaPojo() {
|
||||
return pojo.getSender();
|
||||
}
|
||||
|
||||
public CompletableFuture<Object> getSenderFutureFromSimpleJavaPojo() {
|
||||
return pojo.getSenderFuture();
|
||||
}
|
||||
public interface SimpleJavaPojoCaller {
|
||||
public void setPojo(SimpleJavaPojo pojo);
|
||||
public Object getSenderFromSimpleJavaPojo();
|
||||
public CompletableFuture<Object> getSenderFutureFromSimpleJavaPojo();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
package se.scalablesolutions.akka.actor;
|
||||
|
||||
import se.scalablesolutions.akka.actor.*;
|
||||
import se.scalablesolutions.akka.dispatch.CompletableFuture;
|
||||
|
||||
public class SimpleJavaPojoCallerImpl extends TypedActor implements SimpleJavaPojoCaller {
|
||||
|
||||
SimpleJavaPojo pojo;
|
||||
|
||||
public void setPojo(SimpleJavaPojo pojo) {
|
||||
this.pojo = pojo;
|
||||
}
|
||||
|
||||
public Object getSenderFromSimpleJavaPojo() {
|
||||
return pojo.getSender();
|
||||
}
|
||||
|
||||
public CompletableFuture<Object> getSenderFutureFromSimpleJavaPojo() {
|
||||
return pojo.getSenderFuture();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
package se.scalablesolutions.akka.actor;
|
||||
|
||||
import se.scalablesolutions.akka.actor.*;
|
||||
import se.scalablesolutions.akka.dispatch.CompletableFuture;
|
||||
|
||||
public class SimpleJavaPojoImpl extends TypedActor implements SimpleJavaPojo {
|
||||
|
||||
public static boolean _pre = false;
|
||||
public static boolean _post = false;
|
||||
public static boolean _down = false;
|
||||
public static void reset() {
|
||||
_pre = false;
|
||||
_post = false;
|
||||
_down = false;
|
||||
}
|
||||
|
||||
private String name;
|
||||
|
||||
public Object getSender() {
|
||||
return getContext().getSender();
|
||||
}
|
||||
|
||||
public CompletableFuture<Object> getSenderFuture() {
|
||||
return getContext().getSenderFuture();
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRestart(Throwable e) {
|
||||
_pre = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postRestart(Throwable e) {
|
||||
_post = true;
|
||||
}
|
||||
|
||||
public void throwException() {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package se.scalablesolutions.akka.actor;
|
||||
|
||||
import se.scalablesolutions.akka.actor.*;
|
||||
|
||||
public class TestUntypedActor extends UntypedActor {
|
||||
public void onReceive(Object message, ActorContext context) {
|
||||
System.out.println("TestUntypedActor got " + message);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package se.scalablesolutions.akka.actor;
|
||||
|
||||
public interface TransactionalTypedActor {
|
||||
public String getMapState(String key);
|
||||
public String getVectorState();
|
||||
public String getRefState();
|
||||
public void setMapState(String key, String msg);
|
||||
public void setVectorState(String msg);
|
||||
public void setRefState(String msg);
|
||||
public void success(String key, String msg);
|
||||
public void success(String key, String msg, NestedTransactionalTypedActor nested);
|
||||
public String failure(String key, String msg, TypedActorFailer failer);
|
||||
public String failure(String key, String msg, NestedTransactionalTypedActor nested, TypedActorFailer failer);
|
||||
}
|
||||
|
|
@ -1,20 +1,16 @@
|
|||
package se.scalablesolutions.akka.actor;
|
||||
|
||||
import se.scalablesolutions.akka.actor.annotation.transactionrequired;
|
||||
import se.scalablesolutions.akka.actor.annotation.prerestart;
|
||||
import se.scalablesolutions.akka.actor.annotation.postrestart;
|
||||
import se.scalablesolutions.akka.actor.annotation.inittransactionalstate;
|
||||
import se.scalablesolutions.akka.actor.*;
|
||||
import se.scalablesolutions.akka.stm.*;
|
||||
|
||||
@transactionrequired
|
||||
public class TransactionalActiveObject {
|
||||
public class TransactionalTypedActorImpl extends TypedTransactor implements TransactionalTypedActor {
|
||||
private TransactionalMap<String, String> mapState;
|
||||
private TransactionalVector<String> vectorState;
|
||||
private Ref<String> refState;
|
||||
private boolean isInitialized = false;
|
||||
|
||||
@inittransactionalstate
|
||||
public void init() {
|
||||
|
||||
@Override
|
||||
public void initTransactionalState() {
|
||||
if (!isInitialized) {
|
||||
mapState = new TransactionalMap();
|
||||
vectorState = new TransactionalVector();
|
||||
|
|
@ -53,14 +49,14 @@ public class TransactionalActiveObject {
|
|||
refState.swap(msg);
|
||||
}
|
||||
|
||||
public void success(String key, String msg, NestedTransactionalActiveObject nested) {
|
||||
public void success(String key, String msg, NestedTransactionalTypedActor nested) {
|
||||
mapState.put(key, msg);
|
||||
vectorState.add(msg);
|
||||
refState.swap(msg);
|
||||
nested.success(key, msg);
|
||||
}
|
||||
|
||||
public String failure(String key, String msg, ActiveObjectFailer failer) {
|
||||
public String failure(String key, String msg, TypedActorFailer failer) {
|
||||
mapState.put(key, msg);
|
||||
vectorState.add(msg);
|
||||
refState.swap(msg);
|
||||
|
|
@ -68,7 +64,7 @@ public class TransactionalActiveObject {
|
|||
return msg;
|
||||
}
|
||||
|
||||
public String failure(String key, String msg, NestedTransactionalActiveObject nested, ActiveObjectFailer failer) {
|
||||
public String failure(String key, String msg, NestedTransactionalTypedActor nested, TypedActorFailer failer) {
|
||||
mapState.put(key, msg);
|
||||
vectorState.add(msg);
|
||||
refState.swap(msg);
|
||||
|
|
@ -76,17 +72,13 @@ public class TransactionalActiveObject {
|
|||
return msg;
|
||||
}
|
||||
|
||||
public void thisMethodHangs(String key, String msg, ActiveObjectFailer failer) {
|
||||
setMapState(key, msg);
|
||||
}
|
||||
|
||||
@prerestart
|
||||
public void preRestart() {
|
||||
@Override
|
||||
public void preRestart(Throwable e) {
|
||||
System.out.println("################ PRE RESTART");
|
||||
}
|
||||
|
||||
@postrestart
|
||||
public void postRestart() {
|
||||
@Override
|
||||
public void postRestart(Throwable e) {
|
||||
System.out.println("################ POST RESTART");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package se.scalablesolutions.akka.actor;
|
||||
|
||||
public interface TypedActorFailer extends java.io.Serializable {
|
||||
public int fail();
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package se.scalablesolutions.akka.actor;
|
||||
|
||||
import se.scalablesolutions.akka.actor.*;
|
||||
|
||||
public class TypedActorFailerImpl extends TypedActor implements TypedActorFailer {
|
||||
public int fail() {
|
||||
throw new RuntimeException("expected");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
<aspectwerkz>
|
||||
<system id="akka">
|
||||
<package name="se.scalablesolutions.akka.actor">
|
||||
<aspect class="ActiveObjectAspect" />
|
||||
<aspect class="TypedActorAspect" />
|
||||
</package>
|
||||
</system>
|
||||
</aspectwerkz>
|
||||
|
|
|
|||
|
|
@ -1,167 +0,0 @@
|
|||
package se.scalablesolutions.akka.actor
|
||||
|
||||
import org.junit.runner.RunWith
|
||||
import org.scalatest.{BeforeAndAfterAll, Spec}
|
||||
import org.scalatest.junit.JUnitRunner
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
|
||||
import se.scalablesolutions.akka.actor.ActiveObject._
|
||||
|
||||
import se.scalablesolutions.akka.config.{OneForOneStrategy, ActiveObjectConfigurator}
|
||||
import se.scalablesolutions.akka.config.JavaConfig._
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
@RunWith(classOf[JUnitRunner])
|
||||
class ActiveObjectLifecycleSpec extends Spec with ShouldMatchers with BeforeAndAfterAll {
|
||||
var conf1: ActiveObjectConfigurator = _
|
||||
var conf2: ActiveObjectConfigurator = _
|
||||
var conf3: ActiveObjectConfigurator = _
|
||||
var conf4: ActiveObjectConfigurator = _
|
||||
|
||||
override protected def beforeAll() = {
|
||||
val strategy = new RestartStrategy(new AllForOne(), 3, 1000, Array(classOf[Exception]))
|
||||
val comp1 = new Component(classOf[SamplePojoAnnotated], new LifeCycle(new Permanent()), 1000)
|
||||
val comp2 = new Component(classOf[SamplePojoAnnotated], new LifeCycle(new Temporary()), 1000)
|
||||
val comp3 = new Component(classOf[SamplePojo], new LifeCycle(new Permanent(), new RestartCallbacks("pre", "post")), 1000)
|
||||
val comp4 = new Component(classOf[SamplePojo], new LifeCycle(new Temporary(), new ShutdownCallback("down")), 1000)
|
||||
conf1 = new ActiveObjectConfigurator().configure(strategy, Array(comp1)).supervise
|
||||
conf2 = new ActiveObjectConfigurator().configure(strategy, Array(comp2)).supervise
|
||||
conf3 = new ActiveObjectConfigurator().configure(strategy, Array(comp3)).supervise
|
||||
conf4 = new ActiveObjectConfigurator().configure(strategy, Array(comp4)).supervise
|
||||
}
|
||||
|
||||
override protected def afterAll() = {
|
||||
conf1.stop
|
||||
conf2.stop
|
||||
conf3.stop
|
||||
conf4.stop
|
||||
}
|
||||
|
||||
describe("ActiveObject lifecycle management") {
|
||||
it("should restart supervised, annotated active object on failure") {
|
||||
val obj = conf1.getInstance[SamplePojoAnnotated](classOf[SamplePojoAnnotated])
|
||||
val cdl = obj.newCountdownLatch(2)
|
||||
assert(AspectInitRegistry.initFor(obj) ne null)
|
||||
try {
|
||||
obj.fail
|
||||
fail("expected exception not thrown")
|
||||
} catch {
|
||||
case e: RuntimeException => {
|
||||
cdl.await
|
||||
assert(obj._pre)
|
||||
assert(obj._post)
|
||||
assert(!obj._down)
|
||||
assert(AspectInitRegistry.initFor(obj) ne null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
it("should shutdown supervised, annotated active object on failure") {
|
||||
val obj = conf2.getInstance[SamplePojoAnnotated](classOf[SamplePojoAnnotated])
|
||||
val cdl = obj.newCountdownLatch(1)
|
||||
assert(AspectInitRegistry.initFor(obj) ne null)
|
||||
try {
|
||||
obj.fail
|
||||
fail("expected exception not thrown")
|
||||
} catch {
|
||||
case e: RuntimeException => {
|
||||
cdl.await
|
||||
assert(!obj._pre)
|
||||
assert(!obj._post)
|
||||
assert(obj._down)
|
||||
assert(AspectInitRegistry.initFor(obj) eq null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
it("should restart supervised, non-annotated active object on failure") {
|
||||
val obj = conf3.getInstance[SamplePojo](classOf[SamplePojo])
|
||||
val cdl = obj.newCountdownLatch(2)
|
||||
assert(AspectInitRegistry.initFor(obj) ne null)
|
||||
try {
|
||||
obj.fail
|
||||
fail("expected exception not thrown")
|
||||
} catch {
|
||||
case e: RuntimeException => {
|
||||
cdl.await
|
||||
assert(obj._pre)
|
||||
assert(obj._post)
|
||||
assert(!obj._down)
|
||||
assert(AspectInitRegistry.initFor(obj) ne null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
it("should shutdown supervised, non-annotated active object on failure") {
|
||||
val obj = conf4.getInstance[SamplePojo](classOf[SamplePojo])
|
||||
val cdl = obj.newCountdownLatch(1)
|
||||
assert(AspectInitRegistry.initFor(obj) ne null)
|
||||
try {
|
||||
obj.fail
|
||||
fail("expected exception not thrown")
|
||||
} catch {
|
||||
case e: RuntimeException => {
|
||||
cdl.await
|
||||
assert(!obj._pre)
|
||||
assert(!obj._post)
|
||||
assert(obj._down)
|
||||
assert(AspectInitRegistry.initFor(obj) eq null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
it("should shutdown non-supervised, annotated active object on ActiveObject.stop") {
|
||||
val obj = ActiveObject.newInstance(classOf[SamplePojoAnnotated])
|
||||
assert(AspectInitRegistry.initFor(obj) ne null)
|
||||
assert("hello akka" === obj.greet("akka"))
|
||||
ActiveObject.stop(obj)
|
||||
assert(AspectInitRegistry.initFor(obj) eq null)
|
||||
assert(!obj._pre)
|
||||
assert(!obj._post)
|
||||
assert(obj._down)
|
||||
try {
|
||||
obj.greet("akka")
|
||||
fail("access to stopped active object")
|
||||
} catch {
|
||||
case e: Exception => { /* test passed */ }
|
||||
}
|
||||
}
|
||||
|
||||
it("should shutdown non-supervised, annotated active object on ActorRegistry.shutdownAll") {
|
||||
val obj = ActiveObject.newInstance(classOf[SamplePojoAnnotated])
|
||||
assert(AspectInitRegistry.initFor(obj) ne null)
|
||||
assert("hello akka" === obj.greet("akka"))
|
||||
ActorRegistry.shutdownAll
|
||||
assert(AspectInitRegistry.initFor(obj) eq null)
|
||||
assert(!obj._pre)
|
||||
assert(!obj._post)
|
||||
assert(obj._down)
|
||||
try {
|
||||
obj.greet("akka")
|
||||
fail("access to stopped active object")
|
||||
} catch {
|
||||
case e: Exception => { /* test passed */ }
|
||||
}
|
||||
}
|
||||
|
||||
it("should shutdown non-supervised, non-initialized active object on ActiveObject.stop") {
|
||||
val obj = ActiveObject.newInstance(classOf[SamplePojoAnnotated])
|
||||
ActiveObject.stop(obj)
|
||||
assert(!obj._pre)
|
||||
assert(!obj._post)
|
||||
assert(obj._down)
|
||||
}
|
||||
|
||||
it("both preRestart and postRestart methods should be invoked when an actor is restarted") {
|
||||
val pojo = ActiveObject.newInstance(classOf[SimpleJavaPojo])
|
||||
val supervisor = ActiveObject.newInstance(classOf[SimpleJavaPojo])
|
||||
link(supervisor,pojo, new OneForOneStrategy(3, 2000),Array(classOf[Throwable]))
|
||||
pojo.throwException
|
||||
Thread.sleep(500)
|
||||
pojo.pre should be(true)
|
||||
pojo.post should be(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
/**
|
||||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
|
||||
|
|
@ -14,7 +14,7 @@ import org.junit.runner.RunWith
|
|||
import se.scalablesolutions.akka.actor._
|
||||
|
||||
@RunWith(classOf[JUnitRunner])
|
||||
class NestedTransactionalActiveObjectSpec extends
|
||||
class NestedTransactionalTypedActorSpec extends
|
||||
Spec with
|
||||
ShouldMatchers with
|
||||
BeforeAndAfterAll {
|
||||
|
|
@ -25,14 +25,12 @@ class NestedTransactionalActiveObjectSpec extends
|
|||
// ActorRegistry.shutdownAll
|
||||
}
|
||||
|
||||
describe("Declaratively nested supervised transactional in-memory Active Object") {
|
||||
describe("Declaratively nested supervised transactional in-memory TypedActor") {
|
||||
|
||||
it("map should not rollback state for stateful server in case of success") {
|
||||
val stateful = ActiveObject.newInstance(classOf[TransactionalActiveObject])
|
||||
stateful.init
|
||||
val stateful = TypedActor.newInstance(classOf[TransactionalTypedActor], classOf[TransactionalTypedActorImpl])
|
||||
stateful.setMapState("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "init") // set init state
|
||||
val nested = ActiveObject.newInstance(classOf[NestedTransactionalActiveObject])
|
||||
nested.init
|
||||
val nested = TypedActor.newInstance(classOf[NestedTransactionalTypedActor], classOf[NestedTransactionalTypedActorImpl])
|
||||
nested.setMapState("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "init") // set init state
|
||||
stateful.success("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state", nested) // transactionrequired
|
||||
stateful.getMapState("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess") should equal("new state")
|
||||
|
|
@ -40,13 +38,11 @@ class NestedTransactionalActiveObjectSpec extends
|
|||
}
|
||||
|
||||
it("map should rollback state for stateful server in case of failure") {
|
||||
val stateful = ActiveObject.newInstance(classOf[TransactionalActiveObject])
|
||||
stateful.init
|
||||
val stateful = TypedActor.newInstance(classOf[TransactionalTypedActor], classOf[TransactionalTypedActorImpl])
|
||||
stateful.setMapState("testShouldRollbackStateForStatefulServerInCaseOfFailure", "init") // set init state
|
||||
val nested = ActiveObject.newInstance(classOf[NestedTransactionalActiveObject])
|
||||
nested.init
|
||||
val nested = TypedActor.newInstance(classOf[NestedTransactionalTypedActor], classOf[NestedTransactionalTypedActorImpl])
|
||||
nested.setMapState("testShouldRollbackStateForStatefulServerInCaseOfFailure", "init") // set init state
|
||||
val failer = ActiveObject.newInstance(classOf[ActiveObjectFailer])
|
||||
val failer = TypedActor.newInstance(classOf[TypedActorFailer], classOf[TypedActorFailerImpl])
|
||||
try {
|
||||
stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", nested, failer)
|
||||
fail("should have thrown an exception")
|
||||
|
|
@ -56,11 +52,9 @@ class NestedTransactionalActiveObjectSpec extends
|
|||
}
|
||||
|
||||
it("vector should not rollback state for stateful server in case of success") {
|
||||
val stateful = ActiveObject.newInstance(classOf[TransactionalActiveObject])
|
||||
stateful.init
|
||||
val stateful = TypedActor.newInstance(classOf[TransactionalTypedActor], classOf[TransactionalTypedActorImpl])
|
||||
stateful.setVectorState("init") // set init state
|
||||
val nested = ActiveObject.newInstance(classOf[NestedTransactionalActiveObject])
|
||||
nested.init
|
||||
val nested = TypedActor.newInstance(classOf[NestedTransactionalTypedActor], classOf[NestedTransactionalTypedActorImpl])
|
||||
nested.setVectorState("init") // set init state
|
||||
stateful.success("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state", nested) // transactionrequired
|
||||
stateful.getVectorState should equal("new state")
|
||||
|
|
@ -68,13 +62,11 @@ class NestedTransactionalActiveObjectSpec extends
|
|||
}
|
||||
|
||||
it("vector should rollback state for stateful server in case of failure") {
|
||||
val stateful = ActiveObject.newInstance(classOf[TransactionalActiveObject])
|
||||
stateful.init
|
||||
val stateful = TypedActor.newInstance(classOf[TransactionalTypedActor], classOf[TransactionalTypedActorImpl])
|
||||
stateful.setVectorState("init") // set init state
|
||||
val nested = ActiveObject.newInstance(classOf[NestedTransactionalActiveObject])
|
||||
nested.init
|
||||
val nested = TypedActor.newInstance(classOf[NestedTransactionalTypedActor], classOf[NestedTransactionalTypedActorImpl])
|
||||
nested.setVectorState("init") // set init state
|
||||
val failer = ActiveObject.newInstance(classOf[ActiveObjectFailer])
|
||||
val failer = TypedActor.newInstance(classOf[TypedActorFailer], classOf[TypedActorFailerImpl])
|
||||
try {
|
||||
stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", nested, failer)
|
||||
fail("should have thrown an exception")
|
||||
|
|
@ -84,10 +76,8 @@ class NestedTransactionalActiveObjectSpec extends
|
|||
}
|
||||
|
||||
it("ref should not rollback state for stateful server in case of success") {
|
||||
val stateful = ActiveObject.newInstance(classOf[TransactionalActiveObject])
|
||||
stateful.init
|
||||
val nested = ActiveObject.newInstance(classOf[NestedTransactionalActiveObject])
|
||||
nested.init
|
||||
val stateful = TypedActor.newInstance(classOf[TransactionalTypedActor], classOf[TransactionalTypedActorImpl])
|
||||
val nested = TypedActor.newInstance(classOf[NestedTransactionalTypedActor], classOf[NestedTransactionalTypedActorImpl])
|
||||
stateful.setRefState("init") // set init state
|
||||
nested.setRefState("init") // set init state
|
||||
stateful.success("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state", nested)
|
||||
|
|
@ -96,13 +86,11 @@ class NestedTransactionalActiveObjectSpec extends
|
|||
}
|
||||
|
||||
it("ref should rollback state for stateful server in case of failure") {
|
||||
val stateful = ActiveObject.newInstance(classOf[TransactionalActiveObject])
|
||||
stateful.init
|
||||
val nested = ActiveObject.newInstance(classOf[NestedTransactionalActiveObject])
|
||||
nested.init
|
||||
val stateful = TypedActor.newInstance(classOf[TransactionalTypedActor], classOf[TransactionalTypedActorImpl])
|
||||
val nested = TypedActor.newInstance(classOf[NestedTransactionalTypedActor], classOf[NestedTransactionalTypedActorImpl])
|
||||
stateful.setRefState("init") // set init state
|
||||
nested.setRefState("init") // set init state
|
||||
val failer = ActiveObject.newInstance(classOf[ActiveObjectFailer])
|
||||
val failer = TypedActor.newInstance(classOf[TypedActorFailer], classOf[TypedActorFailerImpl])
|
||||
try {
|
||||
stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", nested, failer)
|
||||
fail("should have thrown an exception")
|
||||
|
|
@ -2,33 +2,36 @@
|
|||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
|
||||
package se.scalablesolutions.akka.actor.remote
|
||||
package se.scalablesolutions.akka.actor
|
||||
|
||||
import org.scalatest.Spec
|
||||
import org.scalatest.Assertions
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
import org.scalatest.BeforeAndAfterAll
|
||||
import org.scalatest.junit.JUnitRunner
|
||||
import org.junit.runner.RunWith
|
||||
import se.scalablesolutions.akka.config.Config
|
||||
import se.scalablesolutions.akka.config.ActiveObjectConfigurator
|
||||
import se.scalablesolutions.akka.remote.{RemoteServer, RemoteClient}
|
||||
import org.junit.{Test, Before, After}
|
||||
|
||||
object RemoteTransactionalActiveObjectSpec {
|
||||
import se.scalablesolutions.akka.config.Config
|
||||
import se.scalablesolutions.akka.config.TypedActorConfigurator
|
||||
import se.scalablesolutions.akka.remote.{RemoteNode, RemoteServer, RemoteClient}
|
||||
|
||||
object RemoteTransactionalTypedActorSpec {
|
||||
val HOSTNAME = "localhost"
|
||||
val PORT = 9988
|
||||
var server: RemoteServer = null
|
||||
}
|
||||
|
||||
@RunWith(classOf[JUnitRunner])
|
||||
class RemoteTransactionalActiveObjectSpec extends
|
||||
class RemoteTransactionalTypedActorSpec extends
|
||||
Spec with
|
||||
ShouldMatchers with
|
||||
BeforeAndAfterAll {
|
||||
|
||||
import RemoteTransactionalActiveObjectSpec._
|
||||
import RemoteTransactionalTypedActorSpec._
|
||||
Config.config
|
||||
|
||||
private val conf = new ActiveObjectConfigurator
|
||||
private val conf = new TypedActorConfigurator
|
||||
private var messageLog = ""
|
||||
|
||||
override def beforeAll = {
|
||||
|
|
@ -48,19 +51,19 @@ class RemoteTransactionalActiveObjectSpec extends
|
|||
}
|
||||
}
|
||||
|
||||
describe("Remote transactional in-memory Active Object ") {
|
||||
describe("Remote transactional in-memory TypedActor ") {
|
||||
/*
|
||||
it("map should not rollback state for stateful server in case of success") {
|
||||
val stateful = ActiveObject.newRemoteInstance(classOf[TransactionalActiveObject], 1000, HOSTNAME, PORT)
|
||||
val stateful = TypedActor.newRemoteInstance(classOf[TransactionalTypedActor], 1000, HOSTNAME, PORT)
|
||||
stateful.setMapState("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "init") // set init state
|
||||
stateful.success("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state") // transactionrequired
|
||||
stateful.getMapState("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess") should equal("new state")
|
||||
}
|
||||
|
||||
it("map should rollback state for stateful server in case of failure") {
|
||||
val stateful = ActiveObject.newRemoteInstance(classOf[TransactionalActiveObject], 1000, HOSTNAME, PORT)
|
||||
val stateful = TypedActor.newRemoteInstance(classOf[TransactionalTypedActor], 1000, HOSTNAME, PORT)
|
||||
stateful.setMapState("testShouldRollbackStateForStatefulServerInCaseOfFailure", "init") // set init state
|
||||
val failer =ActiveObject.newRemoteInstance(classOf[ActiveObjectFailer], 1000, HOSTNAME, PORT) //conf.getInstance(classOf[ActiveObjectFailer])
|
||||
val failer =TypedActor.newRemoteInstance(classOf[TypedActorFailer], 1000, HOSTNAME, PORT) //conf.getInstance(classOf[TypedActorFailer])
|
||||
try {
|
||||
stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer) // call failing transactionrequired method
|
||||
fail("should have thrown an exception")
|
||||
|
|
@ -69,16 +72,16 @@ class RemoteTransactionalActiveObjectSpec extends
|
|||
}
|
||||
|
||||
it("vector should not rollback state for stateful server in case of success") {
|
||||
val stateful = ActiveObject.newRemoteInstance(classOf[TransactionalActiveObject], 1000, HOSTNAME, PORT)
|
||||
val stateful = TypedActor.newRemoteInstance(classOf[TransactionalTypedActor], 1000, HOSTNAME, PORT)
|
||||
stateful.setVectorState("init") // set init state
|
||||
stateful.success("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state") // transactionrequired
|
||||
stateful.getVectorState should equal("new state")
|
||||
}
|
||||
|
||||
it("vector should rollback state for stateful server in case of failure") {
|
||||
val stateful = ActiveObject.newRemoteInstance(classOf[TransactionalActiveObject], 1000, HOSTNAME, PORT)
|
||||
val stateful = TypedActor.newRemoteInstance(classOf[TransactionalTypedActor], 1000, HOSTNAME, PORT)
|
||||
stateful.setVectorState("init") // set init state
|
||||
val failer =ActiveObject.newRemoteInstance(classOf[ActiveObjectFailer], 1000, HOSTNAME, PORT) //conf.getInstance(classOf[ActiveObjectFailer])
|
||||
val failer =TypedActor.newRemoteInstance(classOf[TypedActorFailer], 1000, HOSTNAME, PORT) //conf.getInstance(classOf[TypedActorFailer])
|
||||
try {
|
||||
stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer) // call failing transactionrequired method
|
||||
fail("should have thrown an exception")
|
||||
|
|
@ -87,16 +90,16 @@ class RemoteTransactionalActiveObjectSpec extends
|
|||
}
|
||||
|
||||
it("ref should not rollback state for stateful server in case of success") {
|
||||
val stateful = ActiveObject.newRemoteInstance(classOf[TransactionalActiveObject], 1000, HOSTNAME, PORT)
|
||||
val stateful = TypedActor.newRemoteInstance(classOf[TransactionalTypedActor], 1000, HOSTNAME, PORT)
|
||||
stateful.setRefState("init") // set init state
|
||||
stateful.success("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state") // transactionrequired
|
||||
stateful.getRefState should equal("new state")
|
||||
}
|
||||
|
||||
it("ref should rollback state for stateful server in case of failure") {
|
||||
val stateful = ActiveObject.newRemoteInstance(classOf[TransactionalActiveObject], 1000, HOSTNAME, PORT)
|
||||
val stateful = TypedActor.newRemoteInstance(classOf[TransactionalTypedActor], 1000, HOSTNAME, PORT)
|
||||
stateful.setRefState("init") // set init state
|
||||
val failer =ActiveObject.newRemoteInstance(classOf[ActiveObjectFailer], 1000, HOSTNAME, PORT) //conf.getInstance(classOf[ActiveObjectFailer])
|
||||
val failer =TypedActor.newRemoteInstance(classOf[TypedActorFailer], 1000, HOSTNAME, PORT) //conf.getInstance(classOf[TypedActorFailer])
|
||||
try {
|
||||
stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer) // call failing transactionrequired method
|
||||
fail("should have thrown an exception")
|
||||
|
|
@ -13,53 +13,57 @@ import org.junit.runner.RunWith
|
|||
|
||||
import se.scalablesolutions.akka.config.Config
|
||||
import se.scalablesolutions.akka.config._
|
||||
import se.scalablesolutions.akka.config.ActiveObjectConfigurator
|
||||
import se.scalablesolutions.akka.config.TypedActorConfigurator
|
||||
import se.scalablesolutions.akka.config.JavaConfig._
|
||||
import se.scalablesolutions.akka.actor._
|
||||
|
||||
@RunWith(classOf[JUnitRunner])
|
||||
class RestartNestedTransactionalActiveObjectSpec extends
|
||||
class RestartNestedTransactionalTypedActorSpec extends
|
||||
Spec with
|
||||
ShouldMatchers with
|
||||
BeforeAndAfterAll {
|
||||
|
||||
private val conf = new ActiveObjectConfigurator
|
||||
private val conf = new TypedActorConfigurator
|
||||
private var messageLog = ""
|
||||
|
||||
override def beforeAll {
|
||||
/*
|
||||
Config.config
|
||||
conf.configure(
|
||||
new RestartStrategy(new AllForOne, 3, 5000, List(classOf[Exception]).toArray),
|
||||
List(
|
||||
new Component(classOf[TransactionalActiveObject],
|
||||
new Component(classOf[TransactionalTypedActor],
|
||||
new LifeCycle(new Permanent),
|
||||
10000),
|
||||
new Component(classOf[NestedTransactionalActiveObject],
|
||||
new Component(classOf[NestedTransactionalTypedActor],
|
||||
new LifeCycle(new Permanent),
|
||||
10000),
|
||||
new Component(classOf[ActiveObjectFailer],
|
||||
new Component(classOf[TypedActorFailer],
|
||||
new LifeCycle(new Permanent),
|
||||
10000)
|
||||
).toArray).supervise
|
||||
*/
|
||||
}
|
||||
|
||||
override def afterAll {
|
||||
/*
|
||||
conf.stop
|
||||
ActorRegistry.shutdownAll
|
||||
*/
|
||||
}
|
||||
|
||||
describe("Restart nested supervised transactional Active Object") {
|
||||
/*
|
||||
it("map should rollback state for stateful server in case of failure") {
|
||||
val stateful = conf.getInstance(classOf[TransactionalActiveObject])
|
||||
val stateful = conf.getInstance(classOf[TransactionalTypedActor])
|
||||
stateful.init
|
||||
stateful.setMapState("testShouldRollbackStateForStatefulServerInCaseOfFailure", "init") // set init state
|
||||
|
||||
val nested = conf.getInstance(classOf[NestedTransactionalActiveObject])
|
||||
val nested = conf.getInstance(classOf[NestedTransactionalTypedActor])
|
||||
nested.init
|
||||
nested.setMapState("testShouldRollbackStateForStatefulServerInCaseOfFailure", "init") // set init state
|
||||
|
||||
val failer = conf.getInstance(classOf[ActiveObjectFailer])
|
||||
val failer = conf.getInstance(classOf[TypedActorFailer])
|
||||
try {
|
||||
stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", nested, failer)
|
||||
|
||||
|
|
@ -71,15 +75,15 @@ class RestartNestedTransactionalActiveObjectSpec extends
|
|||
}
|
||||
|
||||
it("vector should rollback state for stateful server in case of failure") {
|
||||
val stateful = conf.getInstance(classOf[TransactionalActiveObject])
|
||||
val stateful = conf.getInstance(classOf[TransactionalTypedActor])
|
||||
stateful.init
|
||||
stateful.setVectorState("init") // set init state
|
||||
|
||||
val nested = conf.getInstance(classOf[NestedTransactionalActiveObject])
|
||||
val nested = conf.getInstance(classOf[NestedTransactionalTypedActor])
|
||||
nested.init
|
||||
nested.setVectorState("init") // set init state
|
||||
|
||||
val failer = conf.getInstance(classOf[ActiveObjectFailer])
|
||||
val failer = conf.getInstance(classOf[TypedActorFailer])
|
||||
try {
|
||||
stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", nested, failer)
|
||||
|
||||
|
|
@ -91,15 +95,15 @@ class RestartNestedTransactionalActiveObjectSpec extends
|
|||
}
|
||||
|
||||
it("ref should rollback state for stateful server in case of failure") {
|
||||
val stateful = conf.getInstance(classOf[TransactionalActiveObject])
|
||||
val stateful = conf.getInstance(classOf[TransactionalTypedActor])
|
||||
stateful.init
|
||||
val nested = conf.getInstance(classOf[NestedTransactionalActiveObject])
|
||||
val nested = conf.getInstance(classOf[NestedTransactionalTypedActor])
|
||||
nested.init
|
||||
stateful.setRefState("init") // set init state
|
||||
|
||||
nested.setRefState("init") // set init state
|
||||
|
||||
val failer = conf.getInstance(classOf[ActiveObjectFailer])
|
||||
val failer = conf.getInstance(classOf[TypedActorFailer])
|
||||
try {
|
||||
stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", nested, failer)
|
||||
|
||||
|
|
@ -13,17 +13,17 @@ import org.junit.runner.RunWith
|
|||
|
||||
import se.scalablesolutions.akka.config.Config
|
||||
import se.scalablesolutions.akka.config._
|
||||
import se.scalablesolutions.akka.config.ActiveObjectConfigurator
|
||||
import se.scalablesolutions.akka.config.TypedActorConfigurator
|
||||
import se.scalablesolutions.akka.config.JavaConfig._
|
||||
import se.scalablesolutions.akka.actor._
|
||||
|
||||
@RunWith(classOf[JUnitRunner])
|
||||
class RestartTransactionalActiveObjectSpec extends
|
||||
class RestartTransactionalTypedActorSpec extends
|
||||
Spec with
|
||||
ShouldMatchers with
|
||||
BeforeAndAfterAll {
|
||||
|
||||
private val conf = new ActiveObjectConfigurator
|
||||
private val conf = new TypedActorConfigurator
|
||||
private var messageLog = ""
|
||||
|
||||
def before {
|
||||
|
|
@ -32,11 +32,11 @@ class RestartTransactionalActiveObjectSpec extends
|
|||
new RestartStrategy(new AllForOne, 3, 5000, List(classOf[Exception]).toArray),
|
||||
List(
|
||||
new Component(
|
||||
classOf[TransactionalActiveObject],
|
||||
classOf[TransactionalTypedActor],
|
||||
new LifeCycle(new Temporary),
|
||||
10000),
|
||||
new Component(
|
||||
classOf[ActiveObjectFailer],
|
||||
classOf[TypedActorFailer],
|
||||
new LifeCycle(new Temporary),
|
||||
10000)
|
||||
).toArray).supervise
|
||||
|
|
@ -51,10 +51,10 @@ class RestartTransactionalActiveObjectSpec extends
|
|||
/*
|
||||
it("map should rollback state for stateful server in case of failure") {
|
||||
before
|
||||
val stateful = conf.getInstance(classOf[TransactionalActiveObject])
|
||||
val stateful = conf.getInstance(classOf[TransactionalTypedActor])
|
||||
stateful.init
|
||||
stateful.setMapState("testShouldRollbackStateForStatefulServerInCaseOfFailure", "init")
|
||||
val failer = conf.getInstance(classOf[ActiveObjectFailer])
|
||||
val failer = conf.getInstance(classOf[TypedActorFailer])
|
||||
try {
|
||||
stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer)
|
||||
fail("should have thrown an exception")
|
||||
|
|
@ -65,10 +65,10 @@ class RestartTransactionalActiveObjectSpec extends
|
|||
|
||||
it("vector should rollback state for stateful server in case of failure") {
|
||||
before
|
||||
val stateful = conf.getInstance(classOf[TransactionalActiveObject])
|
||||
val stateful = conf.getInstance(classOf[TransactionalTypedActor])
|
||||
stateful.init
|
||||
stateful.setVectorState("init") // set init state
|
||||
val failer = conf.getInstance(classOf[ActiveObjectFailer])
|
||||
val failer = conf.getInstance(classOf[TypedActorFailer])
|
||||
try {
|
||||
stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer)
|
||||
fail("should have thrown an exception")
|
||||
|
|
@ -78,10 +78,10 @@ class RestartTransactionalActiveObjectSpec extends
|
|||
}
|
||||
|
||||
it("ref should rollback state for stateful server in case of failure") {
|
||||
val stateful = conf.getInstance(classOf[TransactionalActiveObject])
|
||||
val stateful = conf.getInstance(classOf[TransactionalTypedActor])
|
||||
stateful.init
|
||||
stateful.setRefState("init") // set init state
|
||||
val failer = conf.getInstance(classOf[ActiveObjectFailer])
|
||||
val failer = conf.getInstance(classOf[TypedActorFailer])
|
||||
try {
|
||||
stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer)
|
||||
fail("should have thrown an exception")
|
||||
|
|
@ -14,7 +14,7 @@ import org.junit.runner.RunWith
|
|||
import se.scalablesolutions.akka.actor._
|
||||
|
||||
@RunWith(classOf[JUnitRunner])
|
||||
class TransactionalActiveObjectSpec extends
|
||||
class TransactionalTypedActorSpec extends
|
||||
Spec with
|
||||
ShouldMatchers with
|
||||
BeforeAndAfterAll {
|
||||
|
|
@ -27,18 +27,16 @@ class TransactionalActiveObjectSpec extends
|
|||
|
||||
describe("Declaratively supervised transactional in-memory Active Object ") {
|
||||
it("map should not rollback state for stateful server in case of success") {
|
||||
val stateful = ActiveObject.newInstance(classOf[TransactionalActiveObject])
|
||||
stateful.init
|
||||
val stateful = TypedActor.newInstance(classOf[TransactionalTypedActor], classOf[TransactionalTypedActorImpl])
|
||||
stateful.setMapState("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "init")
|
||||
stateful.success("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state")
|
||||
stateful.getMapState("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess") should equal("new state")
|
||||
}
|
||||
|
||||
it("map should rollback state for stateful server in case of failure") {
|
||||
val stateful = ActiveObject.newInstance(classOf[TransactionalActiveObject])
|
||||
stateful.init
|
||||
val stateful = TypedActor.newInstance(classOf[TransactionalTypedActor], classOf[TransactionalTypedActorImpl])
|
||||
stateful.setMapState("testShouldRollbackStateForStatefulServerInCaseOfFailure", "init")
|
||||
val failer = ActiveObject.newInstance(classOf[ActiveObjectFailer])
|
||||
val failer = TypedActor.newInstance(classOf[TypedActorFailer], classOf[TypedActorFailerImpl])
|
||||
try {
|
||||
stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer)
|
||||
fail("should have thrown an exception")
|
||||
|
|
@ -47,18 +45,16 @@ class TransactionalActiveObjectSpec extends
|
|||
}
|
||||
|
||||
it("vector should not rollback state for stateful server in case of success") {
|
||||
val stateful = ActiveObject.newInstance(classOf[TransactionalActiveObject])
|
||||
stateful.init
|
||||
val stateful = TypedActor.newInstance(classOf[TransactionalTypedActor], classOf[TransactionalTypedActorImpl])
|
||||
stateful.setVectorState("init") // set init state
|
||||
stateful.success("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state")
|
||||
stateful.getVectorState should equal("new state")
|
||||
}
|
||||
|
||||
it("vector should rollback state for stateful server in case of failure") {
|
||||
val stateful = ActiveObject.newInstance(classOf[TransactionalActiveObject])
|
||||
stateful.init
|
||||
val stateful = TypedActor.newInstance(classOf[TransactionalTypedActor], classOf[TransactionalTypedActorImpl])
|
||||
stateful.setVectorState("init") // set init state
|
||||
val failer = ActiveObject.newInstance(classOf[ActiveObjectFailer])
|
||||
val failer = TypedActor.newInstance(classOf[TypedActorFailer], classOf[TypedActorFailerImpl])
|
||||
try {
|
||||
stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer)
|
||||
fail("should have thrown an exception")
|
||||
|
|
@ -67,18 +63,16 @@ class TransactionalActiveObjectSpec extends
|
|||
}
|
||||
|
||||
it("ref should not rollback state for stateful server in case of success") {
|
||||
val stateful = ActiveObject.newInstance(classOf[TransactionalActiveObject])
|
||||
stateful.init
|
||||
val stateful = TypedActor.newInstance(classOf[TransactionalTypedActor], classOf[TransactionalTypedActorImpl])
|
||||
stateful.setRefState("init") // set init state
|
||||
stateful.success("testShouldNotRollbackStateForStatefulServerInCaseOfSuccess", "new state")
|
||||
stateful.getRefState should equal("new state")
|
||||
}
|
||||
|
||||
it("ref should rollback state for stateful server in case of failure") {
|
||||
val stateful = ActiveObject.newInstance(classOf[TransactionalActiveObject])
|
||||
stateful.init
|
||||
val stateful = TypedActor.newInstance(classOf[TransactionalTypedActor], classOf[TransactionalTypedActorImpl])
|
||||
stateful.setRefState("init") // set init state
|
||||
val failer = ActiveObject.newInstance(classOf[ActiveObjectFailer])
|
||||
val failer = TypedActor.newInstance(classOf[TypedActorFailer], classOf[TypedActorFailerImpl])
|
||||
try {
|
||||
stateful.failure("testShouldRollbackStateForStatefulServerInCaseOfFailure", "new state", failer)
|
||||
fail("should have thrown an exception")
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
package se.scalablesolutions.akka.actor
|
||||
|
||||
import org.scalatest.Spec
|
||||
import org.scalatest.Assertions
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
import org.scalatest.BeforeAndAfterAll
|
||||
import org.scalatest.junit.JUnitRunner
|
||||
|
|
@ -13,15 +14,15 @@ import org.junit.runner.RunWith
|
|||
import se.scalablesolutions.akka.dispatch.DefaultCompletableFuture;
|
||||
|
||||
@RunWith(classOf[JUnitRunner])
|
||||
class ActiveObjectContextSpec extends
|
||||
class TypedActorContextSpec extends
|
||||
Spec with
|
||||
ShouldMatchers with
|
||||
BeforeAndAfterAll {
|
||||
|
||||
describe("ActiveObjectContext") {
|
||||
it("context.sender should return the sender Active Object reference") {
|
||||
val pojo = ActiveObject.newInstance(classOf[SimpleJavaPojo])
|
||||
val pojoCaller = ActiveObject.newInstance(classOf[SimpleJavaPojoCaller])
|
||||
describe("TypedActorContext") {
|
||||
it("context.sender should return the sender TypedActor reference") {
|
||||
val pojo = TypedActor.newInstance(classOf[SimpleJavaPojo], classOf[SimpleJavaPojoImpl])
|
||||
val pojoCaller = TypedActor.newInstance(classOf[SimpleJavaPojoCaller], classOf[SimpleJavaPojoCallerImpl])
|
||||
pojoCaller.setPojo(pojo)
|
||||
try {
|
||||
pojoCaller.getSenderFromSimpleJavaPojo should equal (pojoCaller)
|
||||
|
|
@ -30,9 +31,9 @@ class ActiveObjectContextSpec extends
|
|||
}
|
||||
}
|
||||
|
||||
it("context.senderFuture should return the senderFuture Active Object reference") {
|
||||
val pojo = ActiveObject.newInstance(classOf[SimpleJavaPojo])
|
||||
val pojoCaller = ActiveObject.newInstance(classOf[SimpleJavaPojoCaller])
|
||||
it("context.senderFuture should return the senderFuture TypedActor reference") {
|
||||
val pojo = TypedActor.newInstance(classOf[SimpleJavaPojo], classOf[SimpleJavaPojoImpl])
|
||||
val pojoCaller = TypedActor.newInstance(classOf[SimpleJavaPojoCaller], classOf[SimpleJavaPojoCallerImpl])
|
||||
pojoCaller.setPojo(pojo)
|
||||
try {
|
||||
pojoCaller.getSenderFutureFromSimpleJavaPojo.getClass.getName should equal (classOf[DefaultCompletableFuture[_]].getName)
|
||||
|
|
@ -8,23 +8,25 @@ import com.google.inject.AbstractModule
|
|||
import com.google.inject.Scopes
|
||||
|
||||
import org.scalatest.Spec
|
||||
import org.scalatest.Assertions
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
import org.scalatest.BeforeAndAfterAll
|
||||
import org.scalatest.junit.JUnitRunner
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
import se.scalablesolutions.akka.config.Config
|
||||
import se.scalablesolutions.akka.config.ActiveObjectConfigurator
|
||||
import se.scalablesolutions.akka.config.TypedActorConfigurator
|
||||
import se.scalablesolutions.akka.config.JavaConfig._
|
||||
import se.scalablesolutions.akka.dispatch._
|
||||
import se.scalablesolutions.akka.dispatch.FutureTimeoutException
|
||||
|
||||
@RunWith(classOf[JUnitRunner])
|
||||
class ActiveObjectGuiceConfiguratorSpec extends
|
||||
class TypedActorGuiceConfiguratorSpec extends
|
||||
Spec with
|
||||
ShouldMatchers with
|
||||
BeforeAndAfterAll {
|
||||
|
||||
private val conf = new ActiveObjectConfigurator
|
||||
private val conf = new TypedActorConfigurator
|
||||
private var messageLog = ""
|
||||
|
||||
override def beforeAll {
|
||||
|
|
@ -38,6 +40,7 @@ class ActiveObjectGuiceConfiguratorSpec extends
|
|||
List(
|
||||
new Component(
|
||||
classOf[Foo],
|
||||
classOf[FooImpl],
|
||||
new LifeCycle(new Permanent),
|
||||
1000,
|
||||
dispatcher),
|
||||
|
|
@ -53,9 +56,9 @@ class ActiveObjectGuiceConfiguratorSpec extends
|
|||
|
||||
override def afterAll = conf.stop
|
||||
|
||||
describe("ActiveObjectGuiceConfigurator") {
|
||||
describe("TypedActorGuiceConfigurator") {
|
||||
/*
|
||||
it("should inject active object using guice") {
|
||||
it("should inject typed actor using guice") {
|
||||
messageLog = ""
|
||||
val foo = conf.getInstance(classOf[Foo])
|
||||
val bar = conf.getInstance(classOf[Bar])
|
||||
|
|
@ -79,7 +82,7 @@ class ActiveObjectGuiceConfiguratorSpec extends
|
|||
}
|
||||
}
|
||||
|
||||
it("should be able to invoke active object") {
|
||||
it("should be able to invoke typed actor") {
|
||||
messageLog = ""
|
||||
val foo = conf.getInstance(classOf[Foo])
|
||||
messageLog += foo.foo("foo ")
|
||||
|
|
@ -89,7 +92,7 @@ class ActiveObjectGuiceConfiguratorSpec extends
|
|||
messageLog should equal("foo return_foo before_bar ")
|
||||
}
|
||||
|
||||
it("should be able to invoke active object's invocation") {
|
||||
it("should be able to invoke typed actor's invocation") {
|
||||
messageLog = ""
|
||||
val foo = conf.getInstance(classOf[Foo])
|
||||
val bar = conf.getInstance(classOf[Bar])
|
||||
169
akka-core/src/test/scala/actor/TypedActorLifecycleSpec.scala
Normal file
169
akka-core/src/test/scala/actor/TypedActorLifecycleSpec.scala
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
package se.scalablesolutions.akka.actor
|
||||
|
||||
import org.junit.runner.RunWith
|
||||
import org.scalatest.{BeforeAndAfterAll, Spec}
|
||||
import org.scalatest.junit.JUnitRunner
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
|
||||
import se.scalablesolutions.akka.actor.TypedActor._
|
||||
|
||||
import se.scalablesolutions.akka.config.{OneForOneStrategy, TypedActorConfigurator}
|
||||
import se.scalablesolutions.akka.config.JavaConfig._
|
||||
|
||||
import java.util.concurrent.CountDownLatch
|
||||
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
@RunWith(classOf[JUnitRunner])
|
||||
class TypedActorLifecycleSpec extends Spec with ShouldMatchers with BeforeAndAfterAll {
|
||||
var conf1: TypedActorConfigurator = _
|
||||
var conf2: TypedActorConfigurator = _
|
||||
|
||||
override protected def beforeAll() = {
|
||||
val strategy = new RestartStrategy(new AllForOne(), 3, 1000, Array(classOf[Exception]))
|
||||
val comp3 = new Component(classOf[SamplePojo], classOf[SamplePojoImpl], new LifeCycle(new Permanent()), 1000)
|
||||
val comp4 = new Component(classOf[SamplePojo], classOf[SamplePojoImpl], new LifeCycle(new Temporary()), 1000)
|
||||
conf1 = new TypedActorConfigurator().configure(strategy, Array(comp3)).supervise
|
||||
conf2 = new TypedActorConfigurator().configure(strategy, Array(comp4)).supervise
|
||||
}
|
||||
|
||||
override protected def afterAll() = {
|
||||
conf1.stop
|
||||
conf2.stop
|
||||
}
|
||||
|
||||
describe("TypedActor lifecycle management") {
|
||||
it("should restart supervised, non-annotated typed actor on failure") {
|
||||
SamplePojoImpl.reset
|
||||
val obj = conf1.getInstance[SamplePojo](classOf[SamplePojo])
|
||||
val cdl = new CountDownLatch(2)
|
||||
SamplePojoImpl.latch = cdl
|
||||
assert(AspectInitRegistry.initFor(obj) ne null)
|
||||
try {
|
||||
obj.fail
|
||||
fail("expected exception not thrown")
|
||||
} catch {
|
||||
case e: RuntimeException => {
|
||||
cdl.await
|
||||
assert(SamplePojoImpl._pre)
|
||||
assert(SamplePojoImpl._post)
|
||||
assert(!SamplePojoImpl._down)
|
||||
assert(AspectInitRegistry.initFor(obj) ne null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
it("should shutdown supervised, non-annotated typed actor on failure") {
|
||||
SamplePojoImpl.reset
|
||||
val obj = conf2.getInstance[SamplePojo](classOf[SamplePojo])
|
||||
val cdl = new CountDownLatch(1)
|
||||
SamplePojoImpl.latch = cdl
|
||||
assert(AspectInitRegistry.initFor(obj) ne null)
|
||||
try {
|
||||
obj.fail
|
||||
fail("expected exception not thrown")
|
||||
} catch {
|
||||
case e: RuntimeException => {
|
||||
cdl.await
|
||||
assert(!SamplePojoImpl._pre)
|
||||
assert(!SamplePojoImpl._post)
|
||||
assert(SamplePojoImpl._down)
|
||||
assert(AspectInitRegistry.initFor(obj) eq null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
it("should shutdown non-supervised, non-initialized typed actor on TypedActor.stop") {
|
||||
SamplePojoImpl.reset
|
||||
val obj = TypedActor.newInstance(classOf[SamplePojo], classOf[SamplePojoImpl])
|
||||
TypedActor.stop(obj)
|
||||
assert(!SamplePojoImpl._pre)
|
||||
assert(!SamplePojoImpl._post)
|
||||
assert(SamplePojoImpl._down)
|
||||
}
|
||||
|
||||
it("both preRestart and postRestart methods should be invoked when an actor is restarted") {
|
||||
SamplePojoImpl.reset
|
||||
val pojo = TypedActor.newInstance(classOf[SimpleJavaPojo], classOf[SimpleJavaPojoImpl])
|
||||
val supervisor = TypedActor.newInstance(classOf[SimpleJavaPojo], classOf[SimpleJavaPojoImpl])
|
||||
link(supervisor, pojo, new OneForOneStrategy(3, 2000), Array(classOf[Throwable]))
|
||||
pojo.throwException
|
||||
Thread.sleep(500)
|
||||
SimpleJavaPojoImpl._pre should be(true)
|
||||
SimpleJavaPojoImpl._post should be(true)
|
||||
}
|
||||
|
||||
/*
|
||||
it("should shutdown non-supervised, annotated typed actor on TypedActor.stop") {
|
||||
val obj = TypedActor.newInstance(classOf[SamplePojoAnnotated])
|
||||
assert(AspectInitRegistry.initFor(obj) ne null)
|
||||
assert("hello akka" === obj.greet("akka"))
|
||||
TypedActor.stop(obj)
|
||||
assert(AspectInitRegistry.initFor(obj) eq null)
|
||||
assert(!obj.pre)
|
||||
assert(!obj.post)
|
||||
assert(obj.down)
|
||||
try {
|
||||
obj.greet("akka")
|
||||
fail("access to stopped typed actor")
|
||||
} catch {
|
||||
case e: Exception => {}
|
||||
}
|
||||
}
|
||||
|
||||
it("should shutdown non-supervised, annotated typed actor on ActorRegistry.shutdownAll") {
|
||||
val obj = TypedActor.newInstance(classOf[SamplePojoAnnotated])
|
||||
assert(AspectInitRegistry.initFor(obj) ne null)
|
||||
assert("hello akka" === obj.greet("akka"))
|
||||
ActorRegistry.shutdownAll
|
||||
assert(AspectInitRegistry.initFor(obj) eq null)
|
||||
assert(!obj.pre)
|
||||
assert(!obj.post)
|
||||
assert(obj.down)
|
||||
try {
|
||||
obj.greet("akka")
|
||||
fail("access to stopped typed actor")
|
||||
} catch {
|
||||
case e: Exception => { }
|
||||
}
|
||||
}
|
||||
|
||||
it("should restart supervised, annotated typed actor on failure") {
|
||||
val obj = conf1.getInstance[SamplePojoAnnotated](classOf[SamplePojoAnnotated])
|
||||
val cdl = obj.newCountdownLatch(2)
|
||||
assert(AspectInitRegistry.initFor(obj) ne null)
|
||||
try {
|
||||
obj.fail
|
||||
fail("expected exception not thrown")
|
||||
} catch {
|
||||
case e: RuntimeException => {
|
||||
cdl.await
|
||||
assert(obj.pre)
|
||||
assert(obj.post)
|
||||
assert(!obj.down)
|
||||
assert(AspectInitRegistry.initFor(obj) ne null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
it("should shutdown supervised, annotated typed actor on failure") {
|
||||
val obj = conf2.getInstance[SamplePojoAnnotated](classOf[SamplePojoAnnotated])
|
||||
val cdl = obj.newCountdownLatch(1)
|
||||
assert(AspectInitRegistry.initFor(obj) ne null)
|
||||
try {
|
||||
obj.fail
|
||||
fail("expected exception not thrown")
|
||||
} catch {
|
||||
case e: RuntimeException => {
|
||||
cdl.await
|
||||
assert(!obj.pre)
|
||||
assert(!obj.post)
|
||||
assert(obj.down)
|
||||
assert(AspectInitRegistry.initFor(obj) eq null)
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ import org.scalatest.Suite
|
|||
import org.junit.runner.RunWith
|
||||
import org.scalatest.junit.JUnitRunner
|
||||
import org.scalatest.matchers.MustMatchers
|
||||
import org.junit.Test
|
||||
import org.junit.{Before, After, Test}
|
||||
import java.util.concurrent.{ CountDownLatch, TimeUnit }
|
||||
|
||||
@RunWith(classOf[JUnitRunner])
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
package sample.camel;
|
||||
|
||||
import se.scalablesolutions.akka.actor.TypedActor;
|
||||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
public class BeanImpl implements BeanIntf {
|
||||
public class BeanImpl extends TypedActor implements BeanIntf {
|
||||
|
||||
public String foo(String s) {
|
||||
return "hello " + s;
|
||||
|
|
|
|||
|
|
@ -8,17 +8,10 @@ import se.scalablesolutions.akka.actor.annotation.consume;
|
|||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
public class ConsumerPojo1 {
|
||||
|
||||
public interface ConsumerPojo1 {
|
||||
@consume("file:data/input/pojo")
|
||||
public void foo(String body) {
|
||||
System.out.println("Received message:");
|
||||
System.out.println(body);
|
||||
}
|
||||
|
||||
public void foo(String 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);
|
||||
}
|
||||
|
||||
public String bar(@Body String body, @Header("name") String header);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
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 {
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
|
@ -7,11 +7,8 @@ import se.scalablesolutions.akka.actor.annotation.consume;
|
|||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
public class ConsumerPojo2 {
|
||||
public interface ConsumerPojo2 {
|
||||
|
||||
@consume("direct:default")
|
||||
public String foo(String body) {
|
||||
return String.format("default: %s", body);
|
||||
}
|
||||
|
||||
public String foo(String body);
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -8,11 +8,8 @@ import se.scalablesolutions.akka.actor.annotation.consume;
|
|||
/**
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
public class RemoteConsumerPojo1 {
|
||||
public interface RemoteConsumerPojo1 {
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
public String foo(@Body String body, @Header("name") String header);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,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 RemoteConsumerPojo1Impl extends TypedActor implements RemoteConsumerPojo1 {
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
|
@ -7,7 +7,7 @@ import org.apache.camel.spring.spi.ApplicationContextRegistry
|
|||
import org.springframework.context.support.ClassPathXmlApplicationContext
|
||||
|
||||
import se.scalablesolutions.akka.actor.Actor._
|
||||
import se.scalablesolutions.akka.actor.{ActiveObject, Supervisor}
|
||||
import se.scalablesolutions.akka.actor.{TypedActor, Supervisor}
|
||||
import se.scalablesolutions.akka.camel.CamelContextManager
|
||||
import se.scalablesolutions.akka.config.ScalaConfig._
|
||||
|
||||
|
|
@ -89,7 +89,7 @@ class Boot {
|
|||
// Active object example
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
ActiveObject.newInstance(classOf[ConsumerPojo1])
|
||||
TypedActor.newInstance(classOf[ConsumerPojo1], classOf[ConsumerPojo1Impl])
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package sample.camel
|
||||
|
||||
import se.scalablesolutions.akka.actor.Actor._
|
||||
import se.scalablesolutions.akka.actor.{ActiveObject, Actor, ActorRef}
|
||||
import se.scalablesolutions.akka.actor.{TypedActor, Actor, ActorRef}
|
||||
import se.scalablesolutions.akka.camel.Message
|
||||
import se.scalablesolutions.akka.remote.RemoteClient
|
||||
|
||||
|
|
@ -18,15 +18,15 @@ object ClientApplication {
|
|||
val actor1 = actorOf[RemoteActor1]
|
||||
val actor2 = RemoteClient.actorFor("remote2", "localhost", 7777)
|
||||
|
||||
val actobj1 = ActiveObject.newRemoteInstance(classOf[RemoteConsumerPojo1], "localhost", 7777)
|
||||
//val actobj2 = TODO: create reference to server-managed active object (RemoteConsumerPojo2)
|
||||
val actobj1 = TypedActor.newRemoteInstance(classOf[RemoteConsumerPojo1], classOf[RemoteConsumerPojo1Impl], "localhost", 7777)
|
||||
//val actobj2 = TODO: create reference to server-managed typed actor (RemoteConsumerPojo2)
|
||||
|
||||
actor1.start
|
||||
|
||||
println(actor1 !! Message("actor1")) // activates and publishes actor remotely
|
||||
println(actor2 !! Message("actor2")) // actor already activated and published remotely
|
||||
|
||||
println(actobj1.foo("x", "y")) // activates and publishes active object methods remotely
|
||||
println(actobj1.foo("x", "y")) // activates and publishes typed actor methods remotely
|
||||
// ...
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import org.apache.camel.builder.RouteBuilder
|
|||
import org.apache.camel.spring.spi.ApplicationContextRegistry
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext
|
||||
|
||||
import se.scalablesolutions.akka.actor.{Actor, ActorRegistry, ActiveObject}
|
||||
import se.scalablesolutions.akka.actor.{Actor, ActorRegistry, TypedActor}
|
||||
import se.scalablesolutions.akka.camel._
|
||||
import se.scalablesolutions.akka.util.Logging
|
||||
|
||||
|
|
@ -16,10 +16,9 @@ object StandaloneApplication {
|
|||
def main(args: Array[String]) {
|
||||
import CamelContextManager.context
|
||||
|
||||
// 'externally' register active objects
|
||||
// 'externally' register typed actors
|
||||
val registry = new SimpleRegistry
|
||||
registry.put("pojo1", ActiveObject.newInstance(classOf[BeanIntf], new BeanImpl))
|
||||
registry.put("pojo2", ActiveObject.newInstance(classOf[BeanImpl]))
|
||||
registry.put("pojo1", TypedActor.newInstance(classOf[BeanIntf], classOf[BeanImpl]))
|
||||
|
||||
// customize CamelContext
|
||||
CamelContextManager.init(new DefaultCamelContext(registry))
|
||||
|
|
@ -28,12 +27,12 @@ object StandaloneApplication {
|
|||
// start CamelService
|
||||
val camelService = CamelService.newInstance.load
|
||||
|
||||
// access 'externally' registered active objects
|
||||
// access 'externally' registered typed actors
|
||||
assert("hello msg1" == context.createProducerTemplate.requestBody("direct:test1", "msg1"))
|
||||
assert("hello msg2" == context.createProducerTemplate.requestBody("direct:test2", "msg2"))
|
||||
|
||||
// 'internally' register active object (requires CamelService)
|
||||
ActiveObject.newInstance(classOf[ConsumerPojo2])
|
||||
// 'internally' register typed actor (requires CamelService)
|
||||
TypedActor.newInstance(classOf[ConsumerPojo2], classOf[ConsumerPojo2Impl])
|
||||
|
||||
// internal registration is done in background. Wait a bit ...
|
||||
Thread.sleep(1000)
|
||||
|
|
@ -52,7 +51,7 @@ object StandaloneApplication {
|
|||
|
||||
class StandaloneApplicationRoute extends RouteBuilder {
|
||||
def configure = {
|
||||
// routes to active objects (in SimpleRegistry)
|
||||
// routes to typed actors (in SimpleRegistry)
|
||||
from("direct:test1").to("active-object:pojo1?method=foo")
|
||||
from("direct:test2").to("active-object:pojo2?method=foo")
|
||||
}
|
||||
|
|
@ -65,7 +64,7 @@ object StandaloneSpringApplication {
|
|||
// load Spring application context
|
||||
val appctx = new ClassPathXmlApplicationContext("/context-standalone.xml")
|
||||
|
||||
// access 'externally' registered active objects with active-object component
|
||||
// access 'externally' registered typed actors with active-object component
|
||||
assert("hello msg3" == template.requestBody("direct:test3", "msg3"))
|
||||
|
||||
// destroy Spring application context
|
||||
|
|
@ -78,7 +77,7 @@ object StandaloneSpringApplication {
|
|||
|
||||
class StandaloneSpringApplicationRoute extends RouteBuilder {
|
||||
def configure = {
|
||||
// routes to active object (in ApplicationContextRegistry)
|
||||
// routes to typed actor (in ApplicationContextRegistry)
|
||||
from("direct:test3").to("active-object:pojo3?method=foo")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,11 +4,11 @@
|
|||
|
||||
package sample.rest.java;
|
||||
|
||||
import se.scalablesolutions.akka.config.ActiveObjectConfigurator;
|
||||
import se.scalablesolutions.akka.config.TypedActorConfigurator;
|
||||
import static se.scalablesolutions.akka.config.JavaConfig.*;
|
||||
|
||||
public class Boot {
|
||||
public final static ActiveObjectConfigurator configurator = new ActiveObjectConfigurator();
|
||||
public final static TypedActorConfigurator configurator = new TypedActorConfigurator();
|
||||
static {
|
||||
configurator.configure(
|
||||
new RestartStrategy(new OneForOne(), 3, 5000, new Class[]{Exception.class}),
|
||||
|
|
|
|||
|
|
@ -4,42 +4,6 @@
|
|||
|
||||
package sample.rest.java;
|
||||
|
||||
import se.scalablesolutions.akka.actor.annotation.transactionrequired;
|
||||
import se.scalablesolutions.akka.actor.annotation.prerestart;
|
||||
import se.scalablesolutions.akka.actor.annotation.postrestart;
|
||||
import se.scalablesolutions.akka.persistence.common.PersistentMap;
|
||||
import se.scalablesolutions.akka.persistence.cassandra.CassandraStorage;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
@transactionrequired
|
||||
public class PersistentSimpleService {
|
||||
private String KEY = "COUNTER";
|
||||
|
||||
private boolean hasStartedTicking = false;
|
||||
private PersistentMap<byte[], byte[]> storage;
|
||||
|
||||
public String count() {
|
||||
if (storage == null) storage = CassandraStorage.newMap();
|
||||
if (!hasStartedTicking) {
|
||||
storage.put(KEY.getBytes(), ByteBuffer.allocate(4).putInt(0).array());
|
||||
hasStartedTicking = true;
|
||||
return "Tick: 0\n";
|
||||
} else {
|
||||
byte[] bytes = (byte[])storage.get(KEY.getBytes()).get();
|
||||
int counter = ByteBuffer.wrap(bytes).getInt();
|
||||
storage.put(KEY.getBytes(), ByteBuffer.allocate(4).putInt(counter + 1).array());
|
||||
return "Tick: " + counter + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
@prerestart
|
||||
public void preRestart() {
|
||||
System.out.println("Prepare for restart by supervisor");
|
||||
}
|
||||
|
||||
@postrestart
|
||||
public void postRestart() {
|
||||
System.out.println("Reinitialize after restart by supervisor");
|
||||
}
|
||||
public interface PersistentSimpleService {
|
||||
public String count();
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
|
||||
package sample.rest.java;
|
||||
|
||||
import se.scalablesolutions.akka.actor.TypedTransactor;
|
||||
import se.scalablesolutions.akka.persistence.common.PersistentMap;
|
||||
import se.scalablesolutions.akka.persistence.cassandra.CassandraStorage;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class PersistentSimpleServiceImpl extends TypedTransactor implements PersistentSimpleService {
|
||||
private String KEY = "COUNTER";
|
||||
|
||||
private boolean hasStartedTicking = false;
|
||||
private PersistentMap<byte[], byte[]> storage;
|
||||
|
||||
public String count() {
|
||||
if (storage == null) storage = CassandraStorage.newMap();
|
||||
if (!hasStartedTicking) {
|
||||
storage.put(KEY.getBytes(), ByteBuffer.allocate(4).putInt(0).array());
|
||||
hasStartedTicking = true;
|
||||
return "Tick: 0\n";
|
||||
} else {
|
||||
byte[] bytes = (byte[])storage.get(KEY.getBytes()).get();
|
||||
int counter = ByteBuffer.wrap(bytes).getInt();
|
||||
storage.put(KEY.getBytes(), ByteBuffer.allocate(4).putInt(counter + 1).array());
|
||||
return "Tick: " + counter + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRestart(Throwable cause) {
|
||||
System.out.println("Prepare for restart by supervisor");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postRestart(Throwable cause) {
|
||||
System.out.println("Reinitialize after restart by supervisor");
|
||||
}
|
||||
}
|
||||
|
|
@ -4,17 +4,6 @@
|
|||
|
||||
package sample.rest.java;
|
||||
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Produces;
|
||||
|
||||
import se.scalablesolutions.akka.actor.ActiveObject;
|
||||
import se.scalablesolutions.akka.actor.ActiveObjectContext;
|
||||
|
||||
public class Receiver {
|
||||
private ActiveObjectContext context = null;
|
||||
public SimpleService receive() {
|
||||
System.out.println("------ RECEIVE");
|
||||
return (SimpleService) context.getSender();
|
||||
}
|
||||
public interface Receiver {
|
||||
SimpleService receive();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
|
||||
package sample.rest.java;
|
||||
|
||||
import se.scalablesolutions.akka.actor.TypedActorContext;
|
||||
import se.scalablesolutions.akka.actor.TypedActor;
|
||||
|
||||
public class ReceiverImpl extends TypedActor implements Receiver {
|
||||
public SimpleService receive() {
|
||||
return (SimpleService) getContext().getSender();
|
||||
}
|
||||
}
|
||||
|
|
@ -4,43 +4,6 @@
|
|||
|
||||
package sample.rest.java;
|
||||
|
||||
import se.scalablesolutions.akka.actor.ActiveObject;
|
||||
import se.scalablesolutions.akka.actor.ActiveObjectContext;
|
||||
import se.scalablesolutions.akka.actor.annotation.transactionrequired;
|
||||
import se.scalablesolutions.akka.actor.annotation.prerestart;
|
||||
import se.scalablesolutions.akka.actor.annotation.postrestart;
|
||||
import se.scalablesolutions.akka.stm.TransactionalMap;
|
||||
|
||||
@transactionrequired
|
||||
public class SimpleService {
|
||||
private String KEY = "COUNTER";
|
||||
|
||||
private boolean hasStartedTicking = false;
|
||||
private TransactionalMap<String, Integer> storage;
|
||||
private Receiver receiver = ActiveObject.newInstance(Receiver.class);
|
||||
|
||||
public String count() {
|
||||
if (storage == null) storage = new TransactionalMap<String, Integer>();
|
||||
if (!hasStartedTicking) {
|
||||
storage.put(KEY, 0);
|
||||
hasStartedTicking = true;
|
||||
return "Tick: 0\n";
|
||||
} else {
|
||||
// Grabs the sender address and returns it
|
||||
//SimpleService sender = receiver.receive();
|
||||
int counter = (Integer)storage.get(KEY).get() + 1;
|
||||
storage.put(KEY, counter);
|
||||
return "Tick: " + counter + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
@prerestart
|
||||
public void preRestart() {
|
||||
System.out.println("Prepare for restart by supervisor");
|
||||
}
|
||||
|
||||
@postrestart
|
||||
public void postRestart() {
|
||||
System.out.println("Reinitialize after restart by supervisor");
|
||||
}
|
||||
public interface SimpleService {
|
||||
public String count();
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
|
||||
package sample.rest.java;
|
||||
|
||||
import se.scalablesolutions.akka.actor.TypedActor;
|
||||
import se.scalablesolutions.akka.actor.TypedTransactor;
|
||||
import se.scalablesolutions.akka.actor.TypedActorContext;
|
||||
import se.scalablesolutions.akka.stm.TransactionalMap;
|
||||
|
||||
public class SimpleServiceImpl extends TypedTransactor implements SimpleService {
|
||||
private String KEY = "COUNTER";
|
||||
|
||||
private boolean hasStartedTicking = false;
|
||||
private TransactionalMap<String, Integer> storage;
|
||||
private Receiver receiver = TypedActor.newInstance(Receiver.class, ReceiverImpl.class);
|
||||
|
||||
public String count() {
|
||||
if (storage == null) storage = new TransactionalMap<String, Integer>();
|
||||
if (!hasStartedTicking) {
|
||||
storage.put(KEY, 0);
|
||||
hasStartedTicking = true;
|
||||
return "Tick: 0\n";
|
||||
} else {
|
||||
// Grabs the sender address and returns it
|
||||
//SimpleService sender = receiver.receive();
|
||||
int counter = (Integer)storage.get(KEY).get() + 1;
|
||||
storage.put(KEY, counter);
|
||||
return "Tick: " + counter + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRestart(Throwable cause) {
|
||||
System.out.println("Prepare for restart by supervisor");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postRestart(Throwable cause) {
|
||||
System.out.println("Reinitialize after restart by supervisor");
|
||||
}
|
||||
}
|
||||
|
|
@ -10,7 +10,7 @@ http://www.akkasource.org/schema/akka
|
|||
http://scalablesolutions.se/akka/akka-0.10.xsd">
|
||||
|
||||
<bean id="wrappedService"
|
||||
class="se.scalablesolutions.akka.actor.ActiveObject"
|
||||
class="se.scalablesolutions.akka.actor.TypedActor"
|
||||
factory-method="newInstance">
|
||||
<constructor-arg index="0" type="java.lang.Class" value="se.scalablesolutions.akka.spring.foo.MyPojo"/>
|
||||
<constructor-arg index="1" value="1000"/>
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ import org.junit.Test;
|
|||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
|
||||
import se.scalablesolutions.akka.actor.ActiveObject;
|
||||
import se.scalablesolutions.akka.config.ActiveObjectConfigurator;
|
||||
import se.scalablesolutions.akka.actor.TypedActor;
|
||||
import se.scalablesolutions.akka.config.TypedActorConfigurator;
|
||||
import se.scalablesolutions.akka.config.JavaConfig.AllForOne;
|
||||
import se.scalablesolutions.akka.config.JavaConfig.Component;
|
||||
import se.scalablesolutions.akka.config.JavaConfig.LifeCycle;
|
||||
|
|
@ -45,10 +45,10 @@ public class SupervisorConfigurationTest {
|
|||
|
||||
@Test
|
||||
public void testSupervision() {
|
||||
// get ActiveObjectConfigurator bean from spring context
|
||||
ActiveObjectConfigurator myConfigurator = (ActiveObjectConfigurator) context
|
||||
// get TypedActorConfigurator bean from spring context
|
||||
TypedActorConfigurator myConfigurator = (TypedActorConfigurator) context
|
||||
.getBean("supervision1");
|
||||
// get ActiveObjects
|
||||
// get TypedActors
|
||||
Foo foo = myConfigurator.getInstance(Foo.class);
|
||||
assertNotNull(foo);
|
||||
IBar bar = myConfigurator.getInstance(IBar.class);
|
||||
|
|
@ -59,7 +59,7 @@ public class SupervisorConfigurationTest {
|
|||
|
||||
@Test
|
||||
public void testTransactionalState() {
|
||||
ActiveObjectConfigurator conf = (ActiveObjectConfigurator) context
|
||||
TypedActorConfigurator conf = (TypedActorConfigurator) context
|
||||
.getBean("supervision2");
|
||||
StatefulPojo stateful = conf.getInstance(StatefulPojo.class);
|
||||
stateful.setMapState("testTransactionalState", "some map state");
|
||||
|
|
@ -73,23 +73,23 @@ public class SupervisorConfigurationTest {
|
|||
|
||||
@Test
|
||||
public void testInitTransactionalState() {
|
||||
StatefulPojo stateful = ActiveObject.newInstance(StatefulPojo.class,
|
||||
StatefulPojo stateful = TypedActor.newInstance(StatefulPojo.class,
|
||||
1000, true);
|
||||
assertTrue("should be inititalized", stateful.isInitialized());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSupervisionWithDispatcher() {
|
||||
ActiveObjectConfigurator myConfigurator = (ActiveObjectConfigurator) context
|
||||
TypedActorConfigurator myConfigurator = (TypedActorConfigurator) context
|
||||
.getBean("supervision-with-dispatcher");
|
||||
// get ActiveObjects
|
||||
// get TypedActors
|
||||
Foo foo = myConfigurator.getInstance(Foo.class);
|
||||
assertNotNull(foo);
|
||||
// TODO how to check dispatcher?
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoteActiveObject() {
|
||||
public void testRemoteTypedActor() {
|
||||
new Thread(new Runnable() {
|
||||
public void run() {
|
||||
RemoteNode.start();
|
||||
|
|
@ -99,13 +99,13 @@ public class SupervisorConfigurationTest {
|
|||
Thread.currentThread().sleep(1000);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
Foo instance = ActiveObject.newRemoteInstance(Foo.class, 2000, "localhost", 9999);
|
||||
Foo instance = TypedActor.newRemoteInstance(Foo.class, 2000, "localhost", 9999);
|
||||
System.out.println(instance.foo());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSupervisedRemoteActiveObject() {
|
||||
public void testSupervisedRemoteTypedActor() {
|
||||
new Thread(new Runnable() {
|
||||
public void run() {
|
||||
RemoteNode.start();
|
||||
|
|
@ -116,7 +116,7 @@ public class SupervisorConfigurationTest {
|
|||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
ActiveObjectConfigurator conf = new ActiveObjectConfigurator();
|
||||
TypedActorConfigurator conf = new TypedActorConfigurator();
|
||||
conf.configure(
|
||||
new RestartStrategy(new AllForOne(), 3, 10000, new Class[] { Exception.class }),
|
||||
new Component[] {
|
||||
|
|
|
|||
|
|
@ -21,10 +21,10 @@ import se.scalablesolutions.akka.remote.RemoteNode;
|
|||
import se.scalablesolutions.akka.spring.foo.MyPojo;
|
||||
|
||||
/**
|
||||
* Tests for spring configuration of active objects and supervisor configuration.
|
||||
* Tests for spring configuration of typed actors and supervisor configuration.
|
||||
* @author michaelkober
|
||||
*/
|
||||
public class ActiveObjectConfigurationTest {
|
||||
public class TypedActorConfigurationTest {
|
||||
|
||||
private ApplicationContext context = null;
|
||||
|
||||
|
|
@ -50,7 +50,7 @@ public class ActiveObjectConfigurationTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleActiveObject() {
|
||||
public void testSimpleTypedActor() {
|
||||
MyPojo myPojo = (MyPojo) context.getBean("simple-active-object");
|
||||
String msg = myPojo.getFoo();
|
||||
msg += myPojo.getBar();
|
||||
|
|
@ -58,20 +58,20 @@ public class ActiveObjectConfigurationTest {
|
|||
}
|
||||
|
||||
@Test(expected = FutureTimeoutException.class)
|
||||
public void testSimpleActiveObject_Timeout() {
|
||||
public void testSimpleTypedActor_Timeout() {
|
||||
MyPojo myPojo = (MyPojo) context.getBean("simple-active-object");
|
||||
myPojo.longRunning();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleActiveObject_NoTimeout() {
|
||||
public void testSimpleTypedActor_NoTimeout() {
|
||||
MyPojo myPojo = (MyPojo) context.getBean("simple-active-object-long-timeout");
|
||||
String msg = myPojo.longRunning();
|
||||
assertEquals("this took long", msg);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransactionalActiveObject() {
|
||||
public void testTransactionalTypedActor() {
|
||||
MyPojo myPojo = (MyPojo) context.getBean("transactional-active-object");
|
||||
String msg = myPojo.getFoo();
|
||||
msg += myPojo.getBar();
|
||||
|
|
@ -79,7 +79,7 @@ public class ActiveObjectConfigurationTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testRemoteActiveObject() {
|
||||
public void testRemoteTypedActor() {
|
||||
new Thread(new Runnable() {
|
||||
public void run() {
|
||||
RemoteNode.start();
|
||||
|
|
@ -134,7 +134,7 @@
|
|||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<!-- active object -->
|
||||
<!-- typed actor -->
|
||||
<xsd:complexType name="active-object-type">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="remote" type="remote-type" minOccurs="0" maxOccurs="1"/>
|
||||
|
|
@ -196,7 +196,7 @@
|
|||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
|
||||
<!-- active objects -->
|
||||
<!-- typed actors -->
|
||||
<xsd:complexType name="active-objects-type">
|
||||
<xsd:choice minOccurs="1" maxOccurs="unbounded">
|
||||
<xsd:element name="active-object" type="active-object-type"/>
|
||||
|
|
@ -252,7 +252,7 @@
|
|||
<xsd:attribute name="ref" type="xsd:string"/>
|
||||
</xsd:complexType>
|
||||
|
||||
<!-- ActiveObject -->
|
||||
<!-- TypedActor -->
|
||||
<xsd:element name="active-object" type="active-object-type"/>
|
||||
|
||||
<!-- Dispatcher -->
|
||||
|
|
|
|||
|
|
@ -1,198 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||
*/
|
||||
|
||||
package se.scalablesolutions.akka.spring
|
||||
|
||||
import java.beans.PropertyDescriptor
|
||||
import java.lang.reflect.Method
|
||||
import javax.annotation.PreDestroy
|
||||
import javax.annotation.PostConstruct
|
||||
import reflect.BeanProperty
|
||||
|
||||
import org.springframework.beans.BeanWrapperImpl
|
||||
import org.springframework.beans.BeanWrapper
|
||||
import org.springframework.beans.BeanUtils
|
||||
import org.springframework.beans.factory.BeanFactory
|
||||
import org.springframework.beans.factory.config.AbstractFactoryBean
|
||||
import org.springframework.context.{ApplicationContext,ApplicationContextAware}
|
||||
import org.springframework.util.ReflectionUtils
|
||||
import org.springframework.util.StringUtils
|
||||
|
||||
import se.scalablesolutions.akka.actor.{ActiveObjectConfiguration, ActiveObject}
|
||||
import se.scalablesolutions.akka.config.ScalaConfig.{ShutdownCallback, RestartCallbacks}
|
||||
import se.scalablesolutions.akka.dispatch.MessageDispatcher
|
||||
import se.scalablesolutions.akka.util.{Logging, Duration}
|
||||
|
||||
/**
|
||||
* Factory bean for active objects.
|
||||
*
|
||||
* @author michaelkober
|
||||
* @author <a href="johan.rask@jayway.com">Johan Rask</a>
|
||||
* @author Martin Krasser
|
||||
*/
|
||||
class ActiveObjectFactoryBean extends AbstractFactoryBean[AnyRef] with Logging with ApplicationContextAware {
|
||||
import StringReflect._
|
||||
import AkkaSpringConfigurationTags._
|
||||
|
||||
@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 shutdown: String = ""
|
||||
@BeanProperty var host: String = ""
|
||||
@BeanProperty var port: Int = _
|
||||
@BeanProperty var lifecycle: String = ""
|
||||
@BeanProperty var dispatcher: DispatcherProperties = _
|
||||
@BeanProperty var scope:String = VAL_SCOPE_SINGLETON
|
||||
@BeanProperty var property:PropertyEntries = _
|
||||
@BeanProperty var applicationContext:ApplicationContext = _
|
||||
|
||||
// Holds info about if deps has been set or not. Depends on
|
||||
// if interface is specified or not. We must set deps on
|
||||
// target instance if interface is specified
|
||||
var hasSetDependecies = false
|
||||
|
||||
|
||||
override def isSingleton:Boolean = {
|
||||
if(scope.equals(VAL_SCOPE_SINGLETON)) {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
|
||||
*/
|
||||
def getObjectType: Class[AnyRef] = try {
|
||||
target.toClass
|
||||
} catch {
|
||||
// required by contract to return null
|
||||
case e: ClassNotFoundException => null
|
||||
}
|
||||
|
||||
/*
|
||||
* @see org.springframework.beans.factory.config.AbstractFactoryBean#createInstance()
|
||||
*/
|
||||
def createInstance: AnyRef = {
|
||||
var argumentList = ""
|
||||
if (isRemote) argumentList += "r"
|
||||
if (hasInterface) argumentList += "i"
|
||||
if (hasDispatcher) argumentList += "d"
|
||||
|
||||
postConstruct(
|
||||
setProperties(
|
||||
create(argumentList)))
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the active object if it is a singleton.
|
||||
*/
|
||||
override def destroyInstance(instance:AnyRef) {
|
||||
ActiveObject.stop(instance)
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes any method annotated with @PostConstruct
|
||||
* When interfaces are specified, this method is invoked both on the
|
||||
* target instance and on the active object, so a developer is free do decide
|
||||
* where the annotation should be. If no interface is specified it is only invoked
|
||||
* on the active object
|
||||
*/
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
private def setProperties(ref:AnyRef) : AnyRef = {
|
||||
if(hasSetDependecies) {
|
||||
return ref
|
||||
}
|
||||
|
||||
log.debug("Processing properties and dependencies for target class %s",target)
|
||||
val beanWrapper = new BeanWrapperImpl(ref);
|
||||
if(ref.isInstanceOf[ApplicationContextAware]) {
|
||||
log.debug("Setting application context")
|
||||
beanWrapper.setPropertyValue("applicationContext",applicationContext)
|
||||
}
|
||||
for(entry <- property.entryList) {
|
||||
val propertyDescriptor = BeanUtils.getPropertyDescriptor(ref.getClass,entry.name)
|
||||
val method = propertyDescriptor.getWriteMethod();
|
||||
|
||||
if(StringUtils.hasText(entry.ref)) {
|
||||
log.debug("Setting property %s with bean ref %s using method %s",
|
||||
entry.name,entry.ref,method.getName)
|
||||
method.invoke(ref,getBeanFactory().getBean(entry.ref))
|
||||
} else if(StringUtils.hasText(entry.value)) {
|
||||
log.debug("Setting property %s with value %s using method %s",
|
||||
entry.name,entry.value,method.getName)
|
||||
beanWrapper.setPropertyValue(entry.name,entry.value)
|
||||
} else {
|
||||
throw new AkkaBeansException("Either property@ref or property@value must be set on property element")
|
||||
}
|
||||
}
|
||||
ref
|
||||
}
|
||||
|
||||
private[akka] def create(argList : String) : AnyRef = {
|
||||
if (argList == "r") {
|
||||
ActiveObject.newInstance(target.toClass, createConfig.makeRemote(host, port))
|
||||
} else if (argList == "ri" ) {
|
||||
ActiveObject.newInstance(interface.toClass, aNewInstance(target.toClass), createConfig.makeRemote(host, port))
|
||||
} else if (argList == "rd") {
|
||||
ActiveObject.newInstance(target.toClass, createConfig.makeRemote(host, port).dispatcher(dispatcherInstance))
|
||||
} else if (argList == "rid") {
|
||||
ActiveObject.newInstance(interface.toClass, aNewInstance(target.toClass), createConfig.makeRemote(host, port).dispatcher(dispatcherInstance))
|
||||
} else if (argList == "i") {
|
||||
ActiveObject.newInstance(interface.toClass, aNewInstance(target.toClass), createConfig)
|
||||
} else if (argList == "id") {
|
||||
ActiveObject.newInstance(interface.toClass, aNewInstance(target.toClass), createConfig.dispatcher(dispatcherInstance))
|
||||
} else if (argList == "d") {
|
||||
ActiveObject.newInstance(target.toClass, createConfig.dispatcher(dispatcherInstance))
|
||||
} else {
|
||||
ActiveObject.newInstance(target.toClass, createConfig)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private[akka] def createConfig: ActiveObjectConfiguration = {
|
||||
val config = new ActiveObjectConfiguration().timeout(Duration(timeout, "millis"))
|
||||
if (hasRestartCallbacks) config.restartCallbacks(pre, post)
|
||||
if (hasShutdownCallback) config.shutdownCallback(shutdown)
|
||||
if (transactional) config.makeTransactionRequired
|
||||
config
|
||||
}
|
||||
def aNewInstance[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)
|
||||
|
||||
private[akka] def hasRestartCallbacks = ((pre != null) && !pre.isEmpty) || ((post != null) && !post.isEmpty)
|
||||
|
||||
private[akka] def hasShutdownCallback = ((shutdown != null) && !shutdown.isEmpty)
|
||||
|
||||
private[akka] def hasDispatcher = (dispatcher != null) && (dispatcher.dispatcherType != null) && (!dispatcher.dispatcherType.isEmpty)
|
||||
|
||||
private[akka] def dispatcherInstance : MessageDispatcher = {
|
||||
import DispatcherFactoryBean._
|
||||
createNewInstance(dispatcher)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
package se.scalablesolutions.akka.spring
|
||||
|
||||
import org.springframework.beans.BeansException
|
||||
|
||||
/**
|
||||
* Exception to use when something goes wrong during bean creation
|
||||
@author <a href="johan.rask@jayway.com">Johan Rask</a>
|
||||
*/
|
||||
class AkkaBeansException(errorMsg:String,t:Throwable) extends BeansException(errorMsg,t) {
|
||||
|
||||
def this(errorMsg:String) = {
|
||||
this(errorMsg,null)
|
||||
}
|
||||
}
|
||||
|
|
@ -12,7 +12,7 @@ import AkkaSpringConfigurationTags._
|
|||
*/
|
||||
class AkkaNamespaceHandler extends NamespaceHandlerSupport {
|
||||
def init = {
|
||||
registerBeanDefinitionParser(ACTIVE_OBJECT_TAG, new ActiveObjectBeanDefinitionParser());
|
||||
registerBeanDefinitionParser(ACTIVE_OBJECT_TAG, new TypedActorBeanDefinitionParser());
|
||||
registerBeanDefinitionParser(SUPERVISION_TAG, new SupervisionBeanDefinitionParser());
|
||||
registerBeanDefinitionParser(DISPATCHER_TAG, new DispatcherBeanDefinitionParser());
|
||||
registerBeanDefinitionParser(CAMEL_SERVICE_TAG, new CamelServiceBeanDefinitionParser);
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ object AkkaSpringConfigurationTags {
|
|||
|
||||
// --- ATTRIBUTES
|
||||
//
|
||||
// active object attributes
|
||||
// typed actor attributes
|
||||
val TIMEOUT = "timeout"
|
||||
val TARGET = "target"
|
||||
val INTERFACE = "interface"
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import org.springframework.beans.factory.xml.{ParserContext, AbstractSingleBeanD
|
|||
* Parser for custom namespace configuration.
|
||||
* @author michaelkober
|
||||
*/
|
||||
class DispatcherBeanDefinitionParser extends AbstractSingleBeanDefinitionParser with ActiveObjectParser with DispatcherParser {
|
||||
class DispatcherBeanDefinitionParser extends AbstractSingleBeanDefinitionParser with TypedActorParser with DispatcherParser {
|
||||
/*
|
||||
* @see org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#doParse(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext, org.springframework.beans.factory.support.BeanDefinitionBuilder)
|
||||
*/
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue