Document example implementation of ServiceInstanceStateRepository

- Add example implementation in reference docs
- Include warning in javadoc and reference about using in mem implementation
in production

Resolves #257. Resolves #314
This commit is contained in:
Roy Clarkson
2020-01-30 20:23:52 -05:00
committed by Roy Clarkson
parent 082daa2925
commit e5802e6602
12 changed files with 351 additions and 4 deletions

View File

@@ -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.
* <p/>
* WARNING: This implementation is not intended for production applications!
*/
public class InMemoryServiceInstanceBindingStateRepository implements ServiceInstanceBindingStateRepository {
private final Map<BindingKey, ServiceInstanceState> states = new ConcurrentSkipListMap<>();

View File

@@ -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.
* <p/>
* WARNING: This implementation is not intended for production applications!
*/
public class InMemoryServiceInstanceStateRepository implements ServiceInstanceStateRepository {
private final Map<String, ServiceInstanceState> states = new ConcurrentSkipListMap<>();

View File

@@ -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")
}

View File

@@ -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/

View File

@@ -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[]
----

View File

@@ -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[]
----

View File

@@ -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<ServiceInstanceState> 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<ServiceInstanceState> 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<ServiceInstanceState> 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);
}
}

View File

@@ -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<ServiceInstanceState> 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<ServiceInstanceState> getState(String serviceInstanceId) {
return serviceInstanceStateCrudRepository.findByServiceInstanceId(serviceInstanceId)
.switchIfEmpty(Mono.error(new IllegalArgumentException("Unknown service instance ID " + serviceInstanceId)))
.map(ExampleServiceInstanceStateRepository::toServiceInstanceState);
}
@Override
public Mono<ServiceInstanceState> 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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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<ServiceInstanceBinding, Long> {
@Query("select * from service_instance_binding " +
"where service_instance_id = :service_instance_id " +
"and binding_id = :binding_id")
Mono<ServiceInstanceBinding> 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<Void> deleteByServiceInstanceIdAndBindingId(
@Param("service_instance_id") String serviceInstanceId,
@Param("binding_id") String bindingId);
}

View File

@@ -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<ServiceInstance, Long> {
@Query("select * from service_instance where service_instance_id = :service_instance_id")
Mono<ServiceInstance> findByServiceInstanceId(@Param("service_instance_id") String serviceInstanceId);
@Query("delete from service_instance where service_instance_id = :service_instance_id")
Mono<Void> deleteByServiceInstanceId(@Param("service_instance_id") String serviceInstanceId);
}