diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml
index 7c2f53ca0b..f33585c60a 100644
--- a/.github/workflows/validate.yml
+++ b/.github/workflows/validate.yml
@@ -150,3 +150,7 @@ jobs:
working-directory: ./examples
run: |
mm.py ./src/main/java/io/dapr/examples/unittesting/README.md
+ - name: Validate Configuration API example
+ working-directory: ./examples
+ run: |
+ mm.py ./src/main/java/io/dapr/examples/configuration/grpc/README.md
\ No newline at end of file
diff --git a/.java_header b/.java_header
new file mode 100644
index 0000000000..255da1d1a5
--- /dev/null
+++ b/.java_header
@@ -0,0 +1,12 @@
+^/\*$
+^ \* Copyright \d\d\d\d The Dapr Authors$
+^ \* Licensed under the Apache License, Version 2.0 \(the "License"\)\;$
+^ \* you may not use this file except in compliance with the License\.$
+^ \* You may obtain a copy of the License at$
+^ \* http://www.apache.org/licenses/LICENSE-2\.0$
+^ \* Unless required by applicable law or agreed to in writing, software$
+^ \* distributed under the License is distributed on an "AS IS" BASIS,$
+^ \* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied\.$
+^ \* See the License for the specific language governing permissions and$
+^limitations under the License\.$
+^\*/$
\ No newline at end of file
diff --git a/checkstyle.xml b/checkstyle.xml
index 2b2eab09f3..6482adacaf 100644
--- a/checkstyle.xml
+++ b/checkstyle.xml
@@ -51,10 +51,9 @@
-
+
-
-
+
diff --git a/examples/components/configuration/redis_configstore.yaml b/examples/components/configuration/redis_configstore.yaml
new file mode 100644
index 0000000000..5b0e4090da
--- /dev/null
+++ b/examples/components/configuration/redis_configstore.yaml
@@ -0,0 +1,12 @@
+apiVersion: dapr.io/v1alpha1
+kind: Component
+metadata:
+ name: configstore
+spec:
+ type: configuration.redis
+ version: v1
+ metadata:
+ - name: redisHost
+ value: localhost:6379
+ - name: redisPassword
+ value: ""
diff --git a/examples/src/main/java/io/dapr/examples/configuration/grpc/ConfigurationClient.java b/examples/src/main/java/io/dapr/examples/configuration/grpc/ConfigurationClient.java
new file mode 100644
index 0000000000..f586999bff
--- /dev/null
+++ b/examples/src/main/java/io/dapr/examples/configuration/grpc/ConfigurationClient.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2022 The Dapr Authors
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package io.dapr.examples.configuration.grpc;
+
+import io.dapr.client.DaprClientBuilder;
+import io.dapr.client.DaprPreviewClient;
+import io.dapr.client.domain.ConfigurationItem;
+import io.dapr.client.domain.GetConfigurationRequest;
+import io.dapr.client.domain.SubscribeConfigurationRequest;
+import reactor.core.Disposable;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+public class ConfigurationClient {
+
+ private static final String CONFIG_STORE_NAME = "configstore";
+
+ private static final List keys = new ArrayList<>(Arrays.asList("myconfig1", "myconfig3", "myconfig2"));
+
+ /**
+ * Executes various methods to check the different apis.
+ * @param args arguments
+ * @throws Exception throws Exception
+ */
+ public static void main(String[] args) throws Exception {
+ try (DaprPreviewClient client = (new DaprClientBuilder()).buildPreviewClient()) {
+ System.out.println("Using preview client...");
+ getConfigurationForaSingleKey(client);
+ getConfigurationsUsingVarargs(client);
+ getConfigurations(client);
+ subscribeConfigurationRequestWithSubscribe(client);
+ }
+ }
+
+ /**
+ * Gets configuration for a single key.
+ *
+ * @param client DaprPreviewClient object
+ */
+ public static void getConfigurationForaSingleKey(DaprPreviewClient client) {
+ System.out.println("*******trying to retrieve configuration given a single key********");
+ try {
+ Mono item = client.getConfiguration(CONFIG_STORE_NAME, keys.get(0));
+ System.out.println("Value ->" + item.block().getValue() + " key ->" + item.block().getKey());
+ } catch (Exception ex) {
+ System.out.println(ex.getMessage());
+ }
+ }
+
+ /**
+ * Gets configurations for varibale no. of arguments.
+ *
+ * @param client DaprPreviewClient object
+ */
+ public static void getConfigurationsUsingVarargs(DaprPreviewClient client) {
+ System.out.println("*******trying to retrieve configurations for a variable no. of keys********");
+ try {
+ Mono> items =
+ client.getConfiguration(CONFIG_STORE_NAME, "myconfig1", "myconfig3");
+ items.block().forEach(ConfigurationClient::print);
+ } catch (Exception ex) {
+ System.out.println(ex.getMessage());
+ }
+ }
+
+ /**
+ * Gets configurations for a list of keys.
+ *
+ * @param client DaprPreviewClient object
+ */
+ public static void getConfigurations(DaprPreviewClient client) {
+ System.out.println("*******trying to retrieve configurations for a list of keys********");
+ List keys = new ArrayList<>();
+ keys.add("myconfig1");
+ keys.add("myconfig2");
+ keys.add("myconfig3");
+ GetConfigurationRequest req = new GetConfigurationRequest(CONFIG_STORE_NAME, keys);
+ try {
+ Mono> items = client.getConfiguration(req);
+ items.block().forEach(ConfigurationClient::print);
+ } catch (Exception ex) {
+ System.out.println(ex.getMessage());
+ }
+ }
+
+ /**
+ * Subscribe to a list of keys.Optional to above iterator way of retrieving the changes
+ *
+ * @param client DaprPreviewClient object
+ */
+ public static void subscribeConfigurationRequestWithSubscribe(DaprPreviewClient client) {
+ System.out.println("*****Subscribing to keys using subscribe method: " + keys.toString() + " *****");
+ AtomicReference disposableAtomicReference = new AtomicReference<>();
+ SubscribeConfigurationRequest req = new SubscribeConfigurationRequest(CONFIG_STORE_NAME, keys);
+ Runnable subscribeTask = () -> {
+ Flux> outFlux = client.subscribeToConfiguration(req);
+ disposableAtomicReference.set(outFlux
+ .subscribe(
+ cis -> cis.forEach(ConfigurationClient::print)
+ ));
+ };
+ new Thread(subscribeTask).start();
+ try {
+ // To ensure that subscribeThread gets scheduled
+ Thread.sleep(0);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ Runnable updateKeys = () -> {
+ int i = 1;
+ while (i <= 3) {
+ executeDockerCommand(i);
+ i++;
+ }
+ };
+ new Thread(updateKeys).start();
+ try {
+ // To ensure main thread does not die before outFlux subscribe gets called
+ Thread.sleep(10000);
+ disposableAtomicReference.get().dispose();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private static void print(ConfigurationItem item) {
+ System.out.println(item.getValue() + " : key ->" + item.getKey());
+ }
+
+ private static void executeDockerCommand(int postfix) {
+ String[] command = new String[] {
+ "docker", "exec", "dapr_redis", "redis-cli",
+ "SET",
+ "myconfig" + postfix, "update_myconfigvalue" + postfix + "||2"
+ };
+ ProcessBuilder processBuilder = new ProcessBuilder(command);
+ Process process = null;
+ try {
+ process = processBuilder.start();
+ process.waitFor();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/examples/src/main/java/io/dapr/examples/configuration/grpc/README.md b/examples/src/main/java/io/dapr/examples/configuration/grpc/README.md
new file mode 100644
index 0000000000..b784912042
--- /dev/null
+++ b/examples/src/main/java/io/dapr/examples/configuration/grpc/README.md
@@ -0,0 +1,113 @@
+## Retrieve Configurations via Configuration API
+
+This example provides the different capabilities provided by Dapr Java SDK for Configuration. For further information about Configuration APIs please refer to [this link](https://docs.dapr.io/developing-applications/building-blocks/configuration/)
+**This API is available in Preview Mode**.
+
+### Using the ConfigurationAPI
+
+The java SDK exposes several methods for this -
+* `client.getConfiguration(...)` for getting a configuration for a single/multiple keys.
+* `client.subscribeToConfigurations(...)` for subscribing to a list of keys for any change.
+
+## Pre-requisites
+
+* [Dapr and Dapr Cli](https://docs.dapr.io/getting-started/install-dapr/).
+* Java JDK 11 (or greater): [Oracle JDK](https://www.oracle.com/technetwork/java/javase/downloads/index.html#JDK11) or [OpenJDK](https://jdk.java.net/13/).
+* [Apache Maven](https://maven.apache.org/install.html) version 3.x.
+
+### Checking out the code
+
+Clone this repository:
+
+```sh
+git clone https://github.com/dapr/java-sdk.git
+cd java-sdk
+```
+
+Then build the Maven project:
+
+```sh
+# make sure you are in the `java-sdk` directory.
+mvn install
+```
+## Store few dummy configurations in configurationstore
+
+
+```bash
+docker exec dapr_redis redis-cli MSET myconfig1 "val1||1" myconfig2 "val2||1" myconfig3 "val3||1"
+```
+
+
+### Running the example
+
+Get into the examples' directory:
+```sh
+cd examples
+```
+
+Use the following command to run this example-
+
+
+
+```bash
+dapr run --components-path ./components/configuration --app-id configgrpc --log-level debug -- java -jar target/dapr-java-sdk-examples-exec.jar io.dapr.examples.configuration.grpc.ConfigurationClient
+```
+
+
+
+### Sample output
+```
+== APP == Using preview client...
+== APP == *******trying to retrieve configuration given a single key********
+== APP == Value ->val1 key ->myconfig1
+== APP == *******trying to retrieve configurations for a variable no. of keys********
+== APP == val1 : key ->myconfig1
+== APP == val3 : key ->myconfig3
+== APP == *******trying to retrieve configurations for a list of keys********
+== APP == val1 : key ->myconfig1
+== APP == val2 : key ->myconfig2
+== APP == val3 : key ->myconfig3
+== APP == *****Subscribing to keys using subscribe method: [myconfig1, myconfig3, myconfig2] *****
+== APP == update_myconfigvalue1 : key ->myconfig1
+== APP == update_myconfigvalue2 : key ->myconfig2
+== APP == update_myconfigvalue3 : key ->myconfig3
+
+```
+### Cleanup
+
+To stop the app, run (or press CTRL+C):
+
+
+
+```bash
+dapr stop --app-id configgrpc
+```
+
+
+
diff --git a/pom.xml b/pom.xml
index 7911a520e7..f48c596346 100644
--- a/pom.xml
+++ b/pom.xml
@@ -16,7 +16,7 @@
UTF-8
1.39.0
3.13.0
- https://raw.githubusercontent.com/dapr/dapr/v1.4.0-rc.6/dapr/proto
+ https://raw.githubusercontent.com/dapr/dapr/v1.5.1/dapr/proto
1.6.2
3.1.1
1.8
diff --git a/sdk-tests/components/redisconfigstore.yaml b/sdk-tests/components/redisconfigstore.yaml
new file mode 100644
index 0000000000..cd4a025c17
--- /dev/null
+++ b/sdk-tests/components/redisconfigstore.yaml
@@ -0,0 +1,12 @@
+apiVersion: dapr.io/v1alpha1
+kind: Component
+metadata:
+ name: redisconfigstore
+spec:
+ type: configuration.redis
+ version: v1
+ metadata:
+ - name: redisHost
+ value: localhost:6379
+ - name: redisPassword
+ value: ""
diff --git a/sdk-tests/src/test/java/io/dapr/it/configuration/grpc/ConfigurationClientIT.java b/sdk-tests/src/test/java/io/dapr/it/configuration/grpc/ConfigurationClientIT.java
new file mode 100644
index 0000000000..7a6b39565d
--- /dev/null
+++ b/sdk-tests/src/test/java/io/dapr/it/configuration/grpc/ConfigurationClientIT.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2021 The Dapr Authors
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package io.dapr.it.configuration.grpc;
+
+import io.dapr.client.DaprClientBuilder;
+import io.dapr.client.DaprPreviewClient;
+import io.dapr.client.domain.ConfigurationItem;
+import io.dapr.it.BaseIT;
+import io.dapr.it.DaprRun;
+
+import org.junit.*;
+import reactor.core.Disposable;
+import reactor.core.publisher.Flux;
+
+import java.io.IOException;
+import java.util.*;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.junit.Assert.*;
+
+public class ConfigurationClientIT extends BaseIT {
+
+ private static final String CONFIG_STORE_NAME = "redisconfigstore";
+
+ private static DaprRun daprRun;
+
+ private static DaprPreviewClient daprPreviewClient;
+
+ private static String key = "myconfig1";
+
+ private static List keys = new ArrayList<>(Arrays.asList("myconfig1", "myconfig2", "myconfig3"));
+
+ private static String[] insertCmd = new String[] {
+ "docker", "exec", "dapr_redis", "redis-cli",
+ "MSET",
+ "myconfigkey1", "myconfigvalue1||1",
+ "myconfigkey2", "myconfigvalue2||1",
+ "myconfigkey3", "myconfigvalue3||1"
+ };
+
+ private static String[] updateCmd = new String[] {
+ "docker", "exec", "dapr_redis", "redis-cli",
+ "MSET",
+ "myconfigkey1", "update_myconfigvalue1||2",
+ "myconfigkey2", "update_myconfigvalue2||2",
+ "myconfigkey3", "update_myconfigvalue3||2"
+ };
+
+ @BeforeClass
+ public static void init() throws Exception {
+ daprRun = startDaprApp(ConfigurationClientIT.class.getSimpleName(), 5000);
+ daprRun.switchToGRPC();
+ daprPreviewClient = new DaprClientBuilder().buildPreviewClient();
+ }
+
+ @AfterClass
+ public static void tearDown() throws Exception {
+ daprPreviewClient.close();
+ }
+
+ @Before
+ public void setupConfigStore() {
+ executeDockerCommand(insertCmd);
+ }
+
+ @Test
+ public void getConfiguration() {
+ ConfigurationItem ci = daprPreviewClient.getConfiguration(CONFIG_STORE_NAME, "myconfigkey1").block();
+ assertEquals(ci.getKey(), "myconfigkey1");
+ assertEquals(ci.getValue(), "myconfigvalue1");
+ }
+
+ @Test
+ public void getConfigurationWithEmptyKey() {
+ assertThrows(IllegalArgumentException.class, () -> {
+ daprPreviewClient.getConfiguration(CONFIG_STORE_NAME, "").block();
+ });
+ }
+
+ @Test
+ public void getConfigurations() {
+ List cis = daprPreviewClient.getConfiguration(CONFIG_STORE_NAME, "myconfigkey1", "myconfigkey2").block();
+ assertTrue(cis.size() == 2);
+ assertEquals(cis.get(0).getKey(), "myconfigkey1");
+ assertEquals(cis.get(1).getValue(), "myconfigvalue2");
+ }
+
+ @Test
+ public void getConfigurationsWithEmptyList() {
+ List listOfKeys = new ArrayList<>();
+ Map metadata = new HashMap<>();
+ assertThrows(IllegalArgumentException.class, () -> {
+ daprPreviewClient.getConfiguration(CONFIG_STORE_NAME, listOfKeys, metadata).block();
+ });
+ }
+
+ @Test
+ public void subscribeToConfiguration() {
+ List updatedValues = new ArrayList<>();
+ AtomicReference disposable = new AtomicReference<>();
+ Runnable subscribeTask = () -> {
+ Flux> outFlux = daprPreviewClient
+ .subscribeToConfiguration(CONFIG_STORE_NAME, "myconfigkey1", "myconfigkey2");
+ disposable.set(outFlux.subscribe(update -> {
+ updatedValues.add(update.get(0).getValue());
+ }));
+ };
+ Thread subscribeThread = new Thread(subscribeTask);
+ subscribeThread.start();
+ try {
+ // To ensure that subscribeThread gets scheduled
+ Thread.sleep(0);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ Runnable updateKeys = () -> {
+ executeDockerCommand(updateCmd);
+ };
+ new Thread(updateKeys).start();
+ try {
+ // To ensure main thread does not die before outFlux subscribe gets called
+ Thread.sleep(3000);
+ disposable.get().dispose();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ assertEquals(updatedValues.size(), 2);
+ assertTrue(updatedValues.contains("update_myconfigvalue1"));
+ assertTrue(updatedValues.contains("update_myconfigvalue2"));
+ assertFalse(updatedValues.contains("update_myconfigvalue3"));
+ }
+
+ private static void executeDockerCommand(String[] command) {
+ ProcessBuilder processBuilder = new ProcessBuilder(command);
+ Process process = null;
+ try {
+ process = processBuilder.start();
+ process.waitFor();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/sdk/src/main/java/io/dapr/client/AbstractDaprClient.java b/sdk/src/main/java/io/dapr/client/AbstractDaprClient.java
index b8cf2af42d..cfc4b47337 100644
--- a/sdk/src/main/java/io/dapr/client/AbstractDaprClient.java
+++ b/sdk/src/main/java/io/dapr/client/AbstractDaprClient.java
@@ -13,37 +13,33 @@
package io.dapr.client;
+import io.dapr.client.domain.ConfigurationItem;
import io.dapr.client.domain.DeleteStateRequest;
-import io.dapr.client.domain.DeleteStateRequestBuilder;
import io.dapr.client.domain.ExecuteStateTransactionRequest;
-import io.dapr.client.domain.ExecuteStateTransactionRequestBuilder;
import io.dapr.client.domain.GetBulkSecretRequest;
-import io.dapr.client.domain.GetBulkSecretRequestBuilder;
import io.dapr.client.domain.GetBulkStateRequest;
-import io.dapr.client.domain.GetBulkStateRequestBuilder;
+import io.dapr.client.domain.GetConfigurationRequest;
import io.dapr.client.domain.GetSecretRequest;
-import io.dapr.client.domain.GetSecretRequestBuilder;
import io.dapr.client.domain.GetStateRequest;
-import io.dapr.client.domain.GetStateRequestBuilder;
import io.dapr.client.domain.HttpExtension;
import io.dapr.client.domain.InvokeBindingRequest;
-import io.dapr.client.domain.InvokeBindingRequestBuilder;
import io.dapr.client.domain.InvokeMethodRequest;
-import io.dapr.client.domain.InvokeMethodRequestBuilder;
import io.dapr.client.domain.PublishEventRequest;
-import io.dapr.client.domain.PublishEventRequestBuilder;
import io.dapr.client.domain.SaveStateRequest;
-import io.dapr.client.domain.SaveStateRequestBuilder;
import io.dapr.client.domain.State;
import io.dapr.client.domain.StateOptions;
+import io.dapr.client.domain.SubscribeConfigurationRequest;
import io.dapr.client.domain.TransactionalStateOperation;
import io.dapr.serializer.DaprObjectSerializer;
import io.dapr.utils.TypeRef;
+import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.stream.Collectors;
/**
* Abstract class with convenient methods common between client implementations.
@@ -52,7 +48,7 @@
* @see io.dapr.client.DaprClientGrpc
* @see io.dapr.client.DaprClientHttp
*/
-abstract class AbstractDaprClient implements DaprClient {
+abstract class AbstractDaprClient implements DaprClient, DaprPreviewClient {
/**
* A utility class for serialize and deserialize the transient objects.
@@ -416,4 +412,72 @@ public Mono