diff --git a/spring-cloud-app-broker-core/src/main/java/org/springframework/cloud/appbroker/state/InMemoryServiceInstanceBindingStateRepository.java b/spring-cloud-app-broker-core/src/main/java/org/springframework/cloud/appbroker/state/InMemoryServiceInstanceBindingStateRepository.java
index 67f7eaf..38e806d 100644
--- a/spring-cloud-app-broker-core/src/main/java/org/springframework/cloud/appbroker/state/InMemoryServiceInstanceBindingStateRepository.java
+++ b/spring-cloud-app-broker-core/src/main/java/org/springframework/cloud/appbroker/state/InMemoryServiceInstanceBindingStateRepository.java
@@ -26,6 +26,12 @@ import reactor.core.publisher.Mono;
import org.springframework.cloud.servicebroker.model.instance.OperationState;
+/**
+ * Default implementation of {@link ServiceInstanceBindingStateRepository} meant for demonstration and testing purposes
+ * only.
+ *
+ * WARNING: This implementation is not intended for production applications!
+ */
public class InMemoryServiceInstanceBindingStateRepository implements ServiceInstanceBindingStateRepository {
private final Map states = new ConcurrentSkipListMap<>();
diff --git a/spring-cloud-app-broker-core/src/main/java/org/springframework/cloud/appbroker/state/InMemoryServiceInstanceStateRepository.java b/spring-cloud-app-broker-core/src/main/java/org/springframework/cloud/appbroker/state/InMemoryServiceInstanceStateRepository.java
index 3bdf0f1..f43504e 100644
--- a/spring-cloud-app-broker-core/src/main/java/org/springframework/cloud/appbroker/state/InMemoryServiceInstanceStateRepository.java
+++ b/spring-cloud-app-broker-core/src/main/java/org/springframework/cloud/appbroker/state/InMemoryServiceInstanceStateRepository.java
@@ -25,6 +25,11 @@ import reactor.core.publisher.Mono;
import org.springframework.cloud.servicebroker.model.instance.OperationState;
+/**
+ * Default implementation of {@link ServiceInstanceStateRepository} meant for demonstration and testing purposes only.
+ *
+ * WARNING: This implementation is not intended for production applications!
+ */
public class InMemoryServiceInstanceStateRepository implements ServiceInstanceStateRepository {
private final Map states = new ConcurrentSkipListMap<>();
diff --git a/spring-cloud-app-broker-docs/build.gradle b/spring-cloud-app-broker-docs/build.gradle
index ef8ce97..d2efd67 100644
--- a/spring-cloud-app-broker-docs/build.gradle
+++ b/spring-cloud-app-broker-docs/build.gradle
@@ -20,10 +20,6 @@ plugins {
description = "Spring Cloud App Broker Documentation"
-repositories {
- mavenCentral()
-}
-
configurations {
docs
}
@@ -42,6 +38,9 @@ dependencies {
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.boot:spring-boot-starter-tomcat")
implementation("io.projectreactor:reactor-core")
+ implementation("org.springframework.boot.experimental:spring-boot-starter-r2dbc:0.1.0.M3")
+ implementation("org.springframework.boot.experimental:spring-boot-starter-data-r2dbc:0.1.0.M3")
+ implementation("io.r2dbc:r2dbc-h2:0.8.0.RELEASE")
docs("io.spring.docresources:spring-doc-resources:0.1.0.RELEASE@zip")
}
diff --git a/spring-cloud-app-broker-docs/src/docs/asciidoc/index.adoc b/spring-cloud-app-broker-docs/src/docs/asciidoc/index.adoc
index 15879d6..f0fda99 100644
--- a/spring-cloud-app-broker-docs/src/docs/asciidoc/index.adoc
+++ b/spring-cloud-app-broker-docs/src/docs/asciidoc/index.adoc
@@ -6,6 +6,7 @@
:toclevels: 3
:sectlinks:
+:examples-dir: ../../src/test/java/com/example/appbroker/
:sapbr: https://cloud.spring.io/spring-cloud-app-broker/
:sapbr-href: {sapbr}[Spring Cloud App Broker]
:sapbr-api: https://docs.spring.io/spring-cloud-app-broker/docs/{project-version}/api/
diff --git a/spring-cloud-app-broker-docs/src/docs/asciidoc/service-bindings.adoc b/spring-cloud-app-broker-docs/src/docs/asciidoc/service-bindings.adoc
index b8ddfb4..233d0cb 100644
--- a/spring-cloud-app-broker-docs/src/docs/asciidoc/service-bindings.adoc
+++ b/spring-cloud-app-broker-docs/src/docs/asciidoc/service-bindings.adoc
@@ -1,3 +1,4 @@
+:examples-dir: ../../src/test/java/com/example/appbroker/
[[service-bindings]]
== Service Bindings
@@ -12,3 +13,32 @@ The service broker application can implement the {sapbr-api}/org/springframework
=== Deleting a Service Binding
The service broker application can implement the {sapbr-api}/org/springframework/cloud/appbroker/service/DeleteServiceInstanceBindingWorkflow.html[`DeleteServiceInstanceBindingWorkflow`] interface. Alternatively, the service broker application can implement the `ServiceInstanceBindingService` interface provided by Spring Cloud Open Service Broker. See {scosb-docs}/#service-bindings[Service Bindings] in the {scosb-docs}/[Spring Cloud Open Service Broker documentation].
+
+=== Persisting Service Instance Binding State
+
+Spring Cloud App Broker provides the {sapbr-api}/org/springframework/cloud/appbroker/state/ServiceInstanceBindingStateRepository.html[`ServiceInstanceBindingStateRepository`] interface for persisting service instance binding state. The default implementation is {sapbr-api}/org/springframework/cloud/appbroker/state/InMemoryServiceInstanceBindingStateRepository.html[`InMemoryServiceInstanceBindingStateRepository`], which uses an in memory `Map` to save state and offers an easy getting started experience. In order to use a proper database for persisting state, implement `ServiceInstanceBindingStateRepository` in your application.
+
+WARNING: The `InMemoryServiceInstanceBindingStateRepository` is provided for demonstration and testing purposes only. It is not suitable for production applications!
+
+==== Example Implementation
+
+The following example shows a service instance binding state repository implementation:
+
+[source,java,%autofit]
+----
+include::{examples-dir}/ExampleServiceInstanceBindingStateRepository.java[]
+----
+
+One option for persisting service instance binding state is to use a Spring Data `CrudRepository`. The following example shows a `ReactiveCrudRepository` implementation:
+
+[source,java,%autofit]
+----
+include::{examples-dir}/ServiceInstanceBindingStateCrudRepository.java[]
+----
+
+A model object is necessary for persisting data with a `CrudRepository`. The following example shows a `ServiceInstanceBinding` model:
+
+[source,java,%autofit]
+----
+include::{examples-dir}/ServiceInstanceBinding.java[]
+----
diff --git a/spring-cloud-app-broker-docs/src/docs/asciidoc/service-instances.adoc b/spring-cloud-app-broker-docs/src/docs/asciidoc/service-instances.adoc
index 2376985..0734ef0 100644
--- a/spring-cloud-app-broker-docs/src/docs/asciidoc/service-instances.adoc
+++ b/spring-cloud-app-broker-docs/src/docs/asciidoc/service-instances.adoc
@@ -1,3 +1,4 @@
+:examples-dir: ../../src/test/java/com/example/appbroker/
[[service-instances]]
== Service Instances
@@ -340,3 +341,32 @@ CAUTION: Modifying certain properties, such as disk and memory, when updating an
=== Deleting a Service Instance
Spring Cloud App Broker provides the {sapbr-api}/org/springframework/cloud/appbroker/workflow/instance/AppDeploymentDeleteServiceInstanceWorkflow.html[`AppDeploymentDeleteServiceInstanceWorkflow`] workflow, which handles deleting the configured backing applications and services as illustrated in the previous sections. The service broker application can implement the {sapbr-api}/org/springframework/cloud/appbroker/service/DeleteServiceInstanceWorkflow.html[`DeleteServiceInstanceWorkflow`] interface to further modify the deployment. Multiple workflows may be annotated with `@Order` so as to process the workflows in a specific order. Alternatively, the service broker application can implement the `ServiceInstanceService` interface provided by Spring Cloud Open Service Broker. See {scosb-docs}/#service-instances[Service Instances] in the {scosb-docs}/[Spring Cloud Open Service Broker documentation].
+
+=== Persisting Service Instance State
+
+Spring Cloud App Broker provides the {sapbr-api}/org/springframework/cloud/appbroker/state/ServiceInstanceStateRepository.html[`ServiceInstanceStateRepository`] interface for persisting service instance state. The default implementation is {sapbr-api}/org/springframework/cloud/appbroker/state/InMemoryServiceInstanceStateRepository.html[`InMemoryServiceInstanceStateRepository`], which uses an in memory `Map` to save state and offers an easy getting started experience. In order to use a proper database for persisting state, implement `ServiceInstanceStateRepository` in your application.
+
+WARNING: The `InMemoryServiceInstanceStateRepository` is provided for demonstration and testing purposes only. It is not suitable for production applications!
+
+==== Example Implementation
+
+The following example shows a service instance state repository implementation:
+
+[source,java,%autofit]
+----
+include::{examples-dir}/ExampleServiceInstanceStateRepository.java[]
+----
+
+One option for persisting service instance state is to use a Spring Data `CrudRepository`. The following example shows a `ReactiveCrudRepository` implementation:
+
+[source,java,%autofit]
+----
+include::{examples-dir}/ServiceInstanceStateCrudRepository.java[]
+----
+
+A model object is necessary for persisting data with a `CrudRepository`. The following example shows a `ServiceInstance` model:
+
+[source,java,%autofit]
+----
+include::{examples-dir}/ServiceInstance.java[]
+----
diff --git a/spring-cloud-app-broker-docs/src/test/java/com/example/appbroker/ExampleServiceInstanceBindingStateRepository.java b/spring-cloud-app-broker-docs/src/test/java/com/example/appbroker/ExampleServiceInstanceBindingStateRepository.java
new file mode 100644
index 0000000..46495c2
--- /dev/null
+++ b/spring-cloud-app-broker-docs/src/test/java/com/example/appbroker/ExampleServiceInstanceBindingStateRepository.java
@@ -0,0 +1,55 @@
+package com.example.appbroker;
+
+import reactor.core.publisher.Mono;
+
+import org.springframework.cloud.appbroker.state.ServiceInstanceBindingStateRepository;
+import org.springframework.cloud.appbroker.state.ServiceInstanceState;
+import org.springframework.cloud.servicebroker.model.instance.OperationState;
+
+class ExampleServiceInstanceBindingStateRepository implements ServiceInstanceBindingStateRepository {
+
+ private final ServiceInstanceBindingStateCrudRepository serviceInstanceBindingStateCrudRepository;
+
+ ExampleServiceInstanceBindingStateRepository(
+ ServiceInstanceBindingStateCrudRepository serviceInstanceBindingStateCrudRepository) {
+ this.serviceInstanceBindingStateCrudRepository = serviceInstanceBindingStateCrudRepository;
+ }
+
+ @Override
+ public Mono saveState(String serviceInstanceId, String bindingId, OperationState state,
+ String description) {
+ return serviceInstanceBindingStateCrudRepository
+ .findByServiceInstanceIdAndBindingId(serviceInstanceId, bindingId)
+ .switchIfEmpty(Mono.just(new ServiceInstanceBinding()))
+ .flatMap(binding -> {
+ binding.setServiceInstanceId(serviceInstanceId);
+ binding.setBindingId(bindingId);
+ binding.setOperationState(state);
+ binding.setDescription(description);
+ return Mono.just(binding);
+ })
+ .flatMap(serviceInstanceBindingStateCrudRepository::save)
+ .map(ExampleServiceInstanceBindingStateRepository::toServiceInstanceState);
+ }
+
+ @Override
+ public Mono getState(String serviceInstanceId, String bindingId) {
+ return serviceInstanceBindingStateCrudRepository
+ .findByServiceInstanceIdAndBindingId(serviceInstanceId, bindingId)
+ .switchIfEmpty(Mono.error(new IllegalArgumentException(
+ "Unknown binding: serviceInstanceId=" + serviceInstanceId + ", bindingId=" + bindingId)))
+ .map(ExampleServiceInstanceBindingStateRepository::toServiceInstanceState);
+ }
+
+ @Override
+ public Mono removeState(String serviceInstanceId, String bindingId) {
+ return getState(serviceInstanceId, bindingId)
+ .doOnNext(serviceInstanceState -> serviceInstanceBindingStateCrudRepository
+ .deleteByServiceInstanceIdAndBindingId(serviceInstanceId, bindingId));
+ }
+
+ private static ServiceInstanceState toServiceInstanceState(ServiceInstanceBinding binding) {
+ return new ServiceInstanceState(binding.getOperationState(), binding.getDescription(), null);
+ }
+
+}
diff --git a/spring-cloud-app-broker-docs/src/test/java/com/example/appbroker/ExampleServiceInstanceStateRepository.java b/spring-cloud-app-broker-docs/src/test/java/com/example/appbroker/ExampleServiceInstanceStateRepository.java
new file mode 100644
index 0000000..6f45577
--- /dev/null
+++ b/spring-cloud-app-broker-docs/src/test/java/com/example/appbroker/ExampleServiceInstanceStateRepository.java
@@ -0,0 +1,48 @@
+package com.example.appbroker;
+
+import reactor.core.publisher.Mono;
+
+import org.springframework.cloud.appbroker.state.ServiceInstanceState;
+import org.springframework.cloud.appbroker.state.ServiceInstanceStateRepository;
+import org.springframework.cloud.servicebroker.model.instance.OperationState;
+
+class ExampleServiceInstanceStateRepository implements ServiceInstanceStateRepository {
+
+ private final ServiceInstanceStateCrudRepository serviceInstanceStateCrudRepository;
+
+ ExampleServiceInstanceStateRepository(ServiceInstanceStateCrudRepository serviceInstanceStateCrudRepository) {
+ this.serviceInstanceStateCrudRepository = serviceInstanceStateCrudRepository;
+ }
+
+ @Override
+ public Mono saveState(String serviceInstanceId, OperationState state, String description) {
+ return serviceInstanceStateCrudRepository.findByServiceInstanceId(serviceInstanceId)
+ .switchIfEmpty(Mono.just(new ServiceInstance()))
+ .flatMap(serviceInstance -> {
+ serviceInstance.setServiceInstanceId(serviceInstanceId);
+ serviceInstance.setOperationState(state);
+ serviceInstance.setDescription(description);
+ return Mono.just(serviceInstance);
+ })
+ .flatMap(serviceInstanceStateCrudRepository::save)
+ .map(ExampleServiceInstanceStateRepository::toServiceInstanceState);
+ }
+
+ @Override
+ public Mono getState(String serviceInstanceId) {
+ return serviceInstanceStateCrudRepository.findByServiceInstanceId(serviceInstanceId)
+ .switchIfEmpty(Mono.error(new IllegalArgumentException("Unknown service instance ID " + serviceInstanceId)))
+ .map(ExampleServiceInstanceStateRepository::toServiceInstanceState);
+ }
+
+ @Override
+ public Mono removeState(String serviceInstanceId) {
+ return getState(serviceInstanceId)
+ .doOnNext(serviceInstanceState -> serviceInstanceStateCrudRepository.deleteByServiceInstanceId(serviceInstanceId));
+ }
+
+ private static ServiceInstanceState toServiceInstanceState(ServiceInstance serviceInstance) {
+ return new ServiceInstanceState(serviceInstance.getOperationState(), serviceInstance.getDescription(), null);
+ }
+
+}
diff --git a/spring-cloud-app-broker-docs/src/test/java/com/example/appbroker/ServiceInstance.java b/spring-cloud-app-broker-docs/src/test/java/com/example/appbroker/ServiceInstance.java
new file mode 100644
index 0000000..46fa381
--- /dev/null
+++ b/spring-cloud-app-broker-docs/src/test/java/com/example/appbroker/ServiceInstance.java
@@ -0,0 +1,59 @@
+package com.example.appbroker;
+
+import org.springframework.cloud.servicebroker.model.instance.OperationState;
+import org.springframework.data.annotation.Id;
+
+class ServiceInstance {
+
+ @Id
+ private Long id;
+
+ private String serviceInstanceId;
+
+ private String description;
+
+ private OperationState operationState;
+
+ public ServiceInstance() {
+
+ }
+
+ public ServiceInstance(String serviceInstanceId, String description, OperationState operationState) {
+ this.serviceInstanceId = serviceInstanceId;
+ this.description = description;
+ this.operationState = operationState;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getServiceInstanceId() {
+ return serviceInstanceId;
+ }
+
+ public void setServiceInstanceId(String serviceInstanceId) {
+ this.serviceInstanceId = serviceInstanceId;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public OperationState getOperationState() {
+ return operationState;
+ }
+
+ public void setOperationState(OperationState operationState) {
+ this.operationState = operationState;
+ }
+
+}
diff --git a/spring-cloud-app-broker-docs/src/test/java/com/example/appbroker/ServiceInstanceBinding.java b/spring-cloud-app-broker-docs/src/test/java/com/example/appbroker/ServiceInstanceBinding.java
new file mode 100644
index 0000000..1dc0aeb
--- /dev/null
+++ b/spring-cloud-app-broker-docs/src/test/java/com/example/appbroker/ServiceInstanceBinding.java
@@ -0,0 +1,71 @@
+package com.example.appbroker;
+
+import org.springframework.cloud.servicebroker.model.instance.OperationState;
+import org.springframework.data.annotation.Id;
+
+class ServiceInstanceBinding {
+
+ @Id
+ private Long id;
+
+ private String bindingId;
+
+ private String serviceInstanceId;
+
+ private String description;
+
+ private OperationState operationState;
+
+ public ServiceInstanceBinding() {
+
+ }
+
+ public ServiceInstanceBinding(String bindingId, String serviceInstanceId, String description,
+ OperationState operationState) {
+ this.bindingId = bindingId;
+ this.serviceInstanceId = serviceInstanceId;
+ this.description = description;
+ this.operationState = operationState;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getBindingId() {
+ return bindingId;
+ }
+
+ public void setBindingId(String bindingId) {
+ this.bindingId = bindingId;
+ }
+
+ public String getServiceInstanceId() {
+ return serviceInstanceId;
+ }
+
+ public void setServiceInstanceId(String serviceInstanceId) {
+ this.serviceInstanceId = serviceInstanceId;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public OperationState getOperationState() {
+ return operationState;
+ }
+
+ public void setOperationState(OperationState operationState) {
+ this.operationState = operationState;
+ }
+
+}
diff --git a/spring-cloud-app-broker-docs/src/test/java/com/example/appbroker/ServiceInstanceBindingStateCrudRepository.java b/spring-cloud-app-broker-docs/src/test/java/com/example/appbroker/ServiceInstanceBindingStateCrudRepository.java
new file mode 100644
index 0000000..36e02e2
--- /dev/null
+++ b/spring-cloud-app-broker-docs/src/test/java/com/example/appbroker/ServiceInstanceBindingStateCrudRepository.java
@@ -0,0 +1,26 @@
+package com.example.appbroker;
+
+import reactor.core.publisher.Mono;
+
+import org.springframework.data.r2dbc.repository.Query;
+import org.springframework.data.repository.query.Param;
+import org.springframework.data.repository.reactive.ReactiveCrudRepository;
+
+interface ServiceInstanceBindingStateCrudRepository extends ReactiveCrudRepository {
+
+ @Query("select * from service_instance_binding " +
+ "where service_instance_id = :service_instance_id " +
+ "and binding_id = :binding_id")
+ Mono findByServiceInstanceIdAndBindingId(
+ @Param("service_instance_id") String serviceInstanceId,
+ @Param("binding_id") String bindingId);
+
+
+ @Query("delete from service_instance_binding " +
+ "where service_instance_id = :service_instance_id " +
+ "and binding_id = :binding_id")
+ Mono deleteByServiceInstanceIdAndBindingId(
+ @Param("service_instance_id") String serviceInstanceId,
+ @Param("binding_id") String bindingId);
+
+}
diff --git a/spring-cloud-app-broker-docs/src/test/java/com/example/appbroker/ServiceInstanceStateCrudRepository.java b/spring-cloud-app-broker-docs/src/test/java/com/example/appbroker/ServiceInstanceStateCrudRepository.java
new file mode 100644
index 0000000..c1cc4a5
--- /dev/null
+++ b/spring-cloud-app-broker-docs/src/test/java/com/example/appbroker/ServiceInstanceStateCrudRepository.java
@@ -0,0 +1,17 @@
+package com.example.appbroker;
+
+import reactor.core.publisher.Mono;
+
+import org.springframework.data.r2dbc.repository.Query;
+import org.springframework.data.repository.query.Param;
+import org.springframework.data.repository.reactive.ReactiveCrudRepository;
+
+interface ServiceInstanceStateCrudRepository extends ReactiveCrudRepository {
+
+ @Query("select * from service_instance where service_instance_id = :service_instance_id")
+ Mono findByServiceInstanceId(@Param("service_instance_id") String serviceInstanceId);
+
+ @Query("delete from service_instance where service_instance_id = :service_instance_id")
+ Mono deleteByServiceInstanceId(@Param("service_instance_id") String serviceInstanceId);
+
+}