{"id":21625400,"url":"https://github.com/eigr/spawn-springboot-sdk","last_synced_at":"2025-04-11T12:35:17.580Z","repository":{"id":59833101,"uuid":"503119360","full_name":"eigr/spawn-springboot-sdk","owner":"eigr","description":"Spawn Springboot SDK","archived":false,"fork":false,"pushed_at":"2023-08-27T14:42:27.000Z","size":189,"stargazers_count":8,"open_issues_count":1,"forks_count":2,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-04-07T18:02:17.206Z","etag":null,"topics":["actor-model","beam-vm","concurrency","erlang-distribution","serverless-framework","service-mesh","sidecar","spring-boot"],"latest_commit_sha":null,"homepage":"https://eigr.io/","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/eigr.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-06-13T21:24:09.000Z","updated_at":"2024-12-04T02:02:54.000Z","dependencies_parsed_at":"2023-02-18T19:01:30.005Z","dependency_job_id":null,"html_url":"https://github.com/eigr/spawn-springboot-sdk","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eigr%2Fspawn-springboot-sdk","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eigr%2Fspawn-springboot-sdk/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eigr%2Fspawn-springboot-sdk/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eigr%2Fspawn-springboot-sdk/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/eigr","download_url":"https://codeload.github.com/eigr/spawn-springboot-sdk/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248402325,"owners_count":21097328,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["actor-model","beam-vm","concurrency","erlang-distribution","serverless-framework","service-mesh","sidecar","spring-boot"],"created_at":"2024-11-25T01:09:05.888Z","updated_at":"2025-04-11T12:35:17.560Z","avatar_url":"https://github.com/eigr.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Spawn Springboot SDK\n\nSpawn Springboot SDK is the support library for the Spawn Actors system.\n\nSpawn is based on the sidecar proxy pattern to provide the multi-language Actor Model framework.\nSpawn's technology stack on top of BEAM VM (Erlang's virtual machine) provides support for different languages from its \nnative Actor model.\n\nFor a broader understanding of Spawn please consult its official [repository](https://github.com/eigr-labs/spawn).\n\n## Getting Started\n\nThe Spawn Springboot SDK allows you to configure a conventional Spring application to use the Spawn Actor Model. \nIn the sections below you will see how to do this.\n\nYou'll need to make sure Spawn Proxy service is up and running.\nWith `docker-compose` you can define:\n\n\u003e **_NOTE:_** _using docker is recommended for `dev purposes only`, see [spawn deploy](https://github.com/eigr/spawn#getting-started) for production examples._\n\n```yml\nversion: \"3.8\"\n\nservices:\n  spawn-proxy:\n    image: eigr/spawn-proxy:0.5.0\n    restart: always\n    environment:\n      PROXY_APP_NAME: spawn\n      PROXY_HTTP_PORT: 9001\n      PROXY_DATABASE_TYPE: mysql # postgres or others can be used\n      PROXY_DATABASE_NAME: eigr-functions-db\n      PROXY_DATABASE_USERNAME: admin \n      PROXY_DATABASE_SECRET: password\n      PROXY_DATABASE_HOST: localhost\n      PROXY_DATABASE_PORT: 5432\n      SPAWN_STATESTORE_KEY: 3Jnb0hZiHIzHTOih7t2cTEPEpY98Tu1wvQkPfq/XwqE=\n      USER_FUNCTION_HOST: 0.0.0.0 # Your Java Springboot application runtime host\n      USER_FUNCTION_PORT: 8090 # Your Java Springboot application runtime exposed port\n    ports:\n      - \"9001:9001\"\n```\n\n### Maven Setup\nFirst add the following dependency to your `pom.xml` file.\n\n```xml\n\u003cdependency\u003e\n   \u003cgroupId\u003ecom.github.eigr-labs.spawn-springboot-sdk\u003c/groupId\u003e\n   \u003cartifactId\u003espawn-springboot-starter\u003c/artifactId\u003e\n   \u003cversion\u003ev0.1.10\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\nYou should also add jitpack.io repository.\n\n```xml\n\u003crepositories\u003e\n  \u003crepository\u003e\n      \u003cid\u003ejitpack.io\u003c/id\u003e\n      \u003curl\u003ehttps://jitpack.io\u003c/url\u003e\n  \u003c/repository\u003e\n\u003c/repositories\u003e\n```\n\nYou will also need to create a container image of your application. \nYou can do this using the protobuf-maven-plugin. Let's see how the pom.xml file would look with all this configured.\n\n```xml\n\u003cproject xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd\"\u003e\n    \u003crepositories\u003e\n        \u003crepository\u003e\n            \u003cid\u003ejitpack.io\u003c/id\u003e\n            \u003curl\u003ehttps://jitpack.io\u003c/url\u003e\n        \u003c/repository\u003e\n        \u003crepository\u003e\n            \u003cid\u003espring-releases\u003c/id\u003e\n            \u003cname\u003eSpring Releases\u003c/name\u003e\n            \u003curl\u003ehttps://repo.spring.io/release\u003c/url\u003e\n            \u003csnapshots\u003e\n                \u003cenabled\u003efalse\u003c/enabled\u003e\n            \u003c/snapshots\u003e\n        \u003c/repository\u003e\n    \u003c/repositories\u003e\n    \n    \u003cpluginRepositories\u003e\n        \u003cpluginRepository\u003e\n            \u003cid\u003espring-releases\u003c/id\u003e\n            \u003cname\u003eSpring Releases\u003c/name\u003e\n            \u003curl\u003ehttps://repo.spring.io/release\u003c/url\u003e\n            \u003csnapshots\u003e\n                \u003cenabled\u003efalse\u003c/enabled\u003e\n            \u003c/snapshots\u003e\n        \u003c/pluginRepository\u003e\n    \u003c/pluginRepositories\u003e\n\n    \u003cdependencies\u003e\n        \u003cdependency\u003e\n            \u003cgroupId\u003ecom.github.eigr-labs.spawn-springboot-sdk\u003c/groupId\u003e\n            \u003cartifactId\u003espawn-springboot-starter\u003c/artifactId\u003e\n            \u003cversion\u003ev0.1.10\u003c/version\u003e\n        \u003c/dependency\u003e\n\n        \u003cdependency\u003e\n            \u003cgroupId\u003eorg.springframework.boot\u003c/groupId\u003e\n            \u003cartifactId\u003espring-boot-starter-test\u003c/artifactId\u003e\n            \u003cscope\u003etest\u003c/scope\u003e\n        \u003c/dependency\u003e\n    \u003c/dependencies\u003e\n\n    \u003cbuild\u003e\n        \u003cextensions\u003e\n            \u003cextension\u003e\n                \u003cgroupId\u003ekr.motd.maven\u003c/groupId\u003e\n                \u003cartifactId\u003eos-maven-plugin\u003c/artifactId\u003e\n                \u003cversion\u003e1.6.2\u003c/version\u003e\n            \u003c/extension\u003e\n        \u003c/extensions\u003e\n\n        \u003cplugins\u003e\n            \u003cplugin\u003e\n                \u003cgroupId\u003eorg.springframework.boot\u003c/groupId\u003e\n                \u003cartifactId\u003espring-boot-maven-plugin\u003c/artifactId\u003e\n            \u003c/plugin\u003e\n\n            \u003cplugin\u003e\n                \u003cgroupId\u003eorg.xolstice.maven.plugins\u003c/groupId\u003e\n                \u003cartifactId\u003eprotobuf-maven-plugin\u003c/artifactId\u003e\n                \u003cversion\u003e0.6.1\u003c/version\u003e\n                \u003cconfiguration\u003e\n                    \u003cprotocArtifact\u003ecom.google.protobuf:protoc:3.19.2:exe:${os.detected.classifier}\u003c/protocArtifact\u003e\n                    \u003cpluginId\u003egrpc-java\u003c/pluginId\u003e\n                    \u003cpluginArtifact\u003eio.grpc:protoc-gen-grpc-java:1.47.0:exe:${os.detected.classifier}\u003c/pluginArtifact\u003e\n                \u003c/configuration\u003e\n                \u003cexecutions\u003e\n                    \u003cexecution\u003e\n                        \u003cgoals\u003e\n                            \u003cgoal\u003ecompile\u003c/goal\u003e\n                            \u003cgoal\u003ecompile-custom\u003c/goal\u003e\n                        \u003c/goals\u003e\n                    \u003c/execution\u003e\n                \u003c/executions\u003e\n            \u003c/plugin\u003e\n\n            \u003cplugin\u003e\n                \u003cgroupId\u003eorg.apache.maven.plugins\u003c/groupId\u003e\n                \u003cartifactId\u003emaven-surefire-plugin\u003c/artifactId\u003e\n                \u003cversion\u003e2.7\u003c/version\u003e\n            \u003c/plugin\u003e\n            \u003cplugin\u003e\n                \u003cartifactId\u003emaven-dependency-plugin\u003c/artifactId\u003e\n                \u003cversion\u003e2.5.1\u003c/version\u003e\n                \u003cexecutions\u003e\n                    \u003cexecution\u003e\n                        \u003cid\u003egetClasspathFilenames\u003c/id\u003e\n                        \u003cgoals\u003e\n                            \u003c!-- provides the jars of the classpath as properties inside of maven\n                                 so that we can refer to one of the jars in the exec plugin config below --\u003e\n                            \u003cgoal\u003eproperties\u003c/goal\u003e\n                        \u003c/goals\u003e\n                    \u003c/execution\u003e\n                \u003c/executions\u003e\n            \u003c/plugin\u003e\n            \u003cplugin\u003e\n                \u003cgroupId\u003eorg.apache.maven.plugins\u003c/groupId\u003e\n                \u003cartifactId\u003emaven-compiler-plugin\u003c/artifactId\u003e\n                \u003cconfiguration\u003e\n                    \u003csource\u003e1.8\u003c/source\u003e\n                    \u003ctarget\u003e1.8\u003c/target\u003e\n                \u003c/configuration\u003e\n            \u003c/plugin\u003e\n        \u003c/plugins\u003e\n    \u003c/build\u003e\n\u003c/project\u003e\n```\n\n### Defining your domain\n\nNow that your setup is configured the second step is to add your protobuf files to the project.\nFor that create a folder called ***proto*** inside ***src/main***.\n\nNow just add your protobuf files that reflect your business domain inside **src/main/proto** folder. For example:\n\n`example.proto`\n```protobuf\nsyntax = \"proto3\";\n\npackage io.eigr.spawn.example;\n\noption java_multiple_files = true;\noption java_package = \"io.eigr.spawn.example\";\noption java_outer_classname = \"ExampleProtos\";\n\nmessage MyState {\n  int32 value = 1;\n}\n\nmessage MyBusinessMessage {\n  int32 value = 1;\n}\n```\n\nIt is important to try to separate the type of message that must be stored as the actors' state from the type of messages \nthat will be exchanged between their actors' operations calls. In other words, the Actor's internal state is also represented \nas a protobuf type, and it is a good practice to separate these types of messages from the others in its business domain.\n\nIn the above case `MyState` is the type protobuf that represents the state of the Actor that we will create later \nwhile `MyBusiness` is the type of message that we will send and receive from this Actor.\n\n### Writing the Actor code\n\nNow that the protobuf types have been created we can proceed with the code. Example definition of an Actor:\n\n`Actor.java`:\n\n```java\npackage io.eigr.spawn.example;\n\nimport io.eigr.spawn.springboot.starter.ActorContext;\nimport io.eigr.spawn.springboot.starter.ActorKind;\nimport io.eigr.spawn.springboot.starter.Value;\nimport io.eigr.spawn.springboot.starter.annotations.Action;\nimport io.eigr.spawn.springboot.starter.annotations.Actor;\nimport lombok.extern.log4j.Log4j2;\n\nimport java.util.Optional;\n\n@Log4j2\n@Actor(name = \"joe\", kind = ActorKind.SINGLETON, stateType = MyState.class, snapshotTimeout = 5000, deactivatedTimeout = 10000)\npublic class JoeActor {\n   @Action\n   public Value get(ActorContext\u003cMyState\u003e context) {\n      log.info(\"Received invocation. Context: {}\", context);\n      if (context.getState().isPresent()) {\n         MyState state = context.getState().get();\n         return Value.\u003cMyState, MyBusinessMessage\u003eat().state(state)\n                 .response(MyBusinessMessage.newBuilder()\n                         .setValue(state.getValue())\n                         .build())\n                 .reply();\n      }\n      return Value.at().empty();\n   }\n\n   @Action(name = \"sum\", inputType = MyBusinessMessage.class)\n   public Value sum(MyBusinessMessage msg, ActorContext\u003cMyState\u003e context) {\n      log.info(\"Received invocation. Message: {}. Context: {}\", msg, context);\n      int value = 1;\n      if (context.getState().isPresent()) {\n         log.info(\"State is present and value is {}\", context.getState().get());\n         Optional\u003cMyState\u003e oldState = context.getState();\n         value = oldState.get().getValue() + msg.getValue();\n      } else {\n         //log.info(\"State is NOT present. Msg getValue is {}\", msg.getValue());\n         value = msg.getValue();\n      }\n\n      log.info(\"New Value is {}\", value);\n      MyBusinessMessage resultValue = MyBusinessMessage.newBuilder().setValue(value).build();\n\n      return Value.at().response(resultValue).state(updateState(value)).reply();\n   }\n\n   private MyState updateState(int value) {\n      return MyState.newBuilder().setValue(value).build();\n   }\n}\n```\n\n**Explaining the code:**\n\n1. Every Actor must contain the `@Actor` annotation so that it can be registered as both a Spring Bean and a Spawn Actor.\n\n2. Use the `@Action` annotation to tell Spawn which methods to expose as Actor actions. \n   Every command must have a name by which it can be invoked and may contain other input and output type annotations.\n\n3. An ActorContext object will always be passed to the action method via Spawn's sidecar proxy. \n   It is via ActorContext that it will be possible to access the current state of the Actor. \n   This will always be the second argument of the method unless your method is not taking any business type arguments, \n   in which case ActorContext will be passed as the first and only argument.\n   This is used when your method just needs to return some value.\n\n4. To return values and the updated state it will always be necessary to use the return type `Value`. \n   Return values can be one of three types:\n\n   * **reply**: When the intention is to send some type of data to the caller.\n   * **noReply**: When you don't want to send any return to the caller and only the Actor's state can be updated.\n   * **empty**: Similar to noReply but no action will be taken regarding the actor state.\n\n### Setup Main application\n\n`App.java`:\n\n```java\npackage io.eigr.spawn.example;\n\nimport io.eigr.spawn.springboot.starter.ActionRequest;\nimport io.eigr.spawn.springboot.starter.ActionResponse;\nimport io.eigr.spawn.springboot.starter.SpawnSystem;\nimport io.eigr.spawn.springboot.starter.autoconfigure.EnableSpawn;\n// others imports omitted\n\n@Log4j2\n@EnableSpawn // 1\n@SpringBootApplication\n@EntityScan(\"io.eigr.spawn.example\") // 2\npublic class App {\n    public static void main(String[] args) {SpringApplication.run(App.class, args);}\n\n    @Bean\n    public CommandLineRunner commandLineRunner(ApplicationContext ctx) {\n        return args -\u003e {\n            SpawnSystem actorSystem = ctx.getBean(SpawnSystem.class); // 3\n            log.info(\"Let's invoke some Actor\");\n            for (int i = 0; i \u003c 10; i++) {\n               ActionRequest actionRequest = ActionRequest.of()\n                       .actorName(actorName)\n                       .action(\"sum\")\n                       .value(MyBusinessMessage.newBuilder().setValue(i).build())\n                       .responseType(MyBusinessMessage.class)\n                       .build(); // 4\n\n                ActorResponse sumResult = actorSystem.invoke(actionRequest); // 5\n                log.info(\"Actor invoke Sum Actor Action value result: {}\", sumResult.getValue().get());\n\n               ActionRequest getRequest = ActionRequest.of(actorName, \"get\")\n                       .responseType(MyState.class)\n                       .build();\n\n                ActionResponse getResult = actorSystem.invoke(getRequest);\n                log.info(\"Actor invoke Get Actor Action value result: {}\", getResult.getValue());\n            }\n        };\n    }\n}\n```\n\n**Explaining the code:**\n\n1. Enables the Spawn Actor system\n2. Indicates in which package your actors will be searched. This is a Spring annotation and is for finding any beans your application declares.\n3. All interaction with actors takes place through the `SpawnSystem` class. SpawnSystem in turn is a normal Spring Bean and can be injected into any Spring class normally, in the above case we prefer to retrieve it through Spring's own context with ***ctx.getBean(SpawnSystem.class)***.\n4. To call your actors you will need to send the data type you defined as a protobuf via ActionRequest class.\n5. Use the invoke method to perform the actions you defined on your actors.\n\nThe complete example code can be found [here](https://github.com/eigr-labs/spawn-springboot-sdk/tree/main/spawn-springboot-examples).\n\n## **Documentation**\n\n- [Actor options](./docs/actor-options.md)\n   - [Singleton](./docs/actor-options.md#singleton-actor)\n   - [Abstract](./docs/actor-options.md#abstract-actor)\n   - [Pooled](./docs/actor-options.md#pooled-actor)\n   - [Default Actions](./docs/actor-options.md#default-actions)\n- [Actor workflows](./docs/actor-workflows.md)\n\n## **Examples**\n\nYou can check [spawn-springboot-examples folder](./spawn-springboot-examples) to see some examples\n\n## **Environment variables:**\n\n- `PROXY_HTTP_PORT` This is the port of spawn proxy service\n- `PROXY_HTTP_HOST` This is the host of spawn proxy service\n- `USER_FUNCTION_PORT` This is the port that your service will expose to communicate with Spawn\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feigr%2Fspawn-springboot-sdk","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feigr%2Fspawn-springboot-sdk","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feigr%2Fspawn-springboot-sdk/lists"}