Java Spring Server Sent Events

The Spring Server Sent Events client is a java client that allows to receive events when :

  • A job status is modified
  • A job solution is found

Please refer to the documentation of the Master SSE endpoints for further details.

Installation

Maven users

Add this dependency to your project’s POM:

<dependency>
  <groupId>com.decisionbrain</groupId>
  <artifactId>optimserver-client-sse-spring</artifactId>
  <version>PROJECT_VERSION</version>
  <scope>compile</scope>
</dependency>

Gradle users

Add this dependency to your project’s build file:

compile "com.decisionbrain:optimserver-client-sse-spring:PROJECT_VERSION"

Getting Started

A SseListener implementation is defined in the Spring context and allows you to register and unregister a listener to a job events.

You will need to define a optim-server.sse.client.master.url property in your spring context that allows the client to connect to the Master API .

You will also need to define a @Bean of type AuthenticationService to be able to authenticate upon the Master API

The following code is a spring boot example of the application :

OptimserverSseClientApplication.java



import com.decisionbrain.optimserver.client.sse.AuthenticationService;
import com.decisionbrain.optimserver.client.sse.JobEventListener;
import com.decisionbrain.optimserver.client.sse.SseListener;
import com.decisionbrain.optimserver.client.sse.config.KeycloakClientConfig;
import com.decisionbrain.optimserver.client.sse.impl.KeycloakAuthenticationServiceImpl;
import com.decisionbrain.optimserver.master.model.JobSolution;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration;
import org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.function.Consumer;

@SpringBootApplication(exclude = {PersistenceExceptionTranslationAutoConfiguration.class, ReactiveWebServerFactoryAutoConfiguration.class})
@Configuration
public class OptimserverSseClientApplication implements CommandLineRunner {

    @Autowired
    private SseListener sseListener;

    @Autowired
    private JobEventListener jobEventListener;


    public static void main(String[] args) {
        // This is the main Spring boot application, without web profile
        new SpringApplicationBuilder(OptimserverSseClientApplication.class)
                .web(WebApplicationType.NONE)
                .run(args);
    }

    // This bean is the keycloak authentication Service. You could also use the BasicAuthenticationServiceImpl
    @Bean
    public AuthenticationService authenticationService(KeycloakClientConfig keycloakClientConfig) {
        return new KeycloakAuthenticationServiceImpl(keycloakClientConfig);
    }

    // This bean is the keycloak config : you can get the values from the application.yml file
    @Bean
    public KeycloakClientConfig keycloakClientConfig(
            @Value("${keycloak.url}") String url,
            @Value("${keycloak.realm}") String realm,
            @Value("${keycloak.client}") String client,
            @Value("${keycloak.user}") String user,
            @Value("${keycloak.password}") String password
    ) {
        return KeycloakClientConfig.builder()
                .url(url)
                .realm(realm)
                .client(client)
                .user(user)
                .password(password)
                .build();
    }

    // This bean is your main listener
    @Bean
    public JobEventListener jobEventListener(){
        return new JobEventListener() {
            @Override
            public Consumer<JobSolution> onSolution() {
                return (jobSolution) -> System.out.println("Do whatever you want with the jobSolution : " + jobSolution);
            }

            @Override
            public Consumer<com.decisionbrain.optimserver.master.model.JobStatusEvent> onJobStatusEvent() {
                return (jobStatusEvent) -> System.out.println("Do whatever you want with the jobStatusEvent : " + jobStatusEvent);
            }

            @Override
            public Consumer<Throwable> onSolutionError() {
                return (exception) -> System.out.println("Do whatever you want with the Exception : " + exception);
            }

            @Override
            public Consumer<Throwable> onJobStatusEventError() {
                return (exception) -> System.out.println("Do whatever you want with the Exception : " + exception);
            }
        };
    }

    @Override
    public void run(String... args) throws Exception {
        sseListener.register(args[0], jobEventListener);

        Thread.currentThread().join();
    }
}


application.yml :

optim-server:
  sse:
    client:
      master:
        url: http://localhost:8080
        
keycloak:
  url: http://KEYCLOAK_HOST/auth
  realm: decisionbrain
  client: optimserver
  user: optimserver
  password: optimserver

You can then launch your application with a simple java -jar or inside your IDE.