@@ -7,6 +7,8 @@ JVM User Language Support for [Spawn](https://github.com/eigr/spawn).
7
7
2 . [ Getting Started] ( #getting-started )
8
8
3 . [ Advanced Use Cases] ( #advanced-use-cases )
9
9
- [ Types of Actors] ( #types-of-actors )
10
+ - [ Stateless Actors] ( #stateless-actors )
11
+ - [ Considerations about Spawn actors] ( #considerations-about-spawn-actors )
10
12
- [ Broadcast] ( #broadcast )
11
13
- [ Side Effects] ( #side-effects )
12
14
- [ Forward] ( #forward )
@@ -90,7 +92,7 @@ The second thing we have to do is add the spawn dependency to the project.
90
92
<dependency >
91
93
<groupId >com.github.eigr</groupId >
92
94
<artifactId >spawn-java-std-sdk</artifactId >
93
- <version >v0.4.1 </version >
95
+ <version >v0.5.0 </version >
94
96
</dependency >
95
97
```
96
98
We're also going to configure a few things for our application build to work, including compiling the protobuf files.
@@ -124,7 +126,7 @@ See below a full example of the pom.xml file:
124
126
<dependency >
125
127
<groupId >com.github.eigr</groupId >
126
128
<artifactId >spawn-java-std-sdk</artifactId >
127
- <version >v0.4.1 </version >
129
+ <version >v0.5.0 </version >
128
130
</dependency >
129
131
<dependency >
130
132
<groupId >ch.qos.logback</groupId >
@@ -286,12 +288,12 @@ package io.eigr.spawn.java.demo;
286
288
import io.eigr.spawn.api.Value ;
287
289
import io.eigr.spawn.api.actors.ActorContext ;
288
290
import io.eigr.spawn.api.actors.annotations.Action ;
289
- import io.eigr.spawn.api.actors.annotations.NamedActor ;
291
+ import io.eigr.spawn.api.actors.annotations.stateful.StatefulNamedActor ;
290
292
import io.eigr.spawn.java.demo.domain.Domain ;
291
293
import org.slf4j.Logger ;
292
294
import org.slf4j.LoggerFactory ;
293
295
294
- @NamedActor (name = " joe" , stateType = Domain . JoeState . class)
296
+ @StatefulNamedActor (name = " joe" , stateType = Domain . JoeState . class)
295
297
public class Joe {
296
298
private static final Logger log = LoggerFactory . getLogger(Joe . class);
297
299
@@ -408,12 +410,12 @@ package io.eigr.spawn.java.demo;
408
410
import io.eigr.spawn.api.Value ;
409
411
import io.eigr.spawn.api.actors.ActorContext ;
410
412
import io.eigr.spawn.api.actors.annotations.Action ;
411
- import io.eigr.spawn.api.actors.annotations.NamedActor ;
413
+ import io.eigr.spawn.api.actors.annotations.stateful.StatefulNamedActor ;
412
414
import io.eigr.spawn.java.demo.domain.Domain ;
413
415
414
416
import java.util.Map ;
415
417
416
- @NamedActor (name = " joe" , stateful = true , stateType = Domain . JoeState . class, channel = " test" )
418
+ @StatefulNamedActor (name = " joe" , stateful = true , stateType = Domain . JoeState . class, channel = " test" )
417
419
public final class Joe {
418
420
private final String someValue;
419
421
@@ -509,6 +511,37 @@ during program execution. Otherwise, they behave like named actors.
509
511
assigned to them at compile time. Pooled actors are generally used when higher performance is needed and are also
510
512
recommended for handling serverless loads.
511
513
514
+ ### Stateless Actors
515
+
516
+ In addition to these types, Spawn also allows the developer to choose Stateful actors, who need to maintain the state, or Stateless, those who do not need to maintain the state.
517
+ 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:
518
+
519
+ ``` java
520
+ package io.eigr.spawn.test.actors ;
521
+
522
+ import io.eigr.spawn.api.Value ;
523
+ import io.eigr.spawn.api.actors.ActorContext ;
524
+ import io.eigr.spawn.api.actors.annotations.Action ;
525
+ import io.eigr.spawn.api.actors.annotations.stateless.StatelessNamedActor ;
526
+ import io.eigr.spawn.java.test.domain.Actor ;
527
+
528
+ @StatelessNamedActor (name = " test_joe" , channel = " test.channel" )
529
+ public class JoeActor {
530
+ @Action
531
+ public Value hi (Actor .Request msg , ActorContext<?> context ) {
532
+ return Value . at()
533
+ .response(Actor . Reply . newBuilder()
534
+ .setResponse(" Hello From Java" )
535
+ .build())
536
+ .reply();
537
+ }
538
+ }
539
+ ```
540
+
541
+ Other than that the same Named, UnNamed types are supported. Just use the StatelessNamed or StatelessUnNamed annotations.
542
+
543
+ ### Considerations about Spawn actors
544
+
512
545
Another important feature of Spawn Actors is that the lifecycle of each Actor is managed by the platform itself.
513
546
This means that an Actor will exist when it is invoked and that it will be deactivated after an idle time in its execution.
514
547
This pattern is known as [ Virtual Actors] ( #virtual-actors ) but Spawn's implementation differs from some other known
@@ -526,7 +559,7 @@ Actors in Spawn can subscribe to a thread and receive, as well as broadcast, eve
526
559
To consume from a topic, you just need to configure the Actor annotation using the channel option as follows:
527
560
528
561
``` Java
529
- @NamedActor (name = " joe" , stateful = true , stateType = Domain . JoeState . class, channel = " test" )
562
+ @StatefulNamedActor (name = " joe" , stateful = true , stateType = Domain . JoeState . class, channel = " test" )
530
563
```
531
564
In the case above, the Actor ` joe ` was configured to receive events that are forwarded to the topic called ` test ` .
532
565
@@ -540,7 +573,7 @@ package io.eigr.spawn.java.demo;
540
573
import io.eigr.spawn.api.actors.workflows.Broadcast ;
541
574
// some imports omitted for brevity
542
575
543
- @NamedActor (name = " joe" , stateful = true , stateType = Domain . JoeState . class, channel = " test" )
576
+ @StatefulNamedActor (name = " joe" , stateType = Domain . JoeState . class, channel = " test" )
544
577
public class Joe {
545
578
@TimerAction (name = " hi" , period = 60000 )
546
579
public Value hi (ActorContext<Domain . JoeState > context ) {
@@ -582,11 +615,11 @@ import io.eigr.spawn.api.Value;
582
615
import io.eigr.spawn.api.actors.ActorContext ;
583
616
import io.eigr.spawn.api.actors.ActorRef ;
584
617
import io.eigr.spawn.api.actors.annotations.Action ;
585
- import io.eigr.spawn.api.actors.annotations.NamedActor ;
618
+ import io.eigr.spawn.api.actors.annotations.stateful.StatefulNamedActor ;
586
619
import io.eigr.spawn.api.actors.workflows.SideEffect ;
587
620
import io.eigr.spawn.java.demo.domain.Domain ;
588
621
589
- @NamedActor (name = " side_effect_actor" , stateful = true , stateType = Domain . State . class)
622
+ @StatefulNamedActor (name = " side_effect_actor" , stateful = true , stateType = Domain . State . class)
590
623
public class SideEffectActorExample {
591
624
@Action
592
625
public Value setLanguage (Domain .Request msg , ActorContext<Domain . State > ctx ) throws Exception {
@@ -628,13 +661,13 @@ import io.eigr.spawn.api.Value;
628
661
import io.eigr.spawn.api.actors.ActorContext ;
629
662
import io.eigr.spawn.api.actors.ActorRef ;
630
663
import io.eigr.spawn.api.actors.annotations.Action ;
631
- import io.eigr.spawn.api.actors.annotations.NamedActor ;
664
+ import io.eigr.spawn.api.actors.annotations.stateful.StatefulNamedActor ;
632
665
import io.eigr.spawn.api.actors.workflows.Forward ;
633
666
import io.eigr.spawn.java.demo.domain.Domain ;
634
667
import org.slf4j.Logger ;
635
668
import org.slf4j.LoggerFactory ;
636
669
637
- @NamedActor (name = " routing_actor" , stateful = true , stateType = Domain . State . class)
670
+ @StatefulNamedActor (name = " routing_actor" , stateful = true , stateType = Domain . State . class)
638
671
public class ForwardExample {
639
672
private static final Logger log = LoggerFactory . getLogger(ForwardExample . class);
640
673
@@ -670,11 +703,11 @@ import io.eigr.spawn.api.Value;
670
703
import io.eigr.spawn.api.actors.ActorContext ;
671
704
import io.eigr.spawn.api.actors.ActorRef ;
672
705
import io.eigr.spawn.api.actors.annotations.Action ;
673
- import io.eigr.spawn.api.actors.annotations.NamedActor ;
706
+ import io.eigr.spawn.api.actors.annotations.stateful.StatefulNamedActor ;
674
707
import io.eigr.spawn.api.actors.workflows.Pipe ;
675
708
import io.eigr.spawn.java.demo.domain.Domain ;
676
709
677
- @NamedActor (name = " pipe_actor" , stateful = true , stateType = Domain . State . class)
710
+ @StatefulNamedActor (name = " pipe_actor" , stateful = true , stateType = Domain . State . class)
678
711
public class PipeActorExample {
679
712
680
713
@Action
@@ -713,7 +746,7 @@ That is, data is saved at regular intervals asynchronously while the Actor is ac
713
746
when the Actor suffers a deactivation, when it is turned off.
714
747
715
748
These snapshots happen from time to time. And this time is configurable through the *** snapshotTimeout*** property of
716
- the *** NamedActor *** or *** UnNamedActor *** annotation.
749
+ the *** StatefulNamedActor *** or *** UnStatefulNamedActor *** annotation.
717
750
However, you can tell the Spawn runtime that you want it to persist the data immediately synchronously after executing an Action.
718
751
And this can be done in the following way:
719
752
@@ -723,10 +756,10 @@ Example:
723
756
import io.eigr.spawn.api.Value ;
724
757
import io.eigr.spawn.api.actors.ActorContext ;
725
758
import io.eigr.spawn.api.actors.annotations.Action ;
726
- import io.eigr.spawn.api.actors.annotations.NamedActor ;
759
+ import io.eigr.spawn.api.actors.annotations.stateful.StatefulNamedActor ;
727
760
import io.eigr.spawn.java.demo.domain.Domain ;
728
761
729
- @NamedActor (name = " joe" , stateful = true , stateType = Domain . JoeState . class)
762
+ @StatefulNamedActor (name = " joe" , stateType = Domain . JoeState . class)
730
763
public final class Joe {
731
764
@Action (inputType = Domain . Request . class)
732
765
public Value setLanguage (Domain .Request msg , ActorContext<Domain . JoeState > context ) {
@@ -743,8 +776,8 @@ public final class Joe {
743
776
744
777
The most important thing in this example is the use of the last parameter with the true value:
745
778
746
- ``` Java
747
- . state(updateState(" java" ), true )
779
+ ```
780
+ state(updateState("java"), true)
748
781
```
749
782
750
783
It is this parameter that will indicate to the Spawn runtime that you want the data to be saved immediately after this
@@ -781,7 +814,7 @@ ActorRef joeActor = spawnSystem.createActorRef("spawn-system", "joe");
781
814
.setLanguage(" erlang" )
782
815
.build();
783
816
Domain . Reply reply =
784
- (Domain . Reply ) joeActor. invoke(" setLanguage" , msg, Domain . Reply . class, Optional . empty() );
817
+ (Domain . Reply ) joeActor. invoke(" setLanguage" , msg, Domain . Reply . class);
785
818
```
786
819
787
820
More detailed in complete main class:
@@ -813,7 +846,7 @@ public class App {
813
846
.setLanguage(" erlang" )
814
847
.build();
815
848
Domain . Reply reply =
816
- (Domain . Reply ) joeActor. invoke(" setLanguage" , msg, Domain . Reply . class, Optional . empty() );
849
+ (Domain . Reply ) joeActor. invoke(" setLanguage" , msg, Domain . Reply . class);
817
850
}
818
851
}
819
852
```
@@ -830,7 +863,7 @@ name at runtime:
830
863
package io.eigr.spawn.java.demo ;
831
864
// omitted imports for brevity...
832
865
833
- @UnNamedActor (name = " abs_actor" , stateful = true , stateType = Domain . State . class)
866
+ @UnStatefulNamedActor (name = " abs_actor" , stateful = true , stateType = Domain . State . class)
834
867
public class AbstractActor {
835
868
@Action (inputType = Domain . Request . class)
836
869
public Value setLanguage (Domain .Request msg , ActorContext<Domain . State > context ) {
@@ -859,7 +892,7 @@ ActorRef mike = spawnSystem.createActorRef("spawn-system", "mike", "abs_actor");
859
892
.setLanguage(" erlang" )
860
893
.build();
861
894
Domain . Reply reply =
862
- (Domain . Reply ) mike. invoke(" setLanguage" , msg, Domain . Reply . class, Optional . empty() );
895
+ (Domain . Reply ) mike. invoke(" setLanguage" , msg, Domain . Reply . class);
863
896
```
864
897
865
898
The important part of the code above is the following snippet:
@@ -879,11 +912,10 @@ or asynchronously, where the callee doesn't care about the return value of the c
879
912
In this context we should not confuse Spawn's asynchronous way with Java's concept of async like Promises because async for Spawn is
880
913
just a fire-and-forget call.
881
914
882
- Therefore, to call an actor's function asynchronously, just inform the parameter async using the InvocationOpts class with the value true :
915
+ Therefore, to call an actor's function asynchronously, just use the invokeAsync method :
883
916
884
917
``` Java
885
- InvocationOpts opts = InvocationOpts . builder(). async(true ). build();
886
- mike. invoke(" setLanguage" , msg, Domain . Reply . class, opts);
918
+ mike. invokeAsync(" setLanguage" , msg, Domain . Reply . class);
887
919
```
888
920
889
921
## Deploy
0 commit comments