5. Service Bindings

By default, Spring Cloud App Broker does not include functionality for managing bindings to its service instances. App Broker provides interfaces that service broker authors can implement to control service bindings.spring-doc.cn

5.1. Creating a Service Binding

The service broker application can implement the CreateServiceInstanceAppBindingWorkflow interface. Alternatively, the service broker application can implement the ServiceInstanceBindingService interface provided by Spring Cloud Open Service Broker. See Service Bindings in the Spring Cloud Open Service Broker documentation.spring-doc.cn

5.2. Deleting a Service Binding

The service broker application can implement the DeleteServiceInstanceBindingWorkflow interface. Alternatively, the service broker application can implement the ServiceInstanceBindingService interface provided by Spring Cloud Open Service Broker. See Service Bindings in the Spring Cloud Open Service Broker documentation.spring-doc.cn

5.3. Persisting Service Instance Binding State

Spring Cloud App Broker provides the ServiceInstanceBindingStateRepository interface for persisting service instance binding state. The default implementation is 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.spring-doc.cn

The InMemoryServiceInstanceBindingStateRepository is provided for demonstration and testing purposes only. It is not suitable for production applications!

5.3.1. Example Implementation

The following example shows a service instance binding state repository implementation:spring-doc.cn

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

}

One option for persisting service instance binding state is to use a Spring Data CrudRepository. The following example shows a ReactiveCrudRepository implementation:spring-doc.cn

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);

}

A model object is necessary for persisting data with a CrudRepository. The following example shows a ServiceInstanceBinding model:spring-doc.cn

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

}