diff --git a/examples/pom.xml b/examples/pom.xml
index 400b9e1df..94dec827c 100644
--- a/examples/pom.xml
+++ b/examples/pom.xml
@@ -7,38 +7,29 @@
io.dapr
dapr-sdk-parent
- 0.2.0-preview01
+ 0.2.0
+ io.dapr
dapr-sdk-examples
jar
- 0.2.0-preview01
+ 0.2.0
dapr-sdk-examples
${project.build.directory}/generated-sources
- ${project.parent.basedir}/proto
+ ${project.basedir}/proto
11
${java.version}
${java.version}
-
- io.undertow
- undertow-servlet
- 2.0.26.Final
-
commons-cli
commons-cli
1.4
-
- commons-io
- commons-io
- 2.6
-
org.json
json
@@ -47,18 +38,27 @@
io.grpc
grpc-protobuf
+ ${grpc.version}
io.grpc
grpc-stub
+ ${grpc.version}
+
+
+ io.grpc
+ grpc-api
+ ${grpc.version}
javax.annotation
javax.annotation-api
+ 1.3.2
io.grpc
grpc-testing
+ ${grpc.version}
test
@@ -71,10 +71,25 @@
protoc-jar-maven-plugin
3.10.1
+
+ org.springframework.boot
+ spring-boot-starter-web
+ 2.2.2.RELEASE
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure
+ 2.2.2.RELEASE
+
+
+ io.dapr
+ dapr-sdk-springboot
+ 0.2.0
+
io.dapr
dapr-sdk
- 0.2.0-preview01
+ 0.2.0
@@ -96,7 +111,7 @@
direct
true
- ${protobuf.input.directory}/examples
+ ${protobuf.input.directory}
diff --git a/proto/examples/helloworld.proto b/examples/proto/helloworld.proto
similarity index 100%
rename from proto/examples/helloworld.proto
rename to examples/proto/helloworld.proto
diff --git a/examples/src/main/java/io/dapr/examples/actors/http/DemoActorService.java b/examples/src/main/java/io/dapr/examples/actors/http/DemoActorService.java
index a5d39240c..83299d40f 100644
--- a/examples/src/main/java/io/dapr/examples/actors/http/DemoActorService.java
+++ b/examples/src/main/java/io/dapr/examples/actors/http/DemoActorService.java
@@ -5,29 +5,14 @@
package io.dapr.examples.actors.http;
-import com.fasterxml.jackson.core.JsonFactory;
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
import io.dapr.actors.runtime.ActorRuntime;
-import io.undertow.Undertow;
-import io.undertow.server.HttpHandler;
-import io.undertow.server.HttpServerExchange;
-import io.undertow.server.RoutingHandler;
-import io.undertow.util.Headers;
+import io.dapr.springboot.DaprApplication;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.Options;
-import org.apache.commons.io.IOUtils;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.StringWriter;
-import java.io.Writer;
-import java.nio.charset.StandardCharsets;
-import java.util.Deque;
-import java.util.Map;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Service for Actor runtime.
@@ -36,193 +21,26 @@ import java.util.Map;
* 2. Run the server:
* dapr run --app-id demoactorservice --app-port 3000 --port 3005 -- mvn exec:java -pl=examples -Dexec.mainClass=io.dapr.examples.actors.http.DemoActorService -Dexec.args="-p 3000"
*/
+@SpringBootApplication
public class DemoActorService {
- private static final JsonFactory JSON_FACTORY = new JsonFactory();
-
- private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
-
- private static final HttpHandler ROUTES = new RoutingHandler()
- .get("/", DemoActorService::handleDaprConfig)
- .get("/dapr/config", DemoActorService::handleDaprConfig)
- .post("/actors/{actorType}/{id}", DemoActorService::handleActorActivate)
- .delete("/actors/{actorType}/{id}", DemoActorService::handleActorDeactivate)
- .put("/actors/{actorType}/{id}/method/{methodName}", DemoActorService::handleActorInvoke)
- .put("/actors/{actorType}/{id}/method/timer/{timerName}", DemoActorService::handleActorTimer)
- .put("/actors/{actorType}/{id}/method/remind/{reminderName}", DemoActorService::handleActorReminder);
-
- private final int port;
-
- private final Undertow server;
-
- private DemoActorService(int port) {
- this.port = port;
- this.server = Undertow
- .builder()
- .addHttpListener(port, "localhost")
- .setHandler(ROUTES)
- .build();
- ActorRuntime.getInstance().registerActor(DemoActorImpl.class);
- }
-
- private void start() {
- // Now we handle ctrl+c (or any other JVM shutdown)
- Runtime.getRuntime().addShutdownHook(new Thread() {
-
- @Override
- public void run() {
- System.out.println("Server: shutting down gracefully ...");
- DemoActorService.this.server.stop();
- System.out.println("Server: Bye.");
- }
- });
-
- System.out.println(String.format("Server: listening on port %d ...", this.port));
- this.server.start();
- }
-
- private static void handleDaprConfig(HttpServerExchange exchange) throws IOException {
- exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "application/json");
- String result = "";
- try (Writer writer = new StringWriter()) {
- JsonGenerator generator = JSON_FACTORY.createGenerator(writer);
- generator.writeStartObject();
- generator.writeArrayFieldStart("entities");
- for(String actorClass : ActorRuntime.getInstance().getRegisteredActorTypes()) {
- generator.writeString(actorClass);
- }
- generator.writeEndArray();
- generator.writeStringField("actorIdleTimeout", "10s");
- generator.writeStringField("actorScanInterval", "1s");
- generator.writeStringField("drainOngoingCallTimeout", "1s");
- generator.writeBooleanField("drainBalancedActors", true);
- generator.writeEndObject();
- generator.close();
- writer.flush();
- result = writer.toString();
- }
-
- exchange.getResponseSender().send(result);
- }
-
- private static void handleActorActivate(HttpServerExchange exchange) {
- if (exchange.isInIoThread()) {
- exchange.dispatch(DemoActorService::handleActorActivate);
- return;
- }
-
- String actorType = findParamValueOrNull(exchange, "actorType");
- String actorId = findParamValueOrNull(exchange, "id");
- ActorRuntime.getInstance().activate(actorType, actorId).block();
- exchange.getResponseSender().send("");
- }
-
- private static void handleActorDeactivate(HttpServerExchange exchange) {
- if (exchange.isInIoThread()) {
- exchange.dispatch(DemoActorService::handleActorDeactivate);
- return;
- }
-
- String actorType = findParamValueOrNull(exchange, "actorType");
- String actorId = findParamValueOrNull(exchange, "id");
- ActorRuntime.getInstance().deactivate(actorType, actorId).block();
- }
-
- private static void handleActorInvoke(HttpServerExchange exchange) throws IOException {
- if (exchange.isInIoThread()) {
- exchange.dispatch(DemoActorService::handleActorInvoke);
- return;
- }
-
- String actorType = findParamValueOrNull(exchange, "actorType");
- String actorId = findParamValueOrNull(exchange, "id");
- String methodName = findParamValueOrNull(exchange, "methodName");
- exchange.startBlocking();
- String data = findMethodData(exchange.getInputStream());
- String result = ActorRuntime.getInstance().invoke(actorType, actorId, methodName, data).block();
- exchange.getResponseSender().send(buildResponse(result));
- }
-
- private static void handleActorTimer(HttpServerExchange exchange) throws IOException {
- if (exchange.isInIoThread()) {
- exchange.dispatch(DemoActorService::handleActorTimer);
- return;
- }
-
- String actorType = findParamValueOrNull(exchange, "actorType");
- String actorId = findParamValueOrNull(exchange, "id");
- String timerName = findParamValueOrNull(exchange, "timerName");
- ActorRuntime.getInstance().invokeTimer(actorType, actorId, timerName).block();
- exchange.getResponseSender().send("");
- }
-
- private static void handleActorReminder(HttpServerExchange exchange) throws IOException {
- if (exchange.isInIoThread()) {
- exchange.dispatch(DemoActorService::handleActorReminder);
- return;
- }
-
- String actorType = findParamValueOrNull(exchange, "actorType");
- String actorId = findParamValueOrNull(exchange, "id");
- String reminderName = findParamValueOrNull(exchange, "reminderName");
- exchange.startBlocking();
- String params = IOUtils.toString(exchange.getInputStream(), StandardCharsets.UTF_8);
- ActorRuntime.getInstance().invokeReminder(actorType, actorId, reminderName, params).block();
- exchange.getResponseSender().send("");
- }
-
- private static String findParamValueOrNull(HttpServerExchange exchange, String name) {
- Map> params = exchange.getQueryParameters();
- if (params == null) {
- return null;
- }
-
- Deque values = params.get(name);
- if ((values == null) || (values.isEmpty())) {
- return null;
- }
-
- return values.getFirst();
- }
-
- private static String findMethodData(InputStream stream) throws IOException {
- JsonNode root = OBJECT_MAPPER.readTree(stream);
- if (root == null) {
- return null;
- }
-
- JsonNode dataNode = root.get("data");
- if (dataNode == null) {
- return null;
- }
-
- return new String(dataNode.binaryValue(), StandardCharsets.UTF_8);
- }
-
- private static String buildResponse(String data) throws IOException {
- try (Writer writer = new StringWriter()) {
- JsonGenerator generator = JSON_FACTORY.createGenerator(writer);
- generator.writeStartObject();
- if (data != null) {
- generator.writeBinaryField("data", data.getBytes());
- }
- generator.writeEndObject();
- generator.close();
- writer.flush();
- return writer.toString();
- }
- }
-
public static void main(String[] args) throws Exception {
Options options = new Options();
- options.addRequiredOption("p", "port", true, "Port to listen to.");
+ options.addRequiredOption("p", "port", true, "Port Dapr will listen to.");
CommandLineParser parser = new DefaultParser();
CommandLine cmd = parser.parse(options, args);
// If port string is not valid, it will throw an exception.
int port = Integer.parseInt(cmd.getOptionValue("port"));
- final DemoActorService service = new DemoActorService(port);
- service.start();
+
+ // Register the Actor class.
+ ActorRuntime.getInstance().registerActor(DemoActorImpl.class);
+
+ // Start Dapr's callback endpoint.
+ DaprApplication.start(port);
+
+ // Start application's endpoint.
+ SpringApplication.run(DemoActorService.class);
}
}
diff --git a/examples/src/main/java/io/dapr/examples/actors/http/HelloController.java b/examples/src/main/java/io/dapr/examples/actors/http/HelloController.java
new file mode 100644
index 000000000..37eeec522
--- /dev/null
+++ b/examples/src/main/java/io/dapr/examples/actors/http/HelloController.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) Microsoft Corporation.
+ * Licensed under the MIT License.
+ */
+
+package io.dapr.examples.actors.http;
+
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+public class HelloController {
+
+ @RequestMapping("/")
+ public String index() {
+ return "Greetings from your Spring Boot Application!";
+ }
+
+}
diff --git a/pom.xml b/pom.xml
index aae62bcc6..ee5be6777 100644
--- a/pom.xml
+++ b/pom.xml
@@ -7,7 +7,7 @@
io.dapr
dapr-sdk-parent
pom
- 0.2.0-preview01
+ 0.2.0
dapr-sdk-parent
SDK for Dapr.
https://dapr.io
@@ -33,6 +33,11 @@
pom
import
+
+ io.grpc
+ grpc-api
+ ${grpc.version}
+
javax.annotation
javax.annotation-api
@@ -79,6 +84,7 @@
sdk-autogen
sdk
+ sdk-springboot
examples
diff --git a/sdk-autogen/pom.xml b/sdk-autogen/pom.xml
index 399a36cb4..f60417d9c 100644
--- a/sdk-autogen/pom.xml
+++ b/sdk-autogen/pom.xml
@@ -7,12 +7,12 @@
io.dapr
dapr-sdk-parent
- 0.2.0-preview01
+ 0.2.0
dapr-sdk-autogen
jar
- 0.2.0-preview01
+ 0.2.0
dapr-sdk-autogen
Auto-generated SDK for Dapr
diff --git a/sdk-springboot/pom.xml b/sdk-springboot/pom.xml
new file mode 100644
index 000000000..ed1ae238c
--- /dev/null
+++ b/sdk-springboot/pom.xml
@@ -0,0 +1,64 @@
+
+ 4.0.0
+
+
+ io.dapr
+ dapr-sdk-parent
+ 0.2.0
+
+
+ io.dapr
+ dapr-sdk-springboot
+ jar
+ 0.2.0
+ dapr-sdk-springboot
+
+
+ 8
+ ${java.version}
+ ${java.version}
+
+
+
+
+ javax.annotation
+ javax.annotation-api
+ 1.3.2
+
+
+ org.springframework.boot
+ spring-boot
+ 2.2.2.RELEASE
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ 2.2.2.RELEASE
+
+
+ io.dapr
+ dapr-sdk
+ 0.2.0
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+ 8
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
diff --git a/sdk-springboot/src/main/java/io/dapr/springboot/DaprApplication.java b/sdk-springboot/src/main/java/io/dapr/springboot/DaprApplication.java
new file mode 100644
index 000000000..b8dd5fa27
--- /dev/null
+++ b/sdk-springboot/src/main/java/io/dapr/springboot/DaprApplication.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) Microsoft Corporation.
+ * Licensed under the MIT License.
+ */
+
+package io.dapr.springboot;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+import java.util.Properties;
+
+/**
+ * Dapr's callback implementation via SpringBoot.
+ */
+@SpringBootApplication
+public class DaprApplication {
+
+ /**
+ * Starts Dapr's callback in a given port.
+ * @param port Port to listen to.
+ */
+ public static void start(int port) {
+ SpringApplication app = new SpringApplication(DaprApplication.class);
+ Properties properties = new Properties();
+ properties.setProperty("server.port", Integer.toString(port));
+ app.setDefaultProperties(properties);
+ app.run();
+ }
+
+ /**
+ * Main for SpringBoot requirements.
+ * @param args Command line arguments.
+ */
+ public static void main(String[] args) {
+ SpringApplication.run(DaprApplication.class, args);
+ }
+
+}
diff --git a/sdk-springboot/src/main/java/io/dapr/springboot/DaprController.java b/sdk-springboot/src/main/java/io/dapr/springboot/DaprController.java
new file mode 100644
index 000000000..cb890b36d
--- /dev/null
+++ b/sdk-springboot/src/main/java/io/dapr/springboot/DaprController.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) Microsoft Corporation.
+ * Licensed under the MIT License.
+ */
+
+package io.dapr.springboot;
+
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.dapr.actors.runtime.ActorRuntime;
+import org.springframework.web.bind.annotation.*;
+import reactor.core.publisher.Mono;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * SpringBoot Controller to handle callback APIs for Dapr.
+ * TODO: use POJOs instead of String when possible.
+ * TODO: JavaDocs.
+ */
+@RestController
+public class DaprController {
+
+ private static final JsonFactory JSON_FACTORY = new JsonFactory();
+
+ private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+
+ @RequestMapping("/")
+ public String index() {
+ return "Greetings from Dapr!";
+ }
+
+ @RequestMapping("/dapr/config")
+ public String daprConfig() throws Exception {
+ try (Writer writer = new StringWriter()) {
+ JsonGenerator generator = JSON_FACTORY.createGenerator(writer);
+ generator.writeStartObject();
+ generator.writeArrayFieldStart("entities");
+ for (String actorClass : ActorRuntime.getInstance().getRegisteredActorTypes()) {
+ generator.writeString(actorClass);
+ }
+ generator.writeEndArray();
+ generator.writeStringField("actorIdleTimeout", "10s");
+ generator.writeStringField("actorScanInterval", "1s");
+ generator.writeStringField("drainOngoingCallTimeout", "1s");
+ generator.writeBooleanField("drainBalancedActors", true);
+ generator.writeEndObject();
+ generator.close();
+ writer.flush();
+ return writer.toString();
+ }
+ }
+
+ @RequestMapping(method = RequestMethod.POST, path = "/actors/{type}/{id}")
+ public Mono activateActor(@PathVariable("type") String type,
+ @PathVariable("id") String id) throws Exception {
+ return ActorRuntime.getInstance().activate(type, id);
+ }
+
+ @RequestMapping(method = RequestMethod.DELETE, path = "/actors/{type}/{id}")
+ public Mono deactivateActor(@PathVariable("type") String type,
+ @PathVariable("id") String id) throws Exception {
+ return ActorRuntime.getInstance().deactivate(type, id);
+ }
+
+ @RequestMapping(method = RequestMethod.PUT, path = "/actors/{type}/{id}/method/{method}")
+ public Mono invokeActorMethod(@PathVariable("type") String type,
+ @PathVariable("id") String id,
+ @PathVariable("method") String method,
+ @RequestBody(required = false) String body) {
+ try {
+ String data = findMethodData(body);
+ return ActorRuntime.getInstance().invoke(type, id, method, data).map(r -> buildResponse(r));
+ } catch (Exception e) {
+ return Mono.error(e);
+ }
+ }
+
+ @RequestMapping(method = RequestMethod.PUT, path = "/actors/{type}/{id}/method/timer/{timer}")
+ public Mono invokeActorTimer(@PathVariable("type") String type,
+ @PathVariable("id") String id,
+ @PathVariable("timer") String timer) {
+ return ActorRuntime.getInstance().invokeTimer(type, id, timer);
+ }
+
+ @RequestMapping(method = RequestMethod.PUT, path = "/actors/{type}/{id}/method/remind/{reminder}")
+ public Mono invokeActorReminder(@PathVariable("type") String type,
+ @PathVariable("id") String id,
+ @PathVariable("reminder") String reminder,
+ @RequestBody(required = false) String body) {
+ return ActorRuntime.getInstance().invokeReminder(type, id, reminder, body);
+ }
+
+ private static String findMethodData(String body) throws IOException {
+ if (body == null) {
+ return null;
+ }
+
+ JsonNode root = OBJECT_MAPPER.readTree(body);
+ if (root == null) {
+ return null;
+ }
+
+ JsonNode dataNode = root.get("data");
+ if (dataNode == null) {
+ return null;
+ }
+
+ return new String(dataNode.binaryValue(), StandardCharsets.UTF_8);
+ }
+
+ private static String buildResponse(String data) throws RuntimeException {
+ try {
+ try (Writer writer = new StringWriter()) {
+ JsonGenerator generator = JSON_FACTORY.createGenerator(writer);
+ generator.writeStartObject();
+ if (data != null) {
+ generator.writeBinaryField("data", data.getBytes());
+ }
+ generator.writeEndObject();
+ generator.close();
+ writer.flush();
+ return writer.toString();
+ }
+ } catch (IOException e) {
+ // Make Mono happy.
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/sdk/pom.xml b/sdk/pom.xml
index 650e5ba59..e036c01d0 100644
--- a/sdk/pom.xml
+++ b/sdk/pom.xml
@@ -7,12 +7,12 @@
io.dapr
dapr-sdk-parent
- 0.2.0-preview01
+ 0.2.0
dapr-sdk
jar
- 0.2.0-preview01
+ 0.2.0
dapr-sdk
SDK for Dapr
@@ -20,7 +20,7 @@
io.dapr
dapr-sdk-autogen
- 0.2.0-preview01
+ 0.2.0
com.fasterxml.jackson.core