Skip to content

Commit e5802e6

Browse files
committed
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
1 parent 082daa2 commit e5802e6

File tree

12 files changed

+351
-4
lines changed

12 files changed

+351
-4
lines changed

spring-cloud-app-broker-core/src/main/java/org/springframework/cloud/appbroker/state/InMemoryServiceInstanceBindingStateRepository.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@
2626

2727
import org.springframework.cloud.servicebroker.model.instance.OperationState;
2828

29+
/**
30+
* Default implementation of {@link ServiceInstanceBindingStateRepository} meant for demonstration and testing purposes
31+
* only.
32+
* <p/>
33+
* WARNING: This implementation is not intended for production applications!
34+
*/
2935
public class InMemoryServiceInstanceBindingStateRepository implements ServiceInstanceBindingStateRepository {
3036

3137
private final Map<BindingKey, ServiceInstanceState> states = new ConcurrentSkipListMap<>();

spring-cloud-app-broker-core/src/main/java/org/springframework/cloud/appbroker/state/InMemoryServiceInstanceStateRepository.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@
2525

2626
import org.springframework.cloud.servicebroker.model.instance.OperationState;
2727

28+
/**
29+
* Default implementation of {@link ServiceInstanceStateRepository} meant for demonstration and testing purposes only.
30+
* <p/>
31+
* WARNING: This implementation is not intended for production applications!
32+
*/
2833
public class InMemoryServiceInstanceStateRepository implements ServiceInstanceStateRepository {
2934

3035
private final Map<String, ServiceInstanceState> states = new ConcurrentSkipListMap<>();

spring-cloud-app-broker-docs/build.gradle

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,6 @@ plugins {
2020

2121
description = "Spring Cloud App Broker Documentation"
2222

23-
repositories {
24-
mavenCentral()
25-
}
26-
2723
configurations {
2824
docs
2925
}
@@ -42,6 +38,9 @@ dependencies {
4238
implementation("org.springframework.boot:spring-boot-starter-security")
4339
implementation("org.springframework.boot:spring-boot-starter-tomcat")
4440
implementation("io.projectreactor:reactor-core")
41+
implementation("org.springframework.boot.experimental:spring-boot-starter-r2dbc:0.1.0.M3")
42+
implementation("org.springframework.boot.experimental:spring-boot-starter-data-r2dbc:0.1.0.M3")
43+
implementation("io.r2dbc:r2dbc-h2:0.8.0.RELEASE")
4544

4645
docs("io.spring.docresources:spring-doc-resources:0.1.0.RELEASE@zip")
4746
}

spring-cloud-app-broker-docs/src/docs/asciidoc/index.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
:toclevels: 3
77
:sectlinks:
88

9+
:examples-dir: ../../src/test/java/com/example/appbroker/
910
:sapbr: https://cloud.spring.io/spring-cloud-app-broker/
1011
:sapbr-href: {sapbr}[Spring Cloud App Broker]
1112
:sapbr-api: https://docs.spring.io/spring-cloud-app-broker/docs/{project-version}/api/

spring-cloud-app-broker-docs/src/docs/asciidoc/service-bindings.adoc

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
:examples-dir: ../../src/test/java/com/example/appbroker/
12
[[service-bindings]]
23
== Service Bindings
34

@@ -12,3 +13,32 @@ The service broker application can implement the {sapbr-api}/org/springframework
1213
=== Deleting a Service Binding
1314

1415
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].
16+
17+
=== Persisting Service Instance Binding State
18+
19+
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.
20+
21+
WARNING: The `InMemoryServiceInstanceBindingStateRepository` is provided for demonstration and testing purposes only. It is not suitable for production applications!
22+
23+
==== Example Implementation
24+
25+
The following example shows a service instance binding state repository implementation:
26+
27+
[source,java,%autofit]
28+
----
29+
include::{examples-dir}/ExampleServiceInstanceBindingStateRepository.java[]
30+
----
31+
32+
One option for persisting service instance binding state is to use a Spring Data `CrudRepository`. The following example shows a `ReactiveCrudRepository` implementation:
33+
34+
[source,java,%autofit]
35+
----
36+
include::{examples-dir}/ServiceInstanceBindingStateCrudRepository.java[]
37+
----
38+
39+
A model object is necessary for persisting data with a `CrudRepository`. The following example shows a `ServiceInstanceBinding` model:
40+
41+
[source,java,%autofit]
42+
----
43+
include::{examples-dir}/ServiceInstanceBinding.java[]
44+
----

spring-cloud-app-broker-docs/src/docs/asciidoc/service-instances.adoc

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
:examples-dir: ../../src/test/java/com/example/appbroker/
12
[[service-instances]]
23
== Service Instances
34

@@ -340,3 +341,32 @@ CAUTION: Modifying certain properties, such as disk and memory, when updating an
340341
=== Deleting a Service Instance
341342

342343
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].
344+
345+
=== Persisting Service Instance State
346+
347+
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.
348+
349+
WARNING: The `InMemoryServiceInstanceStateRepository` is provided for demonstration and testing purposes only. It is not suitable for production applications!
350+
351+
==== Example Implementation
352+
353+
The following example shows a service instance state repository implementation:
354+
355+
[source,java,%autofit]
356+
----
357+
include::{examples-dir}/ExampleServiceInstanceStateRepository.java[]
358+
----
359+
360+
One option for persisting service instance state is to use a Spring Data `CrudRepository`. The following example shows a `ReactiveCrudRepository` implementation:
361+
362+
[source,java,%autofit]
363+
----
364+
include::{examples-dir}/ServiceInstanceStateCrudRepository.java[]
365+
----
366+
367+
A model object is necessary for persisting data with a `CrudRepository`. The following example shows a `ServiceInstance` model:
368+
369+
[source,java,%autofit]
370+
----
371+
include::{examples-dir}/ServiceInstance.java[]
372+
----
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package com.example.appbroker;
2+
3+
import reactor.core.publisher.Mono;
4+
5+
import org.springframework.cloud.appbroker.state.ServiceInstanceBindingStateRepository;
6+
import org.springframework.cloud.appbroker.state.ServiceInstanceState;
7+
import org.springframework.cloud.servicebroker.model.instance.OperationState;
8+
9+
class ExampleServiceInstanceBindingStateRepository implements ServiceInstanceBindingStateRepository {
10+
11+
private final ServiceInstanceBindingStateCrudRepository serviceInstanceBindingStateCrudRepository;
12+
13+
ExampleServiceInstanceBindingStateRepository(
14+
ServiceInstanceBindingStateCrudRepository serviceInstanceBindingStateCrudRepository) {
15+
this.serviceInstanceBindingStateCrudRepository = serviceInstanceBindingStateCrudRepository;
16+
}
17+
18+
@Override
19+
public Mono<ServiceInstanceState> saveState(String serviceInstanceId, String bindingId, OperationState state,
20+
String description) {
21+
return serviceInstanceBindingStateCrudRepository
22+
.findByServiceInstanceIdAndBindingId(serviceInstanceId, bindingId)
23+
.switchIfEmpty(Mono.just(new ServiceInstanceBinding()))
24+
.flatMap(binding -> {
25+
binding.setServiceInstanceId(serviceInstanceId);
26+
binding.setBindingId(bindingId);
27+
binding.setOperationState(state);
28+
binding.setDescription(description);
29+
return Mono.just(binding);
30+
})
31+
.flatMap(serviceInstanceBindingStateCrudRepository::save)
32+
.map(ExampleServiceInstanceBindingStateRepository::toServiceInstanceState);
33+
}
34+
35+
@Override
36+
public Mono<ServiceInstanceState> getState(String serviceInstanceId, String bindingId) {
37+
return serviceInstanceBindingStateCrudRepository
38+
.findByServiceInstanceIdAndBindingId(serviceInstanceId, bindingId)
39+
.switchIfEmpty(Mono.error(new IllegalArgumentException(
40+
"Unknown binding: serviceInstanceId=" + serviceInstanceId + ", bindingId=" + bindingId)))
41+
.map(ExampleServiceInstanceBindingStateRepository::toServiceInstanceState);
42+
}
43+
44+
@Override
45+
public Mono<ServiceInstanceState> removeState(String serviceInstanceId, String bindingId) {
46+
return getState(serviceInstanceId, bindingId)
47+
.doOnNext(serviceInstanceState -> serviceInstanceBindingStateCrudRepository
48+
.deleteByServiceInstanceIdAndBindingId(serviceInstanceId, bindingId));
49+
}
50+
51+
private static ServiceInstanceState toServiceInstanceState(ServiceInstanceBinding binding) {
52+
return new ServiceInstanceState(binding.getOperationState(), binding.getDescription(), null);
53+
}
54+
55+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package com.example.appbroker;
2+
3+
import reactor.core.publisher.Mono;
4+
5+
import org.springframework.cloud.appbroker.state.ServiceInstanceState;
6+
import org.springframework.cloud.appbroker.state.ServiceInstanceStateRepository;
7+
import org.springframework.cloud.servicebroker.model.instance.OperationState;
8+
9+
class ExampleServiceInstanceStateRepository implements ServiceInstanceStateRepository {
10+
11+
private final ServiceInstanceStateCrudRepository serviceInstanceStateCrudRepository;
12+
13+
ExampleServiceInstanceStateRepository(ServiceInstanceStateCrudRepository serviceInstanceStateCrudRepository) {
14+
this.serviceInstanceStateCrudRepository = serviceInstanceStateCrudRepository;
15+
}
16+
17+
@Override
18+
public Mono<ServiceInstanceState> saveState(String serviceInstanceId, OperationState state, String description) {
19+
return serviceInstanceStateCrudRepository.findByServiceInstanceId(serviceInstanceId)
20+
.switchIfEmpty(Mono.just(new ServiceInstance()))
21+
.flatMap(serviceInstance -> {
22+
serviceInstance.setServiceInstanceId(serviceInstanceId);
23+
serviceInstance.setOperationState(state);
24+
serviceInstance.setDescription(description);
25+
return Mono.just(serviceInstance);
26+
})
27+
.flatMap(serviceInstanceStateCrudRepository::save)
28+
.map(ExampleServiceInstanceStateRepository::toServiceInstanceState);
29+
}
30+
31+
@Override
32+
public Mono<ServiceInstanceState> getState(String serviceInstanceId) {
33+
return serviceInstanceStateCrudRepository.findByServiceInstanceId(serviceInstanceId)
34+
.switchIfEmpty(Mono.error(new IllegalArgumentException("Unknown service instance ID " + serviceInstanceId)))
35+
.map(ExampleServiceInstanceStateRepository::toServiceInstanceState);
36+
}
37+
38+
@Override
39+
public Mono<ServiceInstanceState> removeState(String serviceInstanceId) {
40+
return getState(serviceInstanceId)
41+
.doOnNext(serviceInstanceState -> serviceInstanceStateCrudRepository.deleteByServiceInstanceId(serviceInstanceId));
42+
}
43+
44+
private static ServiceInstanceState toServiceInstanceState(ServiceInstance serviceInstance) {
45+
return new ServiceInstanceState(serviceInstance.getOperationState(), serviceInstance.getDescription(), null);
46+
}
47+
48+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package com.example.appbroker;
2+
3+
import org.springframework.cloud.servicebroker.model.instance.OperationState;
4+
import org.springframework.data.annotation.Id;
5+
6+
class ServiceInstance {
7+
8+
@Id
9+
private Long id;
10+
11+
private String serviceInstanceId;
12+
13+
private String description;
14+
15+
private OperationState operationState;
16+
17+
public ServiceInstance() {
18+
19+
}
20+
21+
public ServiceInstance(String serviceInstanceId, String description, OperationState operationState) {
22+
this.serviceInstanceId = serviceInstanceId;
23+
this.description = description;
24+
this.operationState = operationState;
25+
}
26+
27+
public Long getId() {
28+
return id;
29+
}
30+
31+
public void setId(Long id) {
32+
this.id = id;
33+
}
34+
35+
public String getServiceInstanceId() {
36+
return serviceInstanceId;
37+
}
38+
39+
public void setServiceInstanceId(String serviceInstanceId) {
40+
this.serviceInstanceId = serviceInstanceId;
41+
}
42+
43+
public String getDescription() {
44+
return description;
45+
}
46+
47+
public void setDescription(String description) {
48+
this.description = description;
49+
}
50+
51+
public OperationState getOperationState() {
52+
return operationState;
53+
}
54+
55+
public void setOperationState(OperationState operationState) {
56+
this.operationState = operationState;
57+
}
58+
59+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package com.example.appbroker;
2+
3+
import org.springframework.cloud.servicebroker.model.instance.OperationState;
4+
import org.springframework.data.annotation.Id;
5+
6+
class ServiceInstanceBinding {
7+
8+
@Id
9+
private Long id;
10+
11+
private String bindingId;
12+
13+
private String serviceInstanceId;
14+
15+
private String description;
16+
17+
private OperationState operationState;
18+
19+
public ServiceInstanceBinding() {
20+
21+
}
22+
23+
public ServiceInstanceBinding(String bindingId, String serviceInstanceId, String description,
24+
OperationState operationState) {
25+
this.bindingId = bindingId;
26+
this.serviceInstanceId = serviceInstanceId;
27+
this.description = description;
28+
this.operationState = operationState;
29+
}
30+
31+
public Long getId() {
32+
return id;
33+
}
34+
35+
public void setId(Long id) {
36+
this.id = id;
37+
}
38+
39+
public String getBindingId() {
40+
return bindingId;
41+
}
42+
43+
public void setBindingId(String bindingId) {
44+
this.bindingId = bindingId;
45+
}
46+
47+
public String getServiceInstanceId() {
48+
return serviceInstanceId;
49+
}
50+
51+
public void setServiceInstanceId(String serviceInstanceId) {
52+
this.serviceInstanceId = serviceInstanceId;
53+
}
54+
55+
public String getDescription() {
56+
return description;
57+
}
58+
59+
public void setDescription(String description) {
60+
this.description = description;
61+
}
62+
63+
public OperationState getOperationState() {
64+
return operationState;
65+
}
66+
67+
public void setOperationState(OperationState operationState) {
68+
this.operationState = operationState;
69+
}
70+
71+
}

0 commit comments

Comments
 (0)