diff --git a/README.md b/README.md index 87304ba..fe1bd44 100644 --- a/README.md +++ b/README.md @@ -462,29 +462,28 @@ public final class Joe { } ``` -Then you also need to register your Actor passing arguments like as follows: +Then you also need to register your Actor passing arguments like as follows: ```java package io.eigr.spawn.java.demo; import io.eigr.spawn.api.Spawn; -import io.eigr.spawn.api.actors.ActorRef; import java.util.HashMap; import java.util.Map; public class App { - public static void main(String[] args) { - Map actorConstructorArgs = new HashMap<>(); - actorConstructorArgs.put("someKey", "someValue"); - - Spawn spawnSystem = new Spawn.SpawnSystem() - .create("spawn-system") - .withActor(Joe.class, actorConstructorArgs, arg -> new Joe((Map) arg)) - .build(); + public static void main(String[] args) { + Map actorConstructorArgs = new HashMap<>(); + actorConstructorArgs.put("someKey", "someValue"); - spawnSystem.start(); - } + Spawn spawnSystem = new Spawn.SpawnSystem() + .create("spawn-system") + .withActor(Joe.class, actorConstructorArgs, arg -> new Joe((Map) arg)) + .build(); + + spawnSystem.start(); + } } ``` @@ -635,9 +634,9 @@ See an example: ```Java package io.eigr.spawn.java.demo; +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.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.SideEffect; @@ -683,7 +682,7 @@ 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.ActorRef; +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; @@ -725,7 +724,7 @@ 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.ActorRef; +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; @@ -848,22 +847,20 @@ package io.eigr.spawn.java.demo; import io.eigr.spawn.api.Spawn; import io.eigr.spawn.api.Spawn.SpawnSystem; -import io.eigr.spawn.api.actors.ActorRef; +import io.eigr.spawn.api.ActorRef; import io.eigr.spawn.api.TransportOpts; import io.eigr.spawn.java.demo.domain.Domain; -import java.util.Optional; - public class App { public static void main(String[] args) throws Exception { Spawn spawnSystem = new SpawnSystem() .create("spawn-system") .withActor(Joe.class) .withTransportOptions( - TransportOpts.builder() - .port(8091) - .proxyPort(9003) - .build() + TransportOpts.builder() + .port(8091) + .proxyPort(9003) + .build() ) .build(); @@ -944,7 +941,7 @@ just a fire-and-forget call. Therefore, to call an actor's function asynchronously, just use the invokeAsync method: ```Java -mike.invokeAsync("setLanguage", msg, Domain.Reply.class); +mike.invokeAsync("setLanguage", msg); ``` ## Deploy diff --git a/src/main/java/io/eigr/spawn/api/actors/ActorRef.java b/src/main/java/io/eigr/spawn/api/ActorRef.java similarity index 50% rename from src/main/java/io/eigr/spawn/api/actors/ActorRef.java rename to src/main/java/io/eigr/spawn/api/ActorRef.java index 93f2641..2f71432 100644 --- a/src/main/java/io/eigr/spawn/api/actors/ActorRef.java +++ b/src/main/java/io/eigr/spawn/api/ActorRef.java @@ -1,4 +1,4 @@ -package io.eigr.spawn.api.actors; +package io.eigr.spawn.api; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; @@ -7,7 +7,6 @@ import com.google.protobuf.GeneratedMessageV3; import io.eigr.functions.protocol.Protocol; import io.eigr.functions.protocol.actors.ActorOuterClass; -import io.eigr.spawn.api.InvocationOpts; import io.eigr.spawn.api.exceptions.ActorInvokeException; import io.eigr.spawn.api.exceptions.ActorNotFoundException; import io.eigr.spawn.internal.transport.client.SpawnClient; @@ -16,6 +15,12 @@ import java.util.Objects; import java.util.Optional; +/** + * ActorRef is responsible for representing an instance of an Actor + * + * @author Adriano Santos + * + */ public final class ActorRef { private static final int CACHE_MAXIMUM_SIZE = 1_000; private static final int CACHE_EXPIRE_AFTER_WRITE_SECONDS = 60; @@ -32,8 +37,17 @@ private ActorRef(ActorOuterClass.ActorId actorId, SpawnClient client) { this.client = client; this.actorId = actorId; } - - public static ActorRef of(SpawnClient client, String system, String name) throws Exception { + + /** + *

This method is responsible for creating instances of the ActorRef class + *

+ * @param client is the client part of the Spawn protocol and is responsible for communicating with the Proxy. + * @param system ActorSystem name of the actor that this ActorRef instance should represent + * @param name the name of the actor that this ActorRef instance should represent + * @return the ActorRef instance + * @since 0.0.1 + */ + protected static ActorRef of(SpawnClient client, String system, String name) throws Exception { ActorOuterClass.ActorId actorId = buildActorId(system, name); ActorRef ref = ACTOR_REF_CACHE.getIfPresent(actorId); if (Objects.nonNull(ref)){ @@ -45,7 +59,17 @@ public static ActorRef of(SpawnClient client, String system, String name) throws return ref; } - public static ActorRef of(SpawnClient client, String system, String name, String parent) throws Exception { + /** + *

This method is responsible for creating instances of the ActorRef class when Actor is a UnNamed actor. + *

+ * @param client is the client part of the Spawn protocol and is responsible for communicating with the Proxy. + * @param system ActorSystem name of the actor that this ActorRef instance should represent + * @param name the name of the actor that this ActorRef instance should represent + * @param parent the name of the unnamed parent actor + * @return the ActorRef instance + * @since 0.0.1 + */ + protected static ActorRef of(SpawnClient client, String system, String name, String parent) throws Exception { ActorOuterClass.ActorId actorId = buildActorId(system, name, parent); ActorRef ref = ACTOR_REF_CACHE.getIfPresent(actorId); if (Objects.nonNull(ref)){ @@ -58,8 +82,17 @@ public static ActorRef of(SpawnClient client, String system, String name, String return ref; } - public Optional invoke(String cmd, Class outputType) throws Exception { - Optional res = invokeActor(cmd, Empty.getDefaultInstance(), outputType, Optional.empty()); + /** + *

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. + *

+ * @param action name of the action to be called. + * @param outputType the class that corresponds to the expected return type + * @return an Optional containing, or not, the response object to the Action call + * @since 0.0.1 + */ + public Optional invoke(String action, Class outputType) throws Exception { + Optional res = invokeActor(action, Empty.getDefaultInstance(), outputType, Optional.empty()); if(res.isPresent() ){ return Optional.of(outputType.cast(res.get())); } @@ -67,8 +100,19 @@ public Optional invoke(String cmd, Class return res; } - public Optional invoke(String cmd, Class outputType, InvocationOpts opts) throws Exception { - Optional res = invokeActor(cmd, Empty.getDefaultInstance(), outputType, Optional.ofNullable(opts)); + /** + *

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. + *

+ * @param action name of the action to be called. + * @param outputType the class that corresponds to the expected return type + * @param opts options that can be passed during the invocation of the Action. + * Please see the {@link io.eigr.spawn.api.InvocationOpts} class for more information + * @return an Optional containing, or not, the response object to the Action call + * @since 0.0.1 + */ + public Optional invoke(String action, Class outputType, InvocationOpts opts) throws Exception { + Optional res = invokeActor(action, Empty.getDefaultInstance(), outputType, Optional.ofNullable(opts)); if(res.isPresent() ){ return Optional.of(outputType.cast(res.get())); } @@ -76,8 +120,18 @@ public Optional invoke(String cmd, Class return res; } - public Optional invoke(String cmd, S value, Class outputType) throws Exception { - Optional res = invokeActor(cmd, value, outputType, Optional.empty()); + /** + *

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. + *

+ * @param action name of the action to be called. + * @param value the action argument object. + * @param outputType the class that corresponds to the expected return type + * @return an Optional containing, or not, the response object to the Action call + * @since 0.0.1 + */ + public Optional invoke(String action, S value, Class outputType) throws Exception { + Optional res = invokeActor(action, value, outputType, Optional.empty()); if(res.isPresent() ){ return Optional.of(outputType.cast(res.get())); } @@ -85,8 +139,20 @@ public Optional Optional invoke(String cmd, S value, Class outputType, InvocationOpts opts) throws Exception { - Optional res = invokeActor(cmd, value, outputType, Optional.ofNullable(opts)); + /** + *

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. + *

+ * @param action name of the action to be called. + * @param value the action argument object. + * @param outputType the class that corresponds to the expected return type + * @param opts options that can be passed during the invocation of the Action. + * Please see the {@link io.eigr.spawn.api.InvocationOpts} class for more information + * @return an Optional containing, or not, the response object to the Action call + * @since 0.0.1 + */ + public Optional invoke(String action, S value, Class outputType, InvocationOpts opts) throws Exception { + Optional res = invokeActor(action, value, outputType, Optional.ofNullable(opts)); if(res.isPresent() ){ return Optional.of(outputType.cast(res.get())); } @@ -94,34 +160,68 @@ public Optional void invokeAsync(String cmd, Class outputType) throws Exception { + /** + *

This method asynchronously invokes an action on the actor that this ActorRef instance represents via the Spawn Proxy. + * Used when it is not necessary to send parameters to the Action. + *

+ * @param action name of the action to be called. + * @since 0.0.1 + */ + public void invokeAsync(String action) throws Exception { InvocationOpts opts = InvocationOpts.builder().async(true).build(); - invokeActor(cmd, Empty.getDefaultInstance(), outputType, Optional.of(opts)); + invokeActor(action, Empty.getDefaultInstance(), null, Optional.of(opts)); } - public void invokeAsync(String cmd, Class outputType, InvocationOpts opts) throws Exception { + /** + *

This method asynchronously invokes an action on the actor that this ActorRef instance represents via the Spawn Proxy. + * Used when it is not necessary to send parameters to the Action. + *

+ * @param action name of the action to be called. + * @param opts options that can be passed during the invocation of the Action. + * Please see the {@link io.eigr.spawn.api.InvocationOpts} class for more information + * @since 0.0.1 + */ + public void invokeAsync(String action, InvocationOpts opts) throws Exception { InvocationOpts mergedOpts = InvocationOpts.builder() .async(true) .delay(opts.getDelay()) .scheduledTo(opts.getScheduledTo()) .build(); - invokeActor(cmd, Empty.getDefaultInstance(), outputType, Optional.ofNullable(mergedOpts)); + invokeActor(action, Empty.getDefaultInstance(), null, Optional.ofNullable(mergedOpts)); } - public void invokeAsync(String cmd, S value, Class outputType) throws Exception { + /** + *

This method asynchronously 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. + *

+ * @param action name of the action to be called. + * @param value the action argument object. + * @since 0.0.1 + */ + public void invokeAsync(String action, S value) throws Exception { InvocationOpts opts = InvocationOpts.builder().async(true).build(); - invokeActor(cmd, value, outputType, Optional.of(opts)); + invokeActor(action, value, null, Optional.of(opts)); } - public void invokeAsync(String cmd, S value, Class outputType, InvocationOpts opts) throws Exception { + /** + *

This method asynchronously 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. + *

+ * @param action name of the action to be called. + * @param value the action argument object. + * @param opts options that can be passed during the invocation of the Action. + * Please see the {@link io.eigr.spawn.api.InvocationOpts} class for more information + * @since 0.0.1 + */ + public void invokeAsync(String action, S value, InvocationOpts opts) throws Exception { InvocationOpts mergedOpts = InvocationOpts.builder() .async(true) .delay(opts.getDelay()) .scheduledTo(opts.getScheduledTo()) .build(); - invokeActor(cmd, value, outputType, Optional.of(mergedOpts)); + invokeActor(action, value, null, Optional.of(mergedOpts)); } public String getActorSystem() { @@ -187,7 +287,7 @@ private OptionalThis method is responsible for creating instances of the ActorRef class. + * See more about ActorRef in {@link io.eigr.spawn.api.InvocationOpts} class + *

+ * @param system ActorSystem name of the actor that this ActorRef instance should represent + * @param name the name of the actor that this ActorRef instance should represent + * @return the ActorRef instance + * @since 0.0.1 + */ public ActorRef createActorRef(String system, String name) throws Exception { return ActorRef.of(this.client, system, name); } + /** + *

This method is responsible for creating instances of the ActorRef class when Actor is a UnNamed actor. + * See more about ActorRef in {@link io.eigr.spawn.api.InvocationOpts} class + *

+ * @param system ActorSystem name of the actor that this ActorRef instance should represent + * @param name the name of the actor that this ActorRef instance should represent + * @param parent the name of the unnamed parent actor + * @return the ActorRef instance + * @since 0.0.1 + */ public ActorRef createActorRef(String system, String name, String parent) throws Exception { return ActorRef.of(this.client, system, name, parent); } + /** + *

This method Starts communication with the Spawn proxy. + *

+ * @since 0.0.1 + */ public void start() throws Exception { startServer(); registerActorSystem(); @@ -226,11 +249,25 @@ public static final class SpawnSystem { private TransportOpts transportOpts = TransportOpts.builder().build(); + /** + *

Builder method that establishes the ActorSystem to which the application will be part. + *

+ * @param system ActorSystem name of the actor that this ActorRef instance should represent + * @return the SpawnSystem instance + * @since 0.0.1 + */ public SpawnSystem create(String system) { this.system = system; return this; } + /** + *

Builder method that establishes the ActorSystem to which the application will be part. + * The name of the ActorSystem will be captured at runtime via an environment variable called PROXY_ACTOR_SYSTEM_NAME + *

+ * @return the SpawnSystem instance + * @since 0.0.1 + */ 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"); @@ -238,6 +275,13 @@ public SpawnSystem createFromEnv() { 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); if (maybeEntity.isPresent()) { @@ -246,6 +290,16 @@ public SpawnSystem withActor(Class actorKlass) { 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); if (maybeEntity.isPresent()) { @@ -254,6 +308,12 @@ public SpawnSystem withActor(Class actorKlass, Object arg, ActorFactory facto return this; } + /** + * @param opts TransportOpts instance with options for communicating with the proxy as well as other transport + * settings such as the type of Executor to be used to handle incoming requests. + * @return the SpawnSystem instance + * @since 0.0.1 + */ public SpawnSystem withTransportOptions(TransportOpts opts) { this.transportOpts = opts; return this; diff --git a/src/main/java/io/eigr/spawn/api/actors/workflows/Forward.java b/src/main/java/io/eigr/spawn/api/actors/workflows/Forward.java index 007e5be..e15952d 100644 --- a/src/main/java/io/eigr/spawn/api/actors/workflows/Forward.java +++ b/src/main/java/io/eigr/spawn/api/actors/workflows/Forward.java @@ -1,7 +1,7 @@ package io.eigr.spawn.api.actors.workflows; import io.eigr.functions.protocol.Protocol; -import io.eigr.spawn.api.actors.ActorRef; +import io.eigr.spawn.api.ActorRef; import java.util.Objects; import java.util.StringJoiner; diff --git a/src/main/java/io/eigr/spawn/api/actors/workflows/Pipe.java b/src/main/java/io/eigr/spawn/api/actors/workflows/Pipe.java index a44eac4..fa5f812 100644 --- a/src/main/java/io/eigr/spawn/api/actors/workflows/Pipe.java +++ b/src/main/java/io/eigr/spawn/api/actors/workflows/Pipe.java @@ -1,7 +1,7 @@ package io.eigr.spawn.api.actors.workflows; import io.eigr.functions.protocol.Protocol; -import io.eigr.spawn.api.actors.ActorRef; +import io.eigr.spawn.api.ActorRef; import org.jetbrains.annotations.NotNull; import java.util.Objects; diff --git a/src/main/java/io/eigr/spawn/api/actors/workflows/SideEffect.java b/src/main/java/io/eigr/spawn/api/actors/workflows/SideEffect.java index 004e3d4..a2a46c4 100644 --- a/src/main/java/io/eigr/spawn/api/actors/workflows/SideEffect.java +++ b/src/main/java/io/eigr/spawn/api/actors/workflows/SideEffect.java @@ -5,7 +5,7 @@ import io.eigr.functions.protocol.Protocol; import io.eigr.functions.protocol.actors.ActorOuterClass; import io.eigr.spawn.api.InvocationOpts; -import io.eigr.spawn.api.actors.ActorRef; +import io.eigr.spawn.api.ActorRef; import java.util.Objects; import java.util.Optional; diff --git a/src/test/java/io/eigr/spawn/SpawnTest.java b/src/test/java/io/eigr/spawn/SpawnTest.java index aebbb7e..923522e 100644 --- a/src/test/java/io/eigr/spawn/SpawnTest.java +++ b/src/test/java/io/eigr/spawn/SpawnTest.java @@ -1,7 +1,7 @@ package io.eigr.spawn; import io.eigr.spawn.api.Spawn; -import io.eigr.spawn.api.actors.ActorRef; +import io.eigr.spawn.api.ActorRef; import io.eigr.spawn.api.TransportOpts; import io.eigr.spawn.java.test.domain.Actor; import io.eigr.spawn.test.actors.JoeActor; diff --git a/src/test/java/io/eigr/spawn/WorkflowTest.java b/src/test/java/io/eigr/spawn/WorkflowTest.java index 99614a4..7c3102f 100644 --- a/src/test/java/io/eigr/spawn/WorkflowTest.java +++ b/src/test/java/io/eigr/spawn/WorkflowTest.java @@ -1,18 +1,41 @@ package io.eigr.spawn; import io.eigr.functions.protocol.Protocol; -import io.eigr.spawn.api.actors.ActorRef; +import io.eigr.spawn.api.ActorRef; +import io.eigr.spawn.api.Spawn; +import io.eigr.spawn.api.TransportOpts; import io.eigr.spawn.api.actors.workflows.Broadcast; import io.eigr.spawn.api.actors.workflows.Forward; import io.eigr.spawn.api.actors.workflows.Pipe; import io.eigr.spawn.api.actors.workflows.SideEffect; import io.eigr.spawn.java.test.domain.Actor; +import io.eigr.spawn.test.actors.JoeActor; +import org.junit.Before; import org.junit.Test; import static org.junit.Assert.*; public class WorkflowTest { + private ActorRef joeActorRef; + private Spawn spawnSystem; + @Before + public void before() throws Exception { + spawnSystem = new Spawn.SpawnSystem() + .create("spawn-system") + .withActor(JoeActor.class) + .withTransportOptions( + TransportOpts.builder() + .port(8091) + .proxyPort(9003) + .build() + ) + .build(); + + joeActorRef = spawnSystem.createActorRef("spawn-system", "joe"); + } + + @Test public void testBroadcastBuilder() { Broadcast broadcast = Broadcast.to("test.channel", "hi", Actor.Request.getDefaultInstance()); @@ -24,7 +47,7 @@ public void testBroadcastBuilder() { @Test public void testForwardBuilder() throws Exception { - Forward forward = Forward.to(ActorRef.of(null, "spawn-system", "joe"), "hi"); + Forward forward = Forward.to(joeActorRef, "hi"); final Protocol.Forward protocolForward = forward.build(); assertEquals("hi", protocolForward.getActionName()); assertEquals("joe", protocolForward.getActor()); @@ -32,7 +55,7 @@ public void testForwardBuilder() throws Exception { @Test public void testPipeBuilder() throws Exception { - Pipe pipe = Pipe.to(ActorRef.of(null, "spawn-system", "joe"), "hi"); + Pipe pipe = Pipe.to(joeActorRef, "hi"); final Protocol.Pipe protocolPipe = pipe.build(); assertEquals("hi", protocolPipe.getActionName()); assertEquals("joe", protocolPipe.getActor()); @@ -40,7 +63,7 @@ public void testPipeBuilder() throws Exception { @Test public void testSideEffectBuilder() throws Exception { - SideEffect effect = SideEffect.to(ActorRef.of(null, "spawn-system", "joe"), "hi", Actor.Request.getDefaultInstance()); + SideEffect effect = SideEffect.to(joeActorRef, "hi", Actor.Request.getDefaultInstance()); final Protocol.SideEffect protocolSideEffect = effect.build(); Protocol.InvocationRequest request = protocolSideEffect.getRequest(); assertNotNull(request);