Spring Boot, Docker and Websockets Integration with Apache Camel
Posted: April 1st, 2015 | Author: sabre1041 | Filed under: Technology | Tags: Camel, Docker, Integration, Spring Boot, Websockets | 1 Comment »First, lets discuss the configurations necessary for Spring Boot as it will be the framework the application with run within. An Application class should be created first as it is the entry point for the application and should be annotated with the @SpringBootApplication annotation. This convenience annotation marks this class as the source for component definitions and enables classpath scanning for components.
Next, the Camel RouteBuilder class should be created which will contain all of the Camel routes. This class should be annotated with the @Component annotation to allow Spring Boot to load the class automatically. If the class is created in a different package higher up in the package tree than the Application class, the @ComponentScan(basePackages = “packageName”) can be added to the Application to specify additional packages for Spring Boot to scan.
With the core components in place, let’s configure the first requirement of the application where statistics from Docker are exposed via a rest endpoint in Camel. As previously mentioned, we can use the Camel Rest DSL. While there is support from a number of Camel components for the Rest DSL, the camel-servlet component will be used. This is configured in our route builder class by adding:
restConfiguration().component("servlet").port(8080);
By default, Spring Boot initializes on port 8080 so the servlet will be configured to match this setting. Next, Spring Boot must be configured to register and start the CamelHttpTransportServlet at startup. The method is annotated with the @Bean annotation so it will be managed by the Spring container.
@Bean public ServletRegistrationBean camelServletRegistration() { // Create servlet for Camel Rest Endpoints ServletRegistrationBean registration = new ServletRegistrationBean( new CamelHttpTransportServlet(), “/camel/*”); registration.setName(“CamelServlet”); return registration; }
Now that the servlet exposing the restful resources has been registered, the resources themselves need to be configured within Camel. The following table describes the uri paths and their purpose for the operations that will be created:
URI Path | Description |
---|---|
/ | System Wide Information |
/info | System Wide Information |
/version | Show Docker version information |
/images | List Docker Images |
/containers | List Docker Containers |
To simplify the configurations, only two uri paths need to be defined within Camel:
- / to signify a base request which will be mapped to the Docker info operation
- /{type} – utilize a path parameter the type of Docker statistic to obtain
A message header value will be set signifying the type of request that should be made against Docker. The second resource will obtain the value from the path parameter while the default resource will have this value statically assigned. This header value will be used in content based routing in a subsequent route. In the downstream route, if the header value matches one of the permitted types, the request will be made to Docker. Otherwise, a response is set to the caller with the appropriate message and error code. Docker is invoked through a single endpoint where uri parameters are dynamically loaded at runtime using the recipient list EIP. The Docker operation is retrieved from the header of the current message and the host and port locations for Docker are obtained from external configuration values in Spring Boot via the spring boot component. Externalized configurations in Spring Boot are stored in the application.properties file in the src/main/resources folder. These properties can be referenced within a camel route like any other Camel properties with the {{ }} tag and are resolved at runtime. When a request is received, the appropriate header is set based on the request resource and it combined with the host and port values of the Docker endpoint to invoke the Docker daemon and return a response to the caller. With these configurations complete, the first requirement of the application is fulfilled.
The second requirement of the application is to produce events from within the Docker daemon to a websockets endpoint for consumption by clients. Spring boot makes it easy to create a websockets endpoint. First, annotate the Application class with the @EnableWebSocket annotation to enable websockets functionality within Spring Boot. Next, a handler managing the lifecycle of the endpoint will be created. Since this demonstration will feature a simplistic use case, it will handle only text message, so a class extending the TextWebSocketHandler class can be created. To emulate broadcast style functionality, the current set of active sessions will need to be tracked. This is accomplished by adding and removing sessions within the afterConnectionEstablished and afterConnectionClosed methods which are invoked when sessions connect and disconnect. When a new message is received, the handleTextMessage method is invoked where each active user receives a copy of the incoming message. With the handler class now complete, the websockets endpoint can be registered into the framework. Implement the WebSocketConfigurer interface in the Application class and register the context path and handler created earlier in the registerWebSocketHandlers method.
@Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { // Expose endpoint and add Handler. Wildcard allowed origins to support COORS registry.addHandler(websocketTextHandler(), "/websocket").setAllowedOrigins("*"); }
Now that the Spring Boot components are in place, let’s set up Camel to listen for Docker events and produce the received events to the websockets endpoint. This functionality is enabled through the Docker component which utilizes the streaming events function of the Docker Rest API. All events, such as the lifecycle of images and containers, can be consumed by Camel using the following endpoint consumer:
from("docker://events?host={{docker.server}}&port={{docker.port}}")
To produce to a websockets endpoint, the Camel Async HTTP (AHC) Websocket client component can be leveraged. While the component accepts an array of configurable values, only the websockets endpoint location needs to be specified. Since the websockets endpoint was configured to only accept text messages, the Camel message body must be converted from the Java object that is produced from Docker to a String value using the convertBodyTo component as follows:
.convertBodyTo(String.class) .to("ahc-ws://localhost:8080{{server.context-path}}/websocket?sendToAll=true");
The server.context-path is an externalized property specified in the application.properties file. For the purpose of this application, it will be defined as /spring-boot-docker-camel
The final requirement is to configure a webpage to allow for the events on the websockets endpoint to be viewable along with listing the invokable rest endpoints for viewing Docker statistics. Since the spring-boot-starter-web dependency is included in the project to enable the publishing of the Camel Servlet, it adds the spring-webmvc as a transitive dependency. This allows for static html files to be placed in the src/main/webapps folder which will automatically be served. If a file named index.html is placed in this folder, it will be loaded automatically when navigating to the /spring-boot-docker-camel context in a web browser. A file is placed in this location to handle the project requirement
Hi Andrew
Really great blog. Loved reading it. And great to see your contributions to the Camel project put together into a nice microservice style app.
Keep up the good work and see you around in the community.
I put a link to your blog from our collection at: http://camel.apache.org/articles
/Claus Ibsen