Using Spring to consume Kalix Services
This guide provides a simple example of how a Spring client can be built to interact with a Kalix Service.
What You Will Build
You will build a Kalix Service along with a Spring client to interact with that Service.
The Kalix Service will create, read, update and persist simple counters.
The Spring client will expose the functionality of the Kalix Service as a REST API.
What You Need
-
About 15 minutes
-
A favorite text editor or IDE
-
JDK 11 or later
-
Apache Maven 3.6 or later
-
Docker 20.10.14 or higher
How to get started
To get started, do the following:
-
Download and unzip the source repository for this guide, or clone it using Git:
git clone https://github.com/lightbend/kalix-jvm-sdk.git
-
cd into
kalix-jvm-sdk/samples/java-protobuf-valueentity-counter
Build and Run the Kalix Service Locally
With the source repository downloaded, you can build and run the Kalix Service locally.
Run the Kalix Service using Maven:
mvn kalix:runAll
You should see output similar to the following:
[INFO] --- exec-maven-plugin:3.0.0:exec (default-cli) @ valueentity-counter ---
{"timestamp":"2022-03-02T23:38:12.174Z","thread":"main","logger":"com.example.Main","message":"starting the Kalix service","context":"default","severity":"INFO"}
{"timestamp":"2022-03-02T23:38:12.829Z","thread":"kalix-akka.actor.default-dispatcher-2","logger":"akka.event.slf4j.Slf4jLogger","message":"Slf4jLogger started","context":"default","severity":"INFO"}
Building Spring REST Client
The Spring REST Client would enable Spring application to integrate with REST endpoints exposed via Kalix Service.
Create a Spring Boot Project
Starting with Spring Initializr
Navigate to https://start.spring.io. This service pulls in all the dependencies you need for an application and does most of the setup for you.
Choose Maven and the Java language.
Click Dependencies and select Spring Reactive Web.
Click Generate.
Download the resulting ZIP file, which is an archive of a web application that is configured with your choices.
Create a Spring WebClient
Creating a Spring REST WebClient would allow Spring application to communicate with the REST Endpoints exposed via Kalix Service.
cd into kalix-jvm-sdk/samples/java-protobuf-valueentity-counter-spring-client
.
Now you can create a WebClient
. You need to configure the WebClient to use the Kalix Service’s host and port. The following listing shows how to do so:
@Configuration
public class WebClientConfig {
@Value("${as.host}")
String host;
@Value("${as.port}")
int port;
@Bean
public WebClient createWebClient() {
return WebClient.builder().baseUrl("http://" + host + ":" + port + "")
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.build();
}
}
Create a Spring Service
The Service methods interact with endpoints defined in Kalix Service.
The CounterServiceImpl
shown in the following listing uses the Spring REST WebClient to interact with the Kalix Service and return the response received from called endpoint:
@Service
public class CounterServiceImpl implements CounterService {
@Autowired
WebClient webClient;
@Override
public Mono<String> getCounter(String counterId) {
CounterRequest counterRequest = new CounterRequest();
counterRequest.setCounterId(counterId);
return webClient.post()
.uri("/com.example.CounterService/GetCurrentCounter")
.bodyValue(counterRequest)
.retrieve()
.bodyToMono(String.class);
}
Create a Spring Controller
The Controller defines the Spring application REST endpoints which will be exposed to the user for interaction.
The CounterController
shown in the following listing uses the Spring service to get actual processing done:
@RestController
@RequestMapping("/counter")
public class CounterController {
@Autowired
CounterService counterService;
@Autowired
GrpcClientService grpcClientService;
@GetMapping(path = "/{counterId}")
public Mono<String> getCounter(@PathVariable String counterId){
return counterService.getCounter(counterId);
}
Create a Spring Main Application
The Main Application class defines the starting point for spring boot application.
The CounterClientApplication
shown in the following listing starts spring application:
@SpringBootApplication
public class CounterClientApplication {
public static void main(String[] args) {
SpringApplication.run(CounterClientApplication.class, args);
}
}
Building Spring gRPC Client
The Spring gRPC Client would enable Spring application to integrate with gRPC services exposed via Kalix Service.
Create a Spring Boot Project
Starting with Spring Initializr
Navigate to https://start.spring.io. This service pulls in all the dependencies you need for an application and does most of the setup for you.
Choose Maven and the Java language.
Click Dependencies and select Spring Reactive Web.
Click Generate.
Download the resulting ZIP file, which is an archive of a web application that is configured with your choices.
Add following Maven Dependencies to pom.xml
<properties>
<akka-grpc.version>2.1.6</akka-grpc.version>
<protobuf.version>3.19.2</protobuf.version>
<protobuf-plugin.version>0.6.1</protobuf-plugin.version>
<grpc.version>1.44.0</grpc.version>
</properties>
<dependencies>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>${grpc.version}</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>${grpc.version}</version>
</dependency>
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>1.3.5</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-client-spring-boot-starter</artifactId>
<version>2.13.1.RELEASE</version>
</dependency>
</dependencies>
<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.7.1</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>${protobuf-plugin.version}</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:3.21.7:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Generate gRPC stubs
-
Get all the proto files from relevant Kalix service and copy under
src/main/proto
folder. Remove all the Kalix annotations from the proto files. For instance[(kalix.field).id = true]
andkalix.codegen
etc. For instance, copying proto files from tojava-protobuf-valueentity-counter/src/main/proto/com/example
. -
Run
mvn compile
to generate stubs.
Create a Spring gRPC Client
Creating a Spring gRPC would allow Spring application to communicate with the gRPC Services exposed via Kalix Service.
cd into kalix-jvm-sdk/samples/java-protobuf-valueentity-counter-spring-client
.
Now you can create a GrpcClientConfig
. You need to configure the gRPC Client to use the Kalix Service’s host and port. The following listing shows how to do so:
@Configuration
public class GrpcClientConfig {
@Value("${as.host}")
String host;
@Value("${as.port}")
int port;
@Bean
public ManagedChannel createGrpcClient() {
return ManagedChannelBuilder.forAddress(host, port)
.usePlaintext()
.build();
}
}
Create a Spring Service
The Service methods interact with gRPC services defined in Kalix Service.
The GrpcClientService
shown in the following listing uses the Spring REST WebClient to interact with the Kalix Service and return the response received from called endpoint:
@Service
public class GrpcClientService {
@Autowired
ManagedChannel channel;
public String decreaseCounter(String counterId, ValueRequest valueRequest) {
CounterServiceGrpc.CounterServiceBlockingStub counterServiceBlockingStub =
CounterServiceGrpc.newBlockingStub(channel);
Empty decreaseResponse = counterServiceBlockingStub.decrease(CounterApi.DecreaseValue.newBuilder()
.setCounterId(counterId)
.setValue(valueRequest.getValue())
.build());
return decreaseResponse.toString();
}
}
Create a Spring Controller
The Controller defines the Spring application REST endpoints which will be exposed to the user for interaction.
The CounterController
shown in the following listing uses the Spring service to get actual processing done:
@RestController
@RequestMapping("/counter")
public class CounterController {
@Autowired
CounterService counterService;
@Autowired
GrpcClientService grpcClientService;
@GetMapping(path = "/{counterId}")
public Mono<String> getCounter(@PathVariable String counterId){
return counterService.getCounter(counterId);
}
Create a Spring Main Application
The Main Application class defines the starting point for spring boot application.
The CounterClientApplication
shown in the following listing starts spring application:
@SpringBootApplication
public class CounterClientApplication {
public static void main(String[] args) {
SpringApplication.run(CounterClientApplication.class, args);
}
}