Skip to content

Commit 71f377e

Browse files
committed
Feat. Added simple dependency injector auxiliary class
1 parent fb0dc80 commit 71f377e

File tree

13 files changed

+207
-12
lines changed

13 files changed

+207
-12
lines changed

README.md

Lines changed: 102 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ JVM User Language Support for [Spawn](https://github.com/eigr/spawn).
66
1. [Overview](#overview)
77
2. [Getting Started](#getting-started)
88
3. [Advanced Use Cases](#advanced-use-cases)
9+
- [Dependency Injection](#dependency-injection)
910
- [Types of Actors](#types-of-actors)
1011
- [Stateless Actors](#stateless-actors)
1112
- [Considerations about Spawn actors](#considerations-about-spawn-actors)
@@ -14,16 +15,16 @@ JVM User Language Support for [Spawn](https://github.com/eigr/spawn).
1415
- [Forward](#forward)
1516
- [Pipe](#pipe)
1617
- [State Management](#state-management)
17-
4. [Using Actors](#using-actors)
18+
5. [Using Actors](#using-actors)
1819
- [Call Named Actors](#call-named-actors)
1920
- [Call Unnamed Actors](#call-unnamed-actors)
2021
- [Async](#async)
2122
- [Timeouts](#timeouts)
22-
5. [Deploy](#deploy)
23+
6. [Deploy](#deploy)
2324
- [Defining an ActorSystem](#defining-an-actorsystem)
2425
- [Defining an ActorHost](#defining-an-actorhost)
2526
- [Activators](#activators)
26-
6. [Actor Model](#actor-model)
27+
7. [Actor Model](#actor-model)
2728
- [Virtual Actors](#virtual-actors)
2829

2930

@@ -93,7 +94,7 @@ The second thing we have to do is add the spawn dependency to the project.
9394
<dependency>
9495
<groupId>com.github.eigr</groupId>
9596
<artifactId>spawn-java-std-sdk</artifactId>
96-
<version>v0.9.1</version>
97+
<version>v1.0.0</version>
9798
</dependency>
9899
```
99100
We're also going to configure a few things for our application build to work, including compiling the protobuf files.
@@ -127,7 +128,7 @@ See below a full example of the pom.xml file:
127128
<dependency>
128129
<groupId>com.github.eigr</groupId>
129130
<artifactId>spawn-java-std-sdk</artifactId>
130-
<version>v0.9.1</version>
131+
<version>v1.0.0</version>
131132
</dependency>
132133
<dependency>
133134
<groupId>ch.qos.logback</groupId>
@@ -518,6 +519,102 @@ Spawn Actors abstract a huge amount of developer infrastructure and can be used
518519
In the sections below we will demonstrate some features available in Spawn that contribute to the development of
519520
complex applications in a simplified way.
520521

522+
### Dependency Injection
523+
524+
Sometimes we need to pass many arguments as dependencies to the Actor class.
525+
In this case, it is more convenient to use your own dependency injection mechanism.
526+
However, the Spawn SDK already comes with an auxiliary class to make this easier for the developer.
527+
Let's look at an example:
528+
529+
1. First let's take a look at some example dependency classes:
530+
531+
```java
532+
// We will have an interface that represents any type of service.
533+
public interface MessageService {
534+
String getDefaultMessage();
535+
}
536+
537+
// and concrete implementation here
538+
public class MessageServiceImpl implements MessageService {
539+
@Override
540+
public String getDefaultMessage() {
541+
return "Hello Spawn in English";
542+
}
543+
}
544+
```
545+
546+
2. Second, let's define an actor so that it receives an instance of the DependencyInjector class through the class constructor:
547+
548+
```java
549+
package io.eigr.spawn.test.actors;
550+
551+
import io.eigr.spawn.api.actors.ActorContext;
552+
import io.eigr.spawn.api.actors.Value;
553+
import io.eigr.spawn.api.actors.annotations.Action;
554+
import io.eigr.spawn.api.actors.annotations.stateful.StatefulNamedActor;
555+
import io.eigr.spawn.api.extensions.DependencyInjector;
556+
import io.eigr.spawn.java.test.domain.Actor;
557+
558+
@StatefulNamedActor(name = "test_actor_constructor", stateType = Actor.State.class)
559+
public final class ActorWithDependencies {
560+
561+
private final MessageService messageService;
562+
563+
public ActorWithConstructor(DependencyInjector injector) {
564+
// Note how to use dependency injection here to get a concrete class of MessageService.
565+
this.defaultMessage = injector.getInstance(MessageService.class);
566+
}
567+
568+
@Action(inputType = Actor.Request.class)
569+
public Value setLanguage(Actor.Request msg, ActorContext<Actor.State> context) {
570+
if (context.getState().isPresent()) {
571+
}
572+
573+
return Value.at()
574+
.response(Actor.Reply.newBuilder()
575+
.setResponse(messageService.getDefaultMessage())
576+
.build())
577+
.state(updateState("java"))
578+
.reply();
579+
}
580+
581+
private Actor.State updateState(String language) {
582+
return Actor.State.newBuilder()
583+
.addLanguages(language)
584+
.build();
585+
}
586+
}
587+
588+
```
589+
590+
3. Then you can pass your dependent classes this way to your Actor:
591+
```java
592+
package io.eigr.spawn.java.demo;
593+
594+
import io.eigr.spawn.api.Spawn;
595+
import io.eigr.spawn.api.extensions.DependencyInjector;
596+
import io.eigr.spawn.api.extensions.SimpleDependencyInjector;
597+
598+
public class App {
599+
public static void main(String[] args) {
600+
DependencyInjector injector = SimpleDependencyInjector.createInjector();
601+
injector.bind(MessageService.class, new MessageServiceImpl());
602+
603+
Spawn spawnSystem = new Spawn.SpawnSystem()
604+
.create("spawn-system")
605+
.withActor(Joe.class, actorConstructorArgs, injector -> new Joe((DependencyInjector) injector))
606+
.build();
607+
608+
spawnSystem.start();
609+
}
610+
}
611+
```
612+
613+
It is important to note that this helper mechanism does not currently implement any type of complex dependency graph.
614+
Therefore, it will not build objects based on complex dependencies nor take care of the object lifecycle for you.
615+
In other words, all instances added through the bind method of the SimpleDependencyInjector class will be singletons.
616+
This mechanism works much more like a bucket of objects that will be forwarded via your actor's constructor.
617+
521618
### Types of Actors
522619

523620
First we need to understand how the various types of actors available in Spawn behave. Spawn defines the following types of Actors:

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<groupId>io.eigr.spawn</groupId>
55
<artifactId>spawn-java-std-sdk</artifactId>
66
<packaging>jar</packaging>
7-
<version>0.9.0</version>
7+
<version>1.0.0</version>
88
<name>spawn-java-std-sdk</name>
99
<url>http://maven.apache.org</url>
1010

src/main/java/io/eigr/spawn/api/ActorIdentity.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public final class ActorIdentity {
1414
private ActorIdentity(String system, String name, String parent, boolean lookup){
1515
this.system = system;
1616
this.name = name;
17-
this.maybeParent = Optional.ofNullable(parent);
17+
this.maybeParent = Optional.of(parent);
1818
this.lookup = lookup;
1919
}
2020

@@ -26,7 +26,7 @@ private ActorIdentity(String system, String name, boolean lookup){
2626
}
2727

2828
public static ActorIdentity of(String system, String name) {
29-
return new ActorIdentity(system, name, true);
29+
return new ActorIdentity(system, name, false);
3030
}
3131

3232
public static ActorIdentity of(String system, String name, String parent) {
@@ -58,6 +58,7 @@ public Optional<String> getMaybeParent() {
5858
}
5959

6060
public boolean isParent() {
61+
System.out.println(String.format("Actor %s is parent? = %s", this.name, this.maybeParent.isPresent()));
6162
return this.maybeParent.isPresent();
6263
}
6364

src/main/java/io/eigr/spawn/api/ActorRef.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,12 @@ protected static ActorRef of(SpawnClient client, Cache<ActorOuterClass.ActorId,
4343
ActorOuterClass.ActorId actorId;
4444

4545
if (identity.isParent()) {
46+
System.out.println("Passou como parent");
4647
actorId = buildActorId(identity.getSystem(), identity.getName(), identity.getParent());
4748

4849
spawnActor(actorId, client);
4950
} else {
51+
System.out.println("Passou como normal");
5052
actorId = buildActorId(identity.getSystem(), identity.getName());
5153
}
5254

@@ -56,6 +58,7 @@ protected static ActorRef of(SpawnClient client, Cache<ActorOuterClass.ActorId,
5658
}
5759

5860
if (identity.hasLookup()) {
61+
System.out.println("Faca lookup");
5962
spawnActor(actorId, client);
6063
}
6164

src/main/java/io/eigr/spawn/api/actors/ActorContext.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
package io.eigr.spawn.api.actors;
22

33
import io.eigr.spawn.api.Spawn;
4+
import io.eigr.spawn.api.extensions.DependencyInjector;
45

56
import java.util.Optional;
67
import java.util.StringJoiner;
78

89
public final class ActorContext<S extends Object> {
910
private Spawn spawn;
10-
1111
private Optional<S> state;
1212

13+
private DependencyInjector injector;
14+
1315
public ActorContext(Spawn spawn){
1416
this.spawn = spawn;
1517
this.state = Optional.empty();
@@ -28,6 +30,7 @@ public Optional<S> getState() {
2830
return state;
2931
}
3032

33+
3134
@Override
3235
public String toString() {
3336
return new StringJoiner(", ", ActorContext.class.getSimpleName() + "[", "]")
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package io.eigr.spawn.api.extensions;
2+
3+
public interface DependencyInjector {
4+
5+
<T extends Object> void bind(Class<T> type, Object instance);
6+
7+
<T extends Object> T getInstance(Class<T> type);
8+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package io.eigr.spawn.api.extensions;
2+
3+
import java.util.HashMap;
4+
import java.util.Map;
5+
6+
public final class SimpleDependencyInjector implements DependencyInjector {
7+
8+
private final Map<Class<?>, Object> bucket;
9+
10+
private SimpleDependencyInjector() {
11+
this.bucket = new HashMap<>();
12+
}
13+
14+
private static class SimpleDependencyInjectorHelper{
15+
private static final SimpleDependencyInjector INSTANCE = new SimpleDependencyInjector();
16+
}
17+
18+
public static DependencyInjector createInjector(){
19+
return SimpleDependencyInjectorHelper.INSTANCE;
20+
}
21+
22+
@Override
23+
public <T> void bind(Class<T> type, Object instance) {
24+
if (this.bucket.containsKey(type)) {
25+
throw new IllegalArgumentException("There is already an instance associated with this type in the bucket");
26+
}
27+
28+
this.bucket.put(type, instance);
29+
}
30+
31+
@Override
32+
public <T> T getInstance(Class<T> type) {
33+
return type.cast(this.bucket.get(type));
34+
}
35+
}

src/main/java/io/eigr/spawn/internal/transport/server/ActorServiceHandler.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import io.eigr.spawn.api.actors.ActorFactory;
1515
import io.eigr.spawn.api.actors.workflows.SideEffect;
1616
import io.eigr.spawn.api.exceptions.ActorInvocationException;
17+
import io.eigr.spawn.api.extensions.SimpleDependencyInjector;
1718
import io.eigr.spawn.internal.Entity;
1819
import org.slf4j.Logger;
1920
import org.slf4j.LoggerFactory;

src/test/java/io/eigr/spawn/SpawnTest.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import io.eigr.spawn.api.TransportOpts;
77
import io.eigr.spawn.api.exceptions.ActorCreationException;
88
import io.eigr.spawn.api.exceptions.ActorInvocationException;
9+
import io.eigr.spawn.api.extensions.DependencyInjector;
10+
import io.eigr.spawn.api.extensions.SimpleDependencyInjector;
911
import io.eigr.spawn.java.test.domain.Actor;
1012
import io.eigr.spawn.test.actors.ActorWithConstructor;
1113
import io.eigr.spawn.test.actors.JoeActor;
@@ -23,10 +25,13 @@ public class SpawnTest {
2325

2426
@Before
2527
public void before() throws Exception {
28+
DependencyInjector injector = SimpleDependencyInjector.createInjector();
29+
injector.bind(String.class, "Hello with Constructor");
30+
2631
spawnSystem = new Spawn.SpawnSystem()
2732
.create("spawn-system")
2833
.withActor(JoeActor.class)
29-
.withActor(ActorWithConstructor.class, "Hello with Constructor", arg -> new ActorWithConstructor((String) arg))
34+
.withActor(ActorWithConstructor.class, injector, arg -> new ActorWithConstructor((DependencyInjector) arg))
3035
.withTransportOptions(
3136
TransportOpts.builder()
3237
.port(8091)

src/test/java/io/eigr/spawn/test/actors/ActorWithConstructor.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,16 @@
44
import io.eigr.spawn.api.actors.Value;
55
import io.eigr.spawn.api.actors.annotations.Action;
66
import io.eigr.spawn.api.actors.annotations.stateful.StatefulNamedActor;
7+
import io.eigr.spawn.api.extensions.DependencyInjector;
78
import io.eigr.spawn.java.test.domain.Actor;
89

910
@StatefulNamedActor(name = "test_actor_constructor", stateType = Actor.State.class)
1011
public final class ActorWithConstructor {
12+
1113
private final String defaultMessage;
1214

13-
public ActorWithConstructor(String defaultMessage) {
14-
this.defaultMessage = defaultMessage;
15+
public ActorWithConstructor(DependencyInjector injector) {
16+
this.defaultMessage = injector.getInstance(String.class);
1517
}
1618

1719
@Action(inputType = Actor.Request.class)

0 commit comments

Comments
 (0)