diff --git a/Makefile b/Makefile
index 6e2d3c0..c0ec348 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
# Variables
-VERSION := 1.0-SNAPSHOT
+VERSION := 1.3.0
PACKAGE := target/spawn-java-demo-${VERSION}-shaded.jar
clean:
diff --git a/README.md b/README.md
index 42be8ec..d703f62 100644
--- a/README.md
+++ b/README.md
@@ -94,7 +94,7 @@ The second thing we have to do is add the spawn dependency to the project.
com.github.eigrspawn-java-std-sdk
- v1.2.7
+ v1.3.0
```
We're also going to configure a few things for our application build to work, including compiling the protobuf files.
@@ -128,7 +128,7 @@ See below a full example of the pom.xml file:
com.github.eigrspawn-java-std-sdk
- v1.2.7
+ v1.3.0ch.qos.logback
@@ -254,19 +254,22 @@ And let's populate this file with the following content:
syntax = "proto3";
package domain;
-
option java_package = "io.eigr.spawn.java.demo.domain";
-message JoeState {
- repeated string languages = 1;
+message State {
+ repeated string languages = 1;
}
message Request {
- string language = 1;
+ string language = 1;
}
message Reply {
- string response = 1;
+ string response = 1;
+}
+
+service JoeActor {
+ rpc SetLanguage(Request) returns (Reply);
}
```
@@ -287,35 +290,45 @@ Populate this file with the following content:
```Java
package io.eigr.spawn.java.demo;
-import io.eigr.spawn.api.actors.Value;
import io.eigr.spawn.api.actors.ActorContext;
-import io.eigr.spawn.api.actors.annotations.Action;
-import io.eigr.spawn.api.actors.annotations.stateful.StatefulNamedActor;
-import io.eigr.spawn.java.demo.domain.Domain;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import io.eigr.spawn.api.actors.StatefulActor;
+import io.eigr.spawn.api.actors.Value;
+import io.eigr.spawn.api.actors.behaviors.ActorBehavior;
+import io.eigr.spawn.api.actors.behaviors.BehaviorCtx;
+import io.eigr.spawn.api.actors.behaviors.NamedActorBehavior;
+import io.eigr.spawn.internal.ActionBindings;
+import io.eigr.spawn.java.demo.domain.Actor.Reply;
+import io.eigr.spawn.java.demo.domain.Actor.Request;
+import io.eigr.spawn.java.demo.domain.Actor.State;
-@StatefulNamedActor(name = "joe", stateType = Domain.JoeState.class)
-public class Joe {
- private static final Logger log = LoggerFactory.getLogger(Joe.class);
+import static io.eigr.spawn.api.actors.behaviors.ActorBehavior.*;
- @Action(name = "hi", inputType = Domain.Request.class)
- public Value hi(Domain.Request msg, ActorContext context) {
- log.info("Received invocation. Message: {}. Context: {}", msg, context);
+public final class JoeActor extends StatefulActor {
+
+ @Override
+ public ActorBehavior configure(BehaviorCtx context) {
+ return new NamedActorBehavior(
+ name("JoeActor"),
+ channel("test.channel"),
+ action("SetLanguage", ActionBindings.of(Request.class, this::setLanguage))
+ );
+ }
+
+ private Value setLanguage(ActorContext context, Request msg) {
if (context.getState().isPresent()) {
- log.info("State is present and value is {}", context.getState().get());
+ //Do something with previous state
}
return Value.at()
- .response(Domain.Reply.newBuilder()
- .setResponse("Hello From Java")
+ .response(Reply.newBuilder()
+ .setResponse(String.format("Hi %s. Hello From Java", msg.getLanguage()))
.build())
- .state(updateState("erlang"))
+ .state(updateState(msg.getLanguage()))
.reply();
}
- private Domain.JoeState updateState(String language) {
- return Domain.JoeState.newBuilder()
+ private State updateState(String language) {
+ return State.newBuilder()
.addLanguages(language)
.build();
}
@@ -434,37 +447,37 @@ In this case, it is enough to declare a constructor that receives a single argum
```java
package io.eigr.spawn.java.demo;
-import io.eigr.spawn.api.actors.Value;
import io.eigr.spawn.api.actors.ActorContext;
-import io.eigr.spawn.api.actors.annotations.Action;
-import io.eigr.spawn.api.actors.annotations.stateful.StatefulNamedActor;
-import io.eigr.spawn.java.demo.domain.Domain;
-
-import java.util.Map;
+import io.eigr.spawn.api.actors.StatefulActor;
+import io.eigr.spawn.api.actors.Value;
+import io.eigr.spawn.api.actors.behaviors.ActorBehavior;
+import io.eigr.spawn.api.actors.behaviors.BehaviorCtx;
+import io.eigr.spawn.api.actors.behaviors.NamedActorBehavior;
+import io.eigr.spawn.internal.ActionBindings;
+import io.eigr.spawn.java.demo.domain.Actor.Reply;
+import io.eigr.spawn.java.demo.domain.Actor.Request;
+import io.eigr.spawn.java.demo.domain.Actor.State;
-@StatefulNamedActor(name = "joe", stateful = true, stateType = Domain.JoeState.class, channel = "test")
-public final class Joe {
- private final String someValue;
+import static io.eigr.spawn.api.actors.behaviors.ActorBehavior.action;
+import static io.eigr.spawn.api.actors.behaviors.ActorBehavior.name;
+public final class JoeActor extends StatefulActor {
- public Joe(Map args) {
- this.someValue = args.get("someKey");
- }
+ private String defaultMessage;
- @Action(inputType = Domain.Request.class)
- public Value setLanguage(Domain.Request msg, ActorContext context) {
- return Value.at()
- .response(Domain.Reply.newBuilder()
- .setResponse("Hello From Java")
- .build())
- .state(updateState("java"))
- .reply();
+ @Override
+ public ActorBehavior configure(BehaviorCtx context) {
+ defaultMessage = context.getInjector().getInstance(String.class);
+ return new NamedActorBehavior(
+ name("JoeActor"),
+ action("SetLanguage", ActionBindings.of(Request.class, this::setLanguage))
+ );
}
// ...
}
```
-Then you also need to register your Actor passing arguments like as follows:
+Then you also need to register your Actor using injector :
```java
package io.eigr.spawn.java.demo;
@@ -476,12 +489,12 @@ import java.util.Map;
public class App {
public static void main(String[] args) {
- Map actorConstructorArgs = new HashMap<>();
- actorConstructorArgs.put("someKey", "someValue");
+ DependencyInjector injector = SimpleDependencyInjector.createInjector();
+ injector.bind(String.class, "Hello with Constructor");
Spawn spawnSystem = new Spawn.SpawnSystem()
- .create("spawn-system")
- .withActor(Joe.class, actorConstructorArgs, arg -> new Joe((Map) arg))
+ .create("spawn-system", injector)
+ .withActor(Joe.class)
.build();
spawnSystem.start();
@@ -543,45 +556,52 @@ public class MessageServiceImpl implements MessageService {
}
```
-2. Second, let's define an actor so that it receives an instance of the DependencyInjector class through the class constructor:
+2. Second, let's define an actor so that it receives an instance of the DependencyInjector class through the context of configure method:
```java
-package io.eigr.spawn.test.actors;
+package io.eigr.spawn.java.demo;
import io.eigr.spawn.api.actors.ActorContext;
+import io.eigr.spawn.api.actors.StatefulActor;
import io.eigr.spawn.api.actors.Value;
-import io.eigr.spawn.api.actors.annotations.Action;
-import io.eigr.spawn.api.actors.annotations.stateful.StatefulNamedActor;
-import io.eigr.spawn.api.extensions.DependencyInjector;
-import io.eigr.spawn.java.test.domain.Actor;
-
-@StatefulNamedActor(name = "test_actor_constructor", stateType = Actor.State.class)
-public final class Joe {
-
- private final MessageService messageService;
-
- public Joe(DependencyInjector injector) {
- // Note how to use dependency injection here to get a concrete class of MessageService.
- this.messageService = injector.getInstance(MessageService.class);
- }
-
- @Action(inputType = Actor.Request.class)
- public Value setLanguage(Actor.Request msg, ActorContext context) {
- return Value.at()
- .response(Actor.Reply.newBuilder()
- .setResponse(messageService.getDefaultMessage())
- .build())
- .state(updateState("java"))
- .reply();
- }
-
- private Actor.State updateState(String language) {
- return Actor.State.newBuilder()
- .addLanguages(language)
- .build();
- }
-}
+import io.eigr.spawn.api.actors.behaviors.ActorBehavior;
+import io.eigr.spawn.api.actors.behaviors.BehaviorCtx;
+import io.eigr.spawn.api.actors.behaviors.NamedActorBehavior;
+import io.eigr.spawn.internal.ActionBindings;
+import io.eigr.spawn.java.demo.domain.Actor.Reply;
+import io.eigr.spawn.java.demo.domain.Actor.Request;
+import io.eigr.spawn.java.demo.domain.Actor.State;
+
+import static io.eigr.spawn.api.actors.behaviors.ActorBehavior.action;
+import static io.eigr.spawn.api.actors.behaviors.ActorBehavior.name;
+public final class JoeActor extends StatefulActor {
+
+ private String defaultMessage;
+
+ @Override
+ public ActorBehavior configure(BehaviorCtx context) {
+ defaultMessage = context.getInjector().getInstance(String.class);
+ return new NamedActorBehavior(
+ name("JoeActor"),
+ action("SetLanguage", ActionBindings.of(Request.class, this::setLanguage))
+ );
+ }
+
+ private Value setLanguage(ActorContext context, Request msg) {
+ return Value.at()
+ .response(Reply.newBuilder()
+ .setResponse(defaultMessage)
+ .build())
+ .state(updateState("java"))
+ .reply();
+ }
+ private State updateState(String language) {
+ return State.newBuilder()
+ .addLanguages(language)
+ .build();
+ }
+}
```
3. Then you can pass your dependent classes this way to your Actor:
@@ -600,10 +620,13 @@ public class App {
If you try to add different instances of the same type you will receive an error.
*/
injector.bind(MessageService.class, new MessageServiceImpl());
+
+ // or using alias for put different values of same key types
+ injector.bind(MessageService.class, "myMessageService", new MessageServiceImpl());
Spawn spawnSystem = new Spawn.SpawnSystem()
- .create("spawn-system")
- .withActor(Joe.class, injector -> new Joe((DependencyInjector) injector))
+ .create("spawn-system", injector)
+ .withActor(Joe.class)
.build();
spawnSystem.start();
@@ -614,7 +637,7 @@ public class App {
It is important to note that this helper mechanism does not currently implement any type of complex dependency graph.
Therefore, it will not build objects based on complex dependencies nor take care of the object lifecycle for you.
In other words, all instances added through the bind method of the SimpleDependencyInjector class will be singletons.
-This mechanism works much more like a bucket of objects that will be forwarded via your actor's constructor.
+This mechanism works much more like a bucket of objects that will be forwarded via your actor's context.
> **_NOTE:_** **Why not use the java cdi 2.0 spec?**
Our goals are to keep the SDK for standalone Java applications very simple. We consider that implementing the entire specification would not be viable for us at the moment. It would be a lot of effort and energy expenditure that we consider spending on other parts of the ecosystem that we think will guarantee us more benefits.
@@ -644,28 +667,47 @@ or Stateless, those who do not need to maintain the state.
For this the developer just needs to make use of the correct annotation. For example, I could declare a Serverless Actor using the following code:
```java
-package io.eigr.spawn.test.actors;
+package io.eigr.spawn.java.demo.actors;
-import io.eigr.spawn.api.actors.Value;
import io.eigr.spawn.api.actors.ActorContext;
-import io.eigr.spawn.api.actors.annotations.Action;
-import io.eigr.spawn.api.actors.annotations.stateless.StatelessNamedActor;
-import io.eigr.spawn.java.test.domain.Actor;
-
-@StatelessNamedActor(name = "test_joe")
-public class JoeActor {
- @Action
- public Value hi(Actor.Request msg, ActorContext> context) {
+import io.eigr.spawn.api.actors.StatelessActor;
+import io.eigr.spawn.api.actors.Value;
+import io.eigr.spawn.api.actors.behaviors.ActorBehavior;
+import io.eigr.spawn.api.actors.behaviors.BehaviorCtx;
+import io.eigr.spawn.api.actors.behaviors.NamedActorBehavior;
+import io.eigr.spawn.internal.ActionBindings;
+import io.eigr.spawn.java.demo.domain.Actor.Reply;
+import io.eigr.spawn.java.demo.domain.Actor.Request;
+import io.eigr.spawn.java.demo.domain.Actor.State;
+
+import static io.eigr.spawn.api.actors.behaviors.ActorBehavior.action;
+import static io.eigr.spawn.api.actors.behaviors.ActorBehavior.name;
+
+public final class StatelessNamedActor extends StatelessActor {
+
+ @Override
+ public ActorBehavior configure(BehaviorCtx context) {
+ return new NamedActorBehavior(
+ name("StatelessNamedActor"),
+ action("SetLanguage", ActionBindings.of(Request.class, this::setLanguage))
+ );
+ }
+
+ private Value setLanguage(ActorContext context, Request msg) {
+ if (context.getState().isPresent()) {
+ }
+
return Value.at()
- .response(Actor.Reply.newBuilder()
- .setResponse("Hello From Java")
+ .response(Reply.newBuilder()
+ .setResponse(String.format("Hi %s. Hello From Java", msg.getLanguage()))
.build())
.reply();
}
}
+
```
-Other than that the same Named, UnNamed types are supported. Just use the StatelessNamed or StatelessUnNamed annotations.
+Other than that the same Named, UnNamed types are supported. Just use the StatelessNamedActor or StatelessUnNamedActor super class.
### Considerations about Spawn actors
@@ -683,33 +725,51 @@ Whereas unnamed and pooled actors are instantiated the first time only when they
Actors in Spawn can subscribe to a thread and receive, as well as broadcast, events for a given thread.
-To consume from a topic, you just need to configure the Actor annotation using the channel option as follows:
+To consume from a topic, you just need to configure the Actor using the channel option as follows:
-```Java
-@StatefulNamedActor(name = "joe", stateful = true, stateType = Domain.JoeState.class, channel = "test")
```
-In the case above, the Actor `joe` was configured to receive events that are forwarded to the topic called `test`.
+return new NamedActorBehavior(
+ name("JoeActor"),
+ channel("test.channel"),
+);
+```
+In the case above, the Actor `JoeActor` was configured to receive events that are forwarded to the topic called `test.channel`.
To produce events in a topic, just use the Broadcast Workflow. The example below demonstrates a complete example of
producing and consuming events. In this case, the same actor is the event consumer and producer, but in a more realistic scenario,
different actors would be involved in these processes.
```Java
-package io.eigr.spawn.java.demo;
+package io.eigr.spawn.java.demo.actors;
+import io.eigr.spawn.api.actors.ActorContext;
+import io.eigr.spawn.api.actors.StatefulActor;
+import io.eigr.spawn.api.actors.Value;
+import io.eigr.spawn.api.actors.behaviors.ActorBehavior;
+import io.eigr.spawn.api.actors.behaviors.BehaviorCtx;
+import io.eigr.spawn.api.actors.behaviors.NamedActorBehavior;
import io.eigr.spawn.api.actors.workflows.Broadcast;
-// some imports omitted for brevity
+import io.eigr.spawn.internal.ActionBindings;
+import io.eigr.spawn.java.demo.domain.Actor.Reply;
+import io.eigr.spawn.java.demo.domain.Actor.Request;
+import io.eigr.spawn.java.demo.domain.Actor.State;
-@StatefulNamedActor(name = "joe", stateType = Domain.JoeState.class, channel = "test")
-public class Joe {
- @TimerAction(name = "hi", period = 60000)
- public Value hi(ActorContext context) {
- Domain.Request msg = Domain.Request.newBuilder()
- .setLanguage("erlang")
- .build();
+import static io.eigr.spawn.api.actors.behaviors.ActorBehavior.*;
+
+public final class LoopActor extends StatefulActor {
+ @Override
+ public ActorBehavior configure(BehaviorCtx context) {
+ return new NamedActorBehavior(
+ name("LoopActor"),
+ channel("test.channel"),
+ action("SetLanguage", ActionBindings.of(Request.class, this::setLanguage))
+ );
+ }
+
+ private Value setLanguage(ActorContext context, Request msg) {
return Value.at()
- .flow(Broadcast.to("test", "setLanguage", msg))
+ .flow(Broadcast.to("test.channel", "setLanguage", msg))
.response(Domain.Reply.newBuilder()
.setResponse("Hello From Erlang")
.build())
@@ -717,16 +777,7 @@ public class Joe {
.reply();
}
- @Action(inputType = Domain.Request.class)
- public Value setLanguage(Domain.Request msg, ActorContext context) {
- return Value.at()
- .response(Domain.Reply.newBuilder()
- .setResponse("Hello From Java")
- .build())
- .state(updateState("java"))
- .reply();
- }
- // ....
+ // ...
}
```
@@ -736,36 +787,45 @@ Actors can also emit side effects to other Actors as part of their response.
See an example:
```Java
-package io.eigr.spawn.java.demo;
+package io.eigr.spawn.java.demo.actors;
-import io.eigr.spawn.api.ActorIdentity;
-import io.eigr.spawn.api.ActorRef;
-import io.eigr.spawn.api.actors.Value;
import io.eigr.spawn.api.actors.ActorContext;
-import io.eigr.spawn.api.actors.annotations.Action;
-import io.eigr.spawn.api.actors.annotations.stateful.StatefulNamedActor;
-import io.eigr.spawn.api.actors.workflows.SideEffect;
-import io.eigr.spawn.java.demo.domain.Domain;
+import io.eigr.spawn.api.actors.StatefulActor;
+import io.eigr.spawn.api.actors.Value;
+import io.eigr.spawn.api.actors.behaviors.ActorBehavior;
+import io.eigr.spawn.api.actors.behaviors.BehaviorCtx;
+import io.eigr.spawn.api.actors.behaviors.NamedActorBehavior;
+import io.eigr.spawn.internal.ActionBindings;
+import io.eigr.spawn.java.demo.domain.Actor.Reply;
+import io.eigr.spawn.java.demo.domain.Actor.Request;
+import io.eigr.spawn.java.demo.domain.Actor.State;
+
+import static io.eigr.spawn.api.actors.behaviors.ActorBehavior.*;
+
+public final class JoeActor extends StatefulActor {
+
+ @Override
+ public ActorBehavior configure(BehaviorCtx context) {
+ return new NamedActorBehavior(
+ name("JoeActor"),
+ channel("test.channel"),
+ action("SetLanguage", ActionBindings.of(Request.class, this::setLanguage))
+ );
+ }
-@StatefulNamedActor(name = "side_effect_actor", stateType = Domain.State.class)
-public class SideEffectActorExample {
- @Action
- public Value setLanguage(Domain.Request msg, ActorContext ctx) throws Exception {
- // Create a ActorReference to send side effect message
+ private Value setLanguage(ActorContext context, Request msg) {
ActorRef sideEffectReceiverActor = ctx.getSpawnSystem()
- .createActorRef(ActorIdentity.of("spawn-system", "mike", "abs_actor"));
+ .createActorRef(ActorIdentity.of("spawn-system", "MikeFriendActor", "MikeParentActor"));
return Value.at()
- .response(Domain.Reply.newBuilder()
- .setResponse("Hello From Java")
- .build())
- .state(updateState("java"))
.flow(SideEffect.to(sideEffectReceiverActor, "setLanguage", msg))
- //.flow(SideEffect.to(emailSenderReceiverActor, "sendEmail", emailMessage))
- //.flow(SideEffect.to(otherReceiverActor, "otherAction", otherMessage))
+ .response(Reply.newBuilder()
+ .setResponse(String.format("Hi %s. Hello From Java", msg.getLanguage()))
+ .build())
+ .state(updateState(msg.getLanguage()))
.noReply();
}
-
+
// ....
}
```
@@ -783,31 +843,34 @@ an action in another Actor.
See an example:
```Java
-package io.eigr.spawn.java.demo;
+package io.eigr.spawn.java.demo.actors;
-import io.eigr.spawn.api.actors.Value;
import io.eigr.spawn.api.actors.ActorContext;
-import io.eigr.spawn.api.ActorIdentity;
-import io.eigr.spawn.api.ActorRef;
-import io.eigr.spawn.api.actors.annotations.Action;
-import io.eigr.spawn.api.actors.annotations.stateful.StatefulNamedActor;
-import io.eigr.spawn.api.actors.workflows.Forward;
-import io.eigr.spawn.java.demo.domain.Domain;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-@StatefulNamedActor(name = "routing_actor", stateType = Domain.State.class)
-public class ForwardExample {
- private static final Logger log = LoggerFactory.getLogger(ForwardExample.class);
-
- @Action
- public Value setLanguage(Domain.Request msg, ActorContext ctx) throws Exception {
- log.info("Received invocation. Message: {}. Context: {}", msg, ctx);
- if (ctx.getState().isPresent()) {
- log.info("State is present and value is {}", ctx.getState().get());
- }
+import io.eigr.spawn.api.actors.StatefulActor;
+import io.eigr.spawn.api.actors.Value;
+import io.eigr.spawn.api.actors.behaviors.ActorBehavior;
+import io.eigr.spawn.api.actors.behaviors.BehaviorCtx;
+import io.eigr.spawn.api.actors.behaviors.NamedActorBehavior;
+import io.eigr.spawn.internal.ActionBindings;
+import io.eigr.spawn.java.demo.domain.Actor.Reply;
+import io.eigr.spawn.java.demo.domain.Actor.Request;
+import io.eigr.spawn.java.demo.domain.Actor.State;
+
+import static io.eigr.spawn.api.actors.behaviors.ActorBehavior.*;
+
+public final class RoutingActor extends StatefulActor {
+
+ @Override
+ public ActorBehavior configure(BehaviorCtx context) {
+ return new NamedActorBehavior(
+ name("RoutingActor"),
+ action("SetLanguage", ActionBindings.of(Request.class, this::setLanguage))
+ );
+ }
+
+ private Value setLanguage(ActorContext context, Request msg) {
ActorRef forwardedActor = ctx.getSpawnSystem()
- .createActorRef(ActorIdentity.of("spawn-system", "mike", "abs_actor"));
+ .createActorRef(ActorIdentity.of("spawn-system", "MikeFriendActor", "MikeActor"));
return Value.at()
.flow(Forward.to(forwardedActor, "setLanguage"))
@@ -826,39 +889,45 @@ In the end, just like in a Forward, it is the response of the last Actor in the
Example:
```Java
-package io.eigr.spawn.java.demo;
+package io.eigr.spawn.java.demo.actors;
-import io.eigr.spawn.api.actors.Value;
import io.eigr.spawn.api.actors.ActorContext;
-import io.eigr.spawn.api.ActorIdentity;
-import io.eigr.spawn.api.ActorRef;
-import io.eigr.spawn.api.actors.annotations.Action;
-import io.eigr.spawn.api.actors.annotations.stateful.StatefulNamedActor;
-import io.eigr.spawn.api.actors.workflows.Pipe;
-import io.eigr.spawn.java.demo.domain.Domain;
+import io.eigr.spawn.api.actors.StatefulActor;
+import io.eigr.spawn.api.actors.Value;
+import io.eigr.spawn.api.actors.behaviors.ActorBehavior;
+import io.eigr.spawn.api.actors.behaviors.BehaviorCtx;
+import io.eigr.spawn.api.actors.behaviors.NamedActorBehavior;
+import io.eigr.spawn.internal.ActionBindings;
+import io.eigr.spawn.java.demo.domain.Actor.Reply;
+import io.eigr.spawn.java.demo.domain.Actor.Request;
+import io.eigr.spawn.java.demo.domain.Actor.State;
+
+import static io.eigr.spawn.api.actors.behaviors.ActorBehavior.*;
+
+public final class PipeActor extends StatefulActor {
-@StatefulNamedActor(name = "pipe_actor", stateType = Domain.State.class)
-public class PipeActorExample {
+ @Override
+ public ActorBehavior configure(BehaviorCtx context) {
+ return new NamedActorBehavior(
+ name("PipeActor"),
+ action("SetLanguage", ActionBindings.of(Request.class, this::setLanguage))
+ );
+ }
- @Action
- public Value setLanguage(Domain.Request msg, ActorContext ctx) throws Exception {
+ private Value setLanguage(ActorContext context, Request msg) {
ActorRef pipeReceiverActor = ctx.getSpawnSystem()
- .createActorRef(ActorIdentity.of("spawn-system", "joe"));
+ .createActorRef(ActorIdentity.of("spawn-system", "JoeActor"));
return Value.at()
- .response(Domain.Reply.newBuilder()
+ .response(Reply.newBuilder()
.setResponse("Hello From Java")
.build())
.flow(Pipe.to(pipeReceiverActor, "someAction"))
.state(updateState("java"))
.noReply();
}
-
- private Domain.State updateState(String language) {
- return Domain.State.newBuilder()
- .addLanguages(language)
- .build();
- }
+
+ // ...
}
```
@@ -875,32 +944,51 @@ during the moment of the Actor's deactivation.
That is, data is saved at regular intervals asynchronously while the Actor is active and once synchronously
when the Actor suffers a deactivation, when it is turned off.
-These snapshots happen from time to time. And this time is configurable through the ***snapshotTimeout*** property of
-the ***StatefulNamedActor*** or ***UnStatefulNamedActor*** annotation.
+These snapshots happen from time to time. And this time is configurable through the ***snapshotTimeout*** method of
+the ***NamedActorBehavior*** or ***UnNamedActorBehavior*** class.
However, you can tell the Spawn runtime that you want it to persist the data immediately synchronously after executing an Action.
And this can be done in the following way:
Example:
```Java
-import io.eigr.spawn.api.actors.Value;
+package io.eigr.spawn.test.actors;
+
import io.eigr.spawn.api.actors.ActorContext;
-import io.eigr.spawn.api.actors.annotations.Action;
-import io.eigr.spawn.api.actors.annotations.stateful.StatefulNamedActor;
-import io.eigr.spawn.java.demo.domain.Domain;
+import io.eigr.spawn.api.actors.StatefulActor;
+import io.eigr.spawn.api.actors.Value;
+import io.eigr.spawn.api.actors.behaviors.ActorBehavior;
+import io.eigr.spawn.api.actors.behaviors.BehaviorCtx;
+import io.eigr.spawn.api.actors.behaviors.NamedActorBehavior;
+import io.eigr.spawn.internal.ActionBindings;
+import io.eigr.spawn.java.demo.domain.Actor.Reply;
+import io.eigr.spawn.java.demo.domain.Actor.Request;
+import io.eigr.spawn.java.demo.domain.Actor.State;
+
+import static io.eigr.spawn.api.actors.behaviors.ActorBehavior.*;
-@StatefulNamedActor(name = "joe", stateType = Domain.JoeState.class)
-public final class Joe {
- @Action(inputType = Domain.Request.class)
- public Value setLanguage(Domain.Request msg, ActorContext context) {
+public final class JoeActor extends StatefulActor {
+
+ @Override
+ public ActorBehavior configure(BehaviorCtx context) {
+ return new NamedActorBehavior(
+ name("JoeActor"),
+ snapshot(1000),
+ deactivated(60000),
+ action("SetLanguage", ActionBindings.of(Request.class, this::setLanguage))
+ );
+ }
+
+ private Value setLanguage(ActorContext context, Request msg) {
return Value.at()
- .response(Domain.Reply.newBuilder()
- .setResponse("Hello From Java")
+ .response(Reply.newBuilder()
+ .setResponse(String.format("Hi %s. Hello From Java", msg.getLanguage()))
.build())
- .state(updateState("java"), true)
+ .state(updateState(msg.getLanguage()), true)
.reply();
}
- // ...
+
+ // ...
}
```
@@ -938,13 +1026,13 @@ In the sections below we will give some examples of how to invoke different type
To invoke an actor named like the one we defined in section [Getting Started](#getting-started) we could do as follows:
```Java
-ActorRef joeActor = spawnSystem.createActorRef(ActorIdentity.of("spawn-system", "joe"));
+ActorRef joeActor = spawnSystem.createActorRef(ActorIdentity.of("spawn-system", "JoeActor"));
-Domain.Request msg = Domain.Request.newBuilder()
+Request msg = Request.newBuilder()
.setLanguage("erlang")
.build();
-Optional maybeResponse = joeActor.invoke("setLanguage", msg, Domain.Reply.class);
+Optional maybeResponse = joeActor.invoke("setLanguage", msg, Reply.class);
Domain.Reply reply = maybeResponse.get();
```
@@ -976,7 +1064,7 @@ public class App {
spawnSystem.start();
- ActorRef joeActor = spawnSystem.createActorRef(ActorIdentity.of("spawn-system", "joe"));
+ ActorRef joeActor = spawnSystem.createActorRef(ActorIdentity.of("spawn-system", "JoeActor"));
Domain.Request msg = Domain.Request.newBuilder()
.setLanguage("erlang")
@@ -997,50 +1085,67 @@ To better exemplify, let's first show the Actor's definition code and later how
name at runtime:
```java
-package io.eigr.spawn.java.demo;
-// omitted imports for brevity...
-
-@UnStatefulNamedActor(name = "abs_actor", stateful = true, stateType = Domain.State.class)
-public class AbstractActor {
- @Action(inputType = Domain.Request.class)
- public Value setLanguage(Domain.Request msg, ActorContext context) {
- return Value.at()
- .response(Domain.Reply.newBuilder()
- .setResponse("Hello From Java")
- .build())
- .state(updateState("java"))
- .reply();
- }
-
- private Domain.State updateState(String language) {
- return Domain.State.newBuilder()
- .addLanguages(language)
- .build();
- }
+package io.eigr.spawn.test.actors;
+
+import io.eigr.spawn.api.actors.ActorContext;
+import io.eigr.spawn.api.actors.StatefulActor;
+import io.eigr.spawn.api.actors.Value;
+import io.eigr.spawn.api.actors.behaviors.ActorBehavior;
+import io.eigr.spawn.api.actors.behaviors.BehaviorCtx;
+import io.eigr.spawn.api.actors.behaviors.UnNamedActorBehavior;
+import io.eigr.spawn.internal.ActionBindings;
+import io.eigr.spawn.java.demo.domain.Actor.Reply;
+import io.eigr.spawn.java.demo.domain.Actor.Request;
+import io.eigr.spawn.java.demo.domain.Actor.State;
+
+import static io.eigr.spawn.api.actors.behaviors.ActorBehavior.*;
+
+public final class MikeActor extends StatefulActor {
+
+ @Override
+ public ActorBehavior configure(BehaviorCtx context) {
+ return new UnNamedActorBehavior(
+ name("MikeActor"),
+ snapshot(1000),
+ deactivated(60000),
+ action("SetLanguage", ActionBindings.of(Request.class, this::setLanguage))
+ );
+ }
+
+ private Value setLanguage(ActorContext context, Request msg) {
+ return Value.at()
+ .response(Reply.newBuilder()
+ .setResponse(String.format("Hi %s. Hello From Java", msg.getLanguage()))
+ .build())
+ .state(updateState(msg.getLanguage()), true)
+ .reply();
+ }
+
+ // ...
}
```
So you could define and call this actor at runtime like this:
```Java
-ActorRef mike = spawnSystem.createActorRef(ActorIdentity.of("spawn-system", "mike", "abs_actor"));
+ActorRef mike = spawnSystem.createActorRef(ActorIdentity.of("spawn-system", "MikeInstanceActor", "MikeActor"));
-Domain.Request msg = Domain.Request.newBuilder()
+Request msg = Request.newBuilder()
.setLanguage("erlang")
.build();
-Optional maybeResponse = mike.invoke("setLanguage", msg, Domain.Reply.class);
+Optional maybeResponse = mike.invoke("setLanguage", msg, Reply.class);
Domain.Reply reply = maybeResponse.get();
```
The important part of the code above is the following snippet:
```Java
-ActorRef mike = spawnSystem.createActorRef(ActorIdentity.of("spawn-system", "mike", "abs_actor"));
+ActorRef mike = spawnSystem.createActorRef(ActorIdentity.of("spawn-system", "MikeInstanceActor", "MikeActor"));
```
-These tells Spawn that this actor will actually be named at runtime. The name parameter with value "mike"
-in this case is just a reference to "abs_actor" Actor that will be used later
+These tells Spawn that this actor will actually be named at runtime. The name parameter with value "MikeInstanceActor"
+in this case is just a reference to "MikeActor" Actor that will be used later
so that we can actually create an instance of the real Actor.
### Async
@@ -1063,17 +1168,12 @@ It is possible to change the request waiting timeout using the invocation option
```Java
package io.eigr.spawn.java.demo;
-import io.eigr.spawn.api.ActorIdentity;
-import io.eigr.spawn.api.ActorRef;
-import io.eigr.spawn.api.InvocationOpts;
-import io.eigr.spawn.api.Spawn;
-import io.eigr.spawn.api.TransportOpts;
-import io.eigr.spawn.java.demo.domain.Domain;
+// omitted for brevity
import java.util.Optional;
public class App {
- public static void main(String[] args) throws Exception {
+ public static void main(String[] args) {
Spawn spawnSystem = new Spawn.SpawnSystem()
.create("spawn-system")
.withActor(Joe.class)
@@ -1081,9 +1181,9 @@ public class App {
spawnSystem.start();
- ActorRef joeActor = spawnSystem.createActorRef(ActorIdentity.of("spawn-system", "joe"));
+ ActorRef joeActor = spawnSystem.createActorRef(ActorIdentity.of("spawn-system", "JoeActor"));
- Domain.Request msg = Domain.Request.newBuilder()
+ Request msg = Request.newBuilder()
.setLanguage("erlang")
.build();
@@ -1091,7 +1191,7 @@ public class App {
.timeoutSeconds(Duration.ofSeconds(30))
.build();
- Optional maybeResponse = joeActor.invoke("setLanguage", msg, Domain.Reply.class, opts);
+ Optional maybeResponse = joeActor.invoke("setLanguage", msg, Reply.class, opts);
}
}
```
diff --git a/pom.xml b/pom.xml
index c665d14..4e43e8b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
io.eigr.spawnspawn-java-std-sdkjar
- 1.2.7
+ 1.3.0spawn-java-std-sdkhttp://maven.apache.org
@@ -59,6 +59,13 @@
test
+
+ ch.qos.logback
+ logback-classic
+ 1.4.7
+ test
+
+
org.testcontainersmysql
@@ -110,7 +117,8 @@
protobuf-maven-plugin0.6.1
- com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier}
+ com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier}
+ grpc-javaio.grpc:protoc-gen-grpc-java:1.65.0:exe:${os.detected.classifier}
diff --git a/src/main/java/io/eigr/spawn/api/ActorRef.java b/src/main/java/io/eigr/spawn/api/ActorRef.java
index 4a3a703..4561847 100644
--- a/src/main/java/io/eigr/spawn/api/ActorRef.java
+++ b/src/main/java/io/eigr/spawn/api/ActorRef.java
@@ -24,9 +24,12 @@ public final class ActorRef {
private final SpawnClient client;
- private ActorRef(ActorOuterClass.ActorId actorId, SpawnClient client) {
+ private final Class type;
+
+ private ActorRef(ActorOuterClass.ActorId actorId, SpawnClient client, Class type) {
this.client = client;
this.actorId = actorId;
+ this.type = type;
}
/**
@@ -39,7 +42,7 @@ private ActorRef(ActorOuterClass.ActorId actorId, SpawnClient client) {
* @return the ActorRef instance
* @since 0.0.1
*/
- protected static ActorRef of(SpawnClient client, Cache cache, ActorIdentity identity) throws ActorCreationException {
+ protected static ActorRef of(SpawnClient client, Cache cache, ActorIdentity identity, Class actorType) throws ActorCreationException {
ActorOuterClass.ActorId actorId;
if (identity.isParent()) {
@@ -59,7 +62,7 @@ protected static ActorRef of(SpawnClient client, Cache actorIds, Spa
client.spawn(req);
}
+ public Class getType() {
+ return this.type;
+ }
+
/**
*
This method synchronously invokes an action on the actor that this ActorRef instance represents through the Spawn Proxy.
* Used when it is not necessary to send parameters to the Action.
diff --git a/src/main/java/io/eigr/spawn/api/Spawn.java b/src/main/java/io/eigr/spawn/api/Spawn.java
index 47d2e41..6d414ee 100644
--- a/src/main/java/io/eigr/spawn/api/Spawn.java
+++ b/src/main/java/io/eigr/spawn/api/Spawn.java
@@ -5,17 +5,15 @@
import com.sun.net.httpserver.HttpServer;
import io.eigr.functions.protocol.Protocol;
import io.eigr.functions.protocol.actors.ActorOuterClass;
-import io.eigr.spawn.api.actors.ActorFactory;
-import io.eigr.spawn.api.actors.annotations.stateful.StatefulNamedActor;
-import io.eigr.spawn.api.actors.annotations.stateful.StatefulPooledActor;
-import io.eigr.spawn.api.actors.annotations.stateful.StatefulUnNamedActor;
-import io.eigr.spawn.api.actors.annotations.stateless.StatelessNamedActor;
-import io.eigr.spawn.api.actors.annotations.stateless.StatelessPooledActor;
-import io.eigr.spawn.api.actors.annotations.stateless.StatelessUnNamedActor;
+import io.eigr.spawn.api.actors.BaseActor;
+import io.eigr.spawn.api.actors.StatefulActor;
+import io.eigr.spawn.api.actors.StatelessActor;
+import io.eigr.spawn.api.actors.behaviors.BehaviorCtx;
import io.eigr.spawn.api.exceptions.ActorCreationException;
import io.eigr.spawn.api.exceptions.ActorRegistrationException;
import io.eigr.spawn.api.exceptions.SpawnException;
import io.eigr.spawn.api.exceptions.SpawnFailureException;
+import io.eigr.spawn.api.extensions.DependencyInjector;
import io.eigr.spawn.internal.Entity;
import io.eigr.spawn.internal.transport.client.OkHttpSpawnClient;
import io.eigr.spawn.internal.transport.client.SpawnClient;
@@ -48,6 +46,8 @@ public final class Spawn {
private final String proxyHost;
private final int proxyPort;
private final String system;
+
+ private BehaviorCtx ctx;
private final List entities;
private final String host;
private final Executor executor;
@@ -55,6 +55,7 @@ public final class Spawn {
private Spawn(SpawnSystem builder) {
this.system = builder.system;
+ this.ctx = builder.ctx;
this.entities = builder.entities;
this.port = builder.transportOpts.getPort();
this.host = builder.transportOpts.getHost();
@@ -82,6 +83,10 @@ public String getSystem() {
return system;
}
+ public BehaviorCtx getBehaviorCtx() {
+ return ctx;
+ }
+
public int getTerminationGracePeriodSeconds() {
return terminationGracePeriodSeconds;
}
@@ -96,7 +101,18 @@ public int getTerminationGracePeriodSeconds() {
* @since 0.0.1
*/
public ActorRef createActorRef(ActorIdentity identity) throws ActorCreationException {
- return ActorRef.of(this.client, this.actorIdCache, identity);
+ Class actorType;
+ if (identity.isParent()) {
+ actorType = this.entities.stream().filter(e -> e.getActorName().equalsIgnoreCase(identity.getParent()))
+ .map(e -> e.getActorType())
+ .findFirst().get();
+ } else {
+ actorType = this.entities.stream().filter(e -> e.getActorName().equalsIgnoreCase(identity.getName()))
+ .map(e -> e.getActorType())
+ .findFirst().get();
+ }
+
+ return ActorRef.of(this.client, this.actorIdCache, identity, actorType);
}
/**
@@ -122,14 +138,25 @@ public Stream createMultiActorRefs(List identities) thr
return identities.stream().map(identity -> {
try {
+ Class actorType;
+ if (identity.isParent()) {
+ actorType = this.entities.stream().filter(e -> e.getActorName().equalsIgnoreCase(identity.getParent()))
+ .map(e -> e.getActorType())
+ .findFirst().get();
+ } else {
+ actorType = this.entities.stream().filter(e -> e.getActorName().equalsIgnoreCase(identity.getName()))
+ .map(e -> e.getActorType())
+ .findFirst().get();
+ }
+
if (identity.isParent()) {
return ActorRef.of(
this.client,
this.actorIdCache,
- ActorIdentity.of(identity.getSystem(), identity.getName(), identity.getParent(), false));
+ ActorIdentity.of(identity.getSystem(), identity.getName(), identity.getParent(), false), actorType);
}
- return ActorRef.of(this.client, this.actorIdCache, identity);
+ return ActorRef.of(this.client, this.actorIdCache, identity, actorType);
} catch (ActorCreationException e) {
throw new SpawnFailureException(e);
}
@@ -164,7 +191,9 @@ private void startServer() throws SpawnException {
}
private void registerActorSystem() throws ActorRegistrationException {
- ActorOuterClass.Registry registry = ActorOuterClass.Registry.newBuilder().putAllActors(getActors(this.entities)).build();
+ ActorOuterClass.Registry registry = ActorOuterClass.Registry.newBuilder()
+ .putAllActors(getActors(this.entities))
+ .build();
ActorOuterClass.ActorSystem actorSystem = ActorOuterClass.ActorSystem.newBuilder()
.setName(this.system)
@@ -217,13 +246,17 @@ private Map getActors(List entities) {
.setMaxPoolSize(actorEntity.getMaxPoolSize())
.build();
- Map tags = new HashMap<>();
- ActorOuterClass.Metadata metadata = ActorOuterClass.Metadata.newBuilder()
- .addChannelGroup(ActorOuterClass.Channel.newBuilder()
- .setTopic(actorEntity.getChannel())
- .build())
- .putAllTags(tags)
- .build();
+ final Map tags = new HashMap<>();
+ final ActorOuterClass.Metadata.Builder metadataBuilder = ActorOuterClass.Metadata.newBuilder();
+
+ if (Objects.nonNull(actorEntity.getChannel())){
+ metadataBuilder.addChannelGroup(ActorOuterClass.Channel.newBuilder()
+ .setTopic(actorEntity.getChannel())
+ .build());
+ }
+
+ metadataBuilder.putAllTags(tags);
+ final ActorOuterClass.Metadata metadata = metadataBuilder.build();
return ActorOuterClass.Actor.newBuilder()
.setId(ActorOuterClass.ActorId.newBuilder()
@@ -241,19 +274,23 @@ private Map getActors(List entities) {
}
private List getActions(Entity actorEntity) {
- return actorEntity.getActions().values().stream()
- .filter(v -> Entity.EntityMethodType.DIRECT.equals(v.getType()))
- .map(action -> ActorOuterClass.Action.newBuilder().setName(action.getName()).build())
+ return (List) actorEntity.getActions().values().stream()
+ .filter(v -> Entity.EntityMethodType.DIRECT.equals(((Entity.EntityMethod)v).getType()))
+ .map(action -> ActorOuterClass.Action.newBuilder()
+ .setName(((Entity.EntityMethod)action).getName())
+ .build()
+ )
.collect(Collectors.toList());
}
private List getTimerActions(Entity actorEntity) {
- List timerActions = actorEntity.getTimerActions().values()
- .stream().filter(v -> Entity.EntityMethodType.TIMER.equals(v.getType()))
+ List timerActions =
+ (List) actorEntity.getTimerActions().values()
+ .stream().filter(v -> Entity.EntityMethodType.TIMER.equals(((Entity.EntityMethod)v).getType()))
.map(action -> ActorOuterClass.FixedTimerAction.newBuilder()
.setAction(ActorOuterClass.Action.newBuilder()
- .setName(action.getName()).build())
- .setSeconds(action.getFixedPeriod()).build())
+ .setName(((Entity.EntityMethod)action).getName()).build())
+ .setSeconds(((Entity.EntityMethod)action).getFixedPeriod()).build())
.collect(Collectors.toList());
log.debug("Actor have TimeActions: {}", timerActions);
@@ -262,7 +299,13 @@ private List getTimerActions(Entity actorEntit
@Override
public String toString() {
- return new StringJoiner(", ", Spawn.class.getSimpleName() + "[", "]").add("system='" + system + "'").add("port=" + port).add("host='" + host + "'").add("proxyHost='" + proxyHost + "'").add("proxyPort=" + proxyPort).toString();
+ return new StringJoiner(", ", Spawn.class.getSimpleName() + "[", "]")
+ .add("system='" + system + "'")
+ .add("port=" + port)
+ .add("host='" + host + "'")
+ .add("proxyHost='" + proxyHost + "'")
+ .add("proxyPort=" + proxyPort)
+ .toString();
}
public static final class SpawnSystem {
@@ -270,7 +313,9 @@ public static final class SpawnSystem {
private Cache actorIdCache;
private SpawnClient client;
- private String system = "spawn-system";
+ private String system;
+
+ private BehaviorCtx ctx;
private int terminationGracePeriodSeconds = 30;
@@ -286,6 +331,13 @@ public static final class SpawnSystem {
*/
public SpawnSystem create(String system) {
this.system = system;
+ this.ctx = BehaviorCtx.create();
+ return this;
+ }
+
+ public SpawnSystem create(String system, DependencyInjector injector) {
+ this.system = system;
+ this.ctx = BehaviorCtx.create(injector);
return this;
}
@@ -301,36 +353,28 @@ public SpawnSystem createFromEnv() {
String system = System.getenv("PROXY_ACTOR_SYSTEM_NAME");
Objects.requireNonNull(system, "To use createFromEnv method it is necessary to have defined the environment variable PROXY_ACTOR_SYSTEM_NAME");
this.system = system;
+ this.ctx = BehaviorCtx.create();
return this;
}
- /**
- *
Constructor method that adds a new Actor to the Spawn proxy.
- *
- *
- * @param actorKlass the actor definition class
- * @return the SpawnSystem instance
- * @since 0.0.1
- */
- public SpawnSystem withActor(Class> actorKlass) {
- Optional maybeEntity = getEntity(actorKlass);
- maybeEntity.ifPresent(this.entities::add);
+ public SpawnSystem createFromEnv(DependencyInjector injector) {
+ String system = System.getenv("PROXY_ACTOR_SYSTEM_NAME");
+ Objects.requireNonNull(system, "To use createFromEnv method it is necessary to have defined the environment variable PROXY_ACTOR_SYSTEM_NAME");
+ this.system = system;
+ this.ctx = BehaviorCtx.create(injector);
return this;
}
/**
*
Constructor method that adds a new Actor to the Spawn proxy.
- * Allows options to be passed to the class constructor. The constructor must consist of only one argument
*
*
* @param actorKlass the actor definition class
- * @param arg the object that will be passed as an argument to the constructor via the lambda fabric
- * @param factory a lambda that constructs the instance of the Actor object
* @return the SpawnSystem instance
* @since 0.0.1
*/
- public SpawnSystem withActor(Class> actorKlass, Object arg, ActorFactory factory) {
- Optional maybeEntity = getEntity(actorKlass, arg, factory);
+ public SpawnSystem withActor(Class actorKlass) throws ActorCreationException {
+ Optional maybeEntity = getEntity(actorKlass);
maybeEntity.ifPresent(this.entities::add);
return this;
}
@@ -362,14 +406,9 @@ public Spawn build() {
return new Spawn(this);
}
- private Optional getEntity(Class> actorKlass) {
- Optional maybeEntity = getStatefulEntity(actorKlass, null, null);
-
- if (maybeEntity.isPresent()) {
- return maybeEntity;
- }
+ private Optional getEntity(Class> actorKlass) throws ActorCreationException {
+ Optional maybeEntity = mapEntity(actorKlass);
- maybeEntity = getStatelessEntity(actorKlass, null, null);
if (maybeEntity.isPresent()) {
return maybeEntity;
}
@@ -377,48 +416,13 @@ private Optional getEntity(Class> actorKlass) {
return Optional.empty();
}
- private Optional getEntity(Class> actorKlass, Object arg, ActorFactory factory) {
- Optional maybeEntity = getStatefulEntity(actorKlass, arg, factory);
-
- if (maybeEntity.isPresent()) {
- return maybeEntity;
- }
-
- maybeEntity = getStatelessEntity(actorKlass, arg, factory);
- if (maybeEntity.isPresent()) {
- return maybeEntity;
- }
-
- return Optional.empty();
- }
-
- private Optional getStatefulEntity(Class> actorKlass, Object arg, ActorFactory factory) {
- if (Objects.nonNull(actorKlass.getAnnotation(StatefulNamedActor.class))) {
- return Optional.of(Entity.fromAnnotationToEntity(actorKlass, actorKlass.getAnnotation(StatefulNamedActor.class), arg, factory));
- }
-
- if (Objects.nonNull(actorKlass.getAnnotation(StatefulUnNamedActor.class))) {
- return Optional.of(Entity.fromAnnotationToEntity(actorKlass, actorKlass.getAnnotation(StatefulUnNamedActor.class), arg, factory));
- }
-
- if (Objects.nonNull(actorKlass.getAnnotation(StatefulPooledActor.class))) {
- return Optional.of(Entity.fromAnnotationToEntity(actorKlass, actorKlass.getAnnotation(StatefulPooledActor.class), arg, factory));
- }
-
- return Optional.empty();
- }
-
- private Optional getStatelessEntity(Class> actorKlass, Object arg, ActorFactory factory) {
- if (Objects.nonNull(actorKlass.getAnnotation(StatelessNamedActor.class))) {
- return Optional.of(Entity.fromAnnotationToEntity(actorKlass, actorKlass.getAnnotation(StatelessNamedActor.class), arg, factory));
- }
-
- if (Objects.nonNull(actorKlass.getAnnotation(StatelessUnNamedActor.class))) {
- return Optional.of(Entity.fromAnnotationToEntity(actorKlass, actorKlass.getAnnotation(StatelessUnNamedActor.class), arg, factory));
+ private Optional mapEntity(Class> actorKlass) throws ActorCreationException {
+ if (StatefulActor.class.isAssignableFrom(actorKlass)) {
+ return Optional.of(Entity.fromStatefulActorToEntity(ctx, actorKlass));
}
- if (Objects.nonNull(actorKlass.getAnnotation(StatelessPooledActor.class))) {
- return Optional.of(Entity.fromAnnotationToEntity(actorKlass, actorKlass.getAnnotation(StatelessPooledActor.class), arg, factory));
+ if (StatelessActor.class.isAssignableFrom(actorKlass)) {
+ return Optional.of(Entity.fromStatelessActorToEntity(ctx, actorKlass));
}
return Optional.empty();
diff --git a/src/main/java/io/eigr/spawn/api/actors/ActorFactory.java b/src/main/java/io/eigr/spawn/api/actors/ActorFactory.java
deleted file mode 100644
index e208167..0000000
--- a/src/main/java/io/eigr/spawn/api/actors/ActorFactory.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package io.eigr.spawn.api.actors;
-
-@FunctionalInterface
-public interface ActorFactory {
- Object newInstance(Object arg);
-}
diff --git a/src/main/java/io/eigr/spawn/api/actors/BaseActor.java b/src/main/java/io/eigr/spawn/api/actors/BaseActor.java
new file mode 100644
index 0000000..477c8f8
--- /dev/null
+++ b/src/main/java/io/eigr/spawn/api/actors/BaseActor.java
@@ -0,0 +1,13 @@
+package io.eigr.spawn.api.actors;
+
+import io.eigr.spawn.api.actors.behaviors.ActorBehavior;
+import io.eigr.spawn.api.actors.behaviors.BehaviorCtx;
+
+public abstract class BaseActor {
+
+ public abstract ActorBehavior configure(BehaviorCtx ctx);
+
+ public abstract Boolean isStateful();
+
+
+}
diff --git a/src/main/java/io/eigr/spawn/api/actors/StatefulActor.java b/src/main/java/io/eigr/spawn/api/actors/StatefulActor.java
new file mode 100644
index 0000000..333440d
--- /dev/null
+++ b/src/main/java/io/eigr/spawn/api/actors/StatefulActor.java
@@ -0,0 +1,16 @@
+package io.eigr.spawn.api.actors;
+
+import java.lang.reflect.ParameterizedType;
+
+public abstract class StatefulActor extends BaseActor {
+ public Class getStateType() {
+ return (Class) ((ParameterizedType)
+ getClass().getGenericSuperclass())
+ .getActualTypeArguments()[0];
+ }
+
+ @Override
+ public Boolean isStateful() {
+ return true;
+ }
+}
diff --git a/src/main/java/io/eigr/spawn/api/actors/StatelessActor.java b/src/main/java/io/eigr/spawn/api/actors/StatelessActor.java
new file mode 100644
index 0000000..2ef0272
--- /dev/null
+++ b/src/main/java/io/eigr/spawn/api/actors/StatelessActor.java
@@ -0,0 +1,8 @@
+package io.eigr.spawn.api.actors;
+
+public abstract class StatelessActor extends BaseActor {
+ @Override
+ public Boolean isStateful() {
+ return false;
+ }
+}
diff --git a/src/main/java/io/eigr/spawn/api/actors/annotations/Action.java b/src/main/java/io/eigr/spawn/api/actors/annotations/Action.java
deleted file mode 100644
index 063b8a5..0000000
--- a/src/main/java/io/eigr/spawn/api/actors/annotations/Action.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package io.eigr.spawn.api.actors.annotations;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-@SpawnAnnotation
-@Target(ElementType.METHOD)
-@Retention(RetentionPolicy.RUNTIME)
-public @interface Action {
-
- /**
- * The name of the command to handle.
- *
- *
If not specified, the name of the method will be used as the command name.
- *
- * @return The command name.
- */
- String name() default "";
-
- /**
- * * The input type.
- *
- * The type class of input method parameter. Generally, this will be determined by looking at the parameter of the input type
- * handler method, however if the event doesn't need to be passed to the method (for example,
- * perhaps it contains no data), then this can be used to indicate which event this handler
- * handles.
- */
- Class> inputType() default Default.class;
-
- Class> outputType() default Default.class;
-
- class Default {}
-}
\ No newline at end of file
diff --git a/src/main/java/io/eigr/spawn/api/actors/annotations/SpawnAnnotation.java b/src/main/java/io/eigr/spawn/api/actors/annotations/SpawnAnnotation.java
deleted file mode 100644
index 00eff94..0000000
--- a/src/main/java/io/eigr/spawn/api/actors/annotations/SpawnAnnotation.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package io.eigr.spawn.api.actors.annotations;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-@Target(ElementType.ANNOTATION_TYPE)
-@Retention(RetentionPolicy.RUNTIME)
-public @interface SpawnAnnotation {
-}
diff --git a/src/main/java/io/eigr/spawn/api/actors/annotations/TimerAction.java b/src/main/java/io/eigr/spawn/api/actors/annotations/TimerAction.java
deleted file mode 100644
index b437ca3..0000000
--- a/src/main/java/io/eigr/spawn/api/actors/annotations/TimerAction.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package io.eigr.spawn.api.actors.annotations;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-@SpawnAnnotation
-@Target(ElementType.METHOD)
-@Retention(RetentionPolicy.RUNTIME)
-public @interface TimerAction {
-
- /**
- * The name of the action to handle.
- *
- *