Spring Boot + Undertow + Actuator with Camel XML DSL example

May 19, 2021   881 Views
  • In this blog, we will learn How to enable actuator endpoints? How we can exclude embedded tomcat container in Spring Boot microservice? How to configure Undertow server in Spring Boot application?
  • To load camel context, e.g. camel-context.xml we need to add @ImportResource annotation and put your XML route definition on classpath
  • Application properties can be read from application.properties or application.yaml and extrapolated with {{ }} syntax.
  • Beans can be defined in XML, Java Config and Spring Autoconfiguration.

Step by Step Example

Here we have step by step guide.

1. Spring Boot and Camel BOM Dependencies

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

  <modelVersion>4.0.0</modelVersion>

  <groupId>com.example</groupId>
  <artifactId>order-route5</artifactId>
  <packaging>jar</packaging>
  <version>1.0-SNAPSHOT</version>

  <name>A Camel Spring Boot Route</name>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <spring.boot-version>2.3.5.RELEASE</spring.boot-version>
  </properties>

  <dependencyManagement>
    <dependencies>
      <!-- Spring Boot BOM -->
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-dependencies</artifactId>
        <version>${spring.boot-version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
      <!-- Camel BOM -->
      <dependency>
        <groupId>org.apache.camel.springboot</groupId>
        <artifactId>camel-spring-boot-dependencies</artifactId>
        <version>3.7.0-SNAPSHOT</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <dependencies>

    <!-- Spring Boot -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
      <exclusions>
        <exclusion>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-undertow</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>

    <!-- Camel -->
    <dependency>
      <groupId>org.apache.camel.springboot</groupId>
      <artifactId>camel-spring-boot-starter</artifactId>
    </dependency>
    <dependency>
      <groupId>org.apache.camel.springboot</groupId>
      <artifactId>camel-stream-starter</artifactId>
    </dependency>

    <!-- Test -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.apache.camel</groupId>
      <artifactId>camel-test-spring-junit5</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.1</version>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <version>${spring.boot-version}</version>
        <executions>
          <execution>
            <goals>
              <goal>repackage</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>3.0.0-M4</version>
      </plugin>
    </plugins>
  </build>
</project>

2. Spring Boot Application Runner

package the.basic.tech.info;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportResource;

@SpringBootApplication
@ImportResource("classpath:camel-context.xml")
public class MySpringBootApplication {	
	public static void main(String[] args) {
		SpringApplication.run(MySpringBootApplication.class, args);
	}
}

3. Camel Context and Camel routes

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">

	<camelContext xmlns="http://camel.apache.org/schema/spring">

		<route id="hello">
			<from uri="timer:hello?period={{timer.period}}"/>
			<transform>
				<method ref="myBean" method="saySomething" />
			</transform>
			<filter>
				<simple>"${body} contains 'foo'"</simple>
				<to uri="log:foo" />
			</filter>
			<to uri="seda:out" />
		</route>
	</camelContext>
</beans>

4. Bean class (myBean)

package the.basic.tech.info;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component("myBean")
public class MySpringBean {

    @Value("${greeting}")
    private String say;

    public String saySomething() {
        return say;
    }
}

5. Resources (application.properties/application.yaml)

# Management
server:
  port: 8080
  servlet.context-path: /camel-springboot-route/api/
endpoints:
  hawtio:
    enabled: true
  jolokia:
    enabled: true
management:
  endpoints:
    web:
      exposure:
        include: info, health, metrics, hawtio, jolokia, env, camelroutes
      base-path: /monitoring
      path-mapping.hawtio: /hawtio/console
  endpoint:
    health:
      show-details: always
hawtio:
  authenticationEnabled: false
info:
  app:
    name:camel-springboot-route
    description:This is a microservice for camel springboot route testing
          
## Application Name
camel:
  springboot:
    name: camel-springboot-route

# what to say
greeting: Hello World

# how often to trigger the timer
timer.period: 2000

6. Unit Test with Junit 5

package the.basic.tech.info;

import org.apache.camel.CamelContext;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.builder.AdviceWithRouteBuilder;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
@CamelSpringBootTest
public class MySpringBootApplicationTest {

	@Autowired
	private CamelContext camelContext;

	@Autowired
	private ProducerTemplate producerTemplate;

	@Test
	public void test() throws Exception {
		MockEndpoint mock = camelContext.getEndpoint("mock:seda:out", MockEndpoint.class);

		AdviceWithRouteBuilder.adviceWith(camelContext, "hello",
				// intercepting an exchange on route
				r -> {
					// replacing consumer with direct component
					r.replaceFromWith("direct:start");
					// mocking producer
					r.mockEndpoints("seda*");
				}
		);

		// setting expectations
		mock.expectedMessageCount(1);
		mock.expectedBodiesReceived("Hello World");

		// invoking consumer
		producerTemplate.sendBody("direct:start", null);

		// asserting mock is satisfied
		mock.assertIsSatisfied();
	}
}

7. Run the Spring Boot Application

>mvn spring-boot:run

8. Snapshot

9. Application Health Monitoring – We are using the below maven dependency, hence application health can be monitored.

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
 </dependency>

You can hit: http://localhost:8080/camel-springboot-route/api/monitoring/info

Enjoy :), kindly let me know in comment section in case of any issue.