
이 글에서는
Apache Camel 4.12.0,jdk 21버전을 사용합니다.
또한 쉬운 시작을 위해서Spring boot프로젝트 기반으로 작성했습니다만,
실제Spring Boot기능을 사용하지는 않는다는 점 참고하시기 바랍니다.
camel 에서 대표적인 2개의 DSL 인 xml, yaml dsl 적용을 위해서
아래와 같이 spring boot 프로젝트 생성시 기본으로 생성되는 pom.xml 에다가
2개의 dependency 를 추가했습니다.
camel-yaml-dslcamel-xml-io-dsl<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>coding.toast</groupId>
<artifactId>camel-micro-service-a</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>my-little-camel</name>
<properties>
<java.version>21</java.version>
<camel.version>4.12.0</camel.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-spring-boot-starter</artifactId>
<version>${camel.version}</version>
</dependency>
<!-- yaml dsl 사용을 위한 dependency -->
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-yaml-dsl</artifactId>
<version>${camel.version}</version>
</dependency>
<!-- xml dsl 사용을 위한 dependency -->
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-xml-io-dsl</artifactId>
<version>${camel.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
package coding.toast.mylittlecamel;
import org.apache.camel.CamelContext;
import org.apache.camel.impl.DefaultCamelContext;
import org.apache.camel.impl.engine.DefaultResourceResolvers;
import org.apache.camel.spi.Resource;
import org.apache.camel.spi.RoutesLoader;
import org.apache.camel.support.PluginHelper;
import org.apache.camel.support.ResourceHelper;
public class MainTestClass {
public static void main(String[] args) {
try (CamelContext camelContext = new DefaultCamelContext()) {
// camelContext 를 미리 Start 합니다.
// Router 를 모두 적용한 이후에 해도 상관없습니다.
camelContext.start();
// DSL 정보를 담는 Resource 를 읽어옵니다.
// 아래 Resource 는 classpath 에 파일(src/main/resources/my-route.yaml) 을 정보입니다.
Resource resource
= ResourceHelper.resolveResource(camelContext, "classpath:/my-route.yaml");
// routeLoader 를 참조합니다.
RoutesLoader routesLoader = PluginHelper.getRoutesLoader(camelContext);
// 참조하는 routeLoader 에 Router 정보를 담고 있는 DSL Resource 를 Load 합니다.
loader.loadRoutes(resource);
// 기본적으로 카멜은 별개의 쓰레드 위에서 비동기적으로 동작함으로,
// 강제로 main 쓰레드를 대기 시킵니다.
Thread.sleep(10000);
camelContext.stop();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
// 참고: classpath 뿐만아니라, file 스키마도 지원합니다!
// Resource resource
// = ResourceHelper.resolveResource(camelContext, "file:C:\\my-route.yaml");
// 참고2: ResourceLoader 는 다음과 같은 방법으로 참조할 수도 있습니다.
// RoutesLoader loader
// = camelContext.getCamelContextExtension().getContextPlugin(RoutesLoader.class);
src/resources/my-route.yaml 파일을 아래처럼 작성합니다.
- from:
uri: "timer:yamlTimer?period=1000"
steps:
- setBody:
simple: "Hello from YAML at ${date:now}"
- to: "log:yamlRoute"

참고로 하나의 yaml 파일에 여러개의 from 절(=consumer)을 사용할 수 있습니다.
- from:
uri: "timer:yamlTimer?period=1000"
steps:
- setBody:
simple: "Hello from YAML at ${date:now}"
- to: "direct:aaa"
- from:
uri: "direct:aaa"
steps:
- to: "log:finish-logger"
이러면 "log:finish-logger" 에 의하여 로그가 찍히게 됩니다.

router id 도 부여하고 싶다면 아래처럼 하면 됩니다.
- route:
id: foo1
from:
uri: "timer:yamlTimer?period=1000"
steps:
- setBody:
simple: "Hello from YAML at ${date:now}"
- to: "direct:aaa"
- route:
id: foo2
from:
uri: "direct:aaa"
steps:
- to: "log:finish-logger"
package coding.toast.mylittlecamel;
import org.apache.camel.CamelContext;
import org.apache.camel.impl.DefaultCamelContext;
import org.apache.camel.spi.Resource;
import org.apache.camel.spi.RoutesLoader;
import org.apache.camel.support.PluginHelper;
import org.apache.camel.support.ResourceHelper;
public class MainTestClass {
public static void main(String[] args) {
try (CamelContext camelContext = new DefaultCamelContext()) {
camelContext.start();
// my-route.xml -> my-route.xml 로 바뀐 거 빼고는 다 똑같습니다.
Resource resource
= ResourceHelper.resolveResource(camelContext, "classpath:/my-route.xml");
RoutesLoader loader = PluginHelper.getRoutesLoader(camelContext);
loader.loadRoutes(resource);
Thread.sleep(10000);
camelContext.stop();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
<routes>
<route id="xmlRoute">
<from uri="timer:xmlTimer?period=1000"/>
<setBody>
<simple>Hello from XML at ${date:now}</simple>
</setBody>
<to uri="direct:second"/>
</route>
<route id="xmlRoute2">
<from uri="direct:second"/>
<to uri="log:xml-log-finisher"/>
</route>
</routes>
실행결과:

때로는 문자열 형태의 DSL 이 필요할 수 있습니다.
이때는 MemResolver 를 사용해보세요!
package coding.toast.mylittlecamel;
import org.apache.camel.CamelContext;
import org.apache.camel.impl.DefaultCamelContext;
import org.apache.camel.impl.engine.DefaultResourceResolvers;
import org.apache.camel.spi.Resource;
import org.apache.camel.spi.RoutesLoader;
import org.apache.camel.support.PluginHelper;
import org.apache.camel.support.ResourceHelper;
class CamelTestMain {
public static void main(String[] args) {
try (CamelContext camelContext = new DefaultCamelContext()) {
camelContext.start();
String yaml = """
- from:
uri: "timer:inMemory?period=3000"
steps:
- setBody:
simple: "Hi from MemResolver at ${date:now}"
- to: "log:memResolver"
""";
// 방법(1): Resolver 사용
try (DefaultResourceResolvers.MemResolver resolver
= new DefaultResourceResolvers.MemResolver()) {
// createResource 의 첫번째 인자를 넣을 때,
// 문자열 형태가
// - yaml 이면 "mem:??.yaml",
// - xml 이면 "mem:???.xml"
// 처럼 끝에 확장자를 정확히 명시적으로 표기해야 합니다!
Resource resource = resolver.createResource("mem:my-route.yaml", yaml);
// Resource resource = resolver.createResource("mem:my-route.xml", xml);
RoutesLoader loader = PluginHelper.getRoutesLoader(camelContext);
loader.loadRoutes(resource);
}
// 방법(2): ResourceHelper 사용
// 또 다른 방법으로는 ResourceHelper 를 사용하는 겁니다.
// Resource resource
// = ResourceHelper.fromBytes("dynamic-route.yaml", yaml.getBytes());
// RoutesLoader loader = PluginHelper.getRoutesLoader(camelContext);
// loader.loadRoutes(resource);
Thread.sleep(10000);
camelContext.stop();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
Thread.sleep(5000);
// stopRoute, removeRoute 모두 routeId 를 문자열로 넘겨줘야 합니다.
// 먼서 Stop 을 해야 합니다. Stop 없이 remove 하려면 무효화됩니다.
camelContext.getRouteController().stopRoute("2025-01-01-UUID1", 5000, TimeUnit.SECONDS);
camelContext.getRouteController().stopRoute("2025-01-01-UUID2", 5000, TimeUnit.SECONDS);
// Stop 이후 remove 를 하면 정상적으로 Load 했던 router 가 삭제됩니다.
camelContext.removeRoute("2025-01-01-UUID1");
camelContext.removeRoute("2025-01-01-UUID2");
Thread.sleep(5000); // keep app running for a while
camelContext.stop();
참고로 어떤 Component/Endpoint 를 쓰냐에 따라서 곧바로 중지할 수도 있고,
Graceful 하게 느릿하게 정지될 수도 있습니다.
하지만 Graceful 하게 종료시키려고 함에도 timeout 날 수도 있습니다.
이는 Camel 의 자연스러운 동작 방식입니다. Camel 은 어떻게든 마지막까지 데이터를
다 받거나 주는 것이 안전한 것으로 판단하기 때문이죠.
Jbang + camel 을 활용하면 cli 환경에서 곧바로 camel route 를 실행할 수 있고,
심지어는 여러 camel 실행 내역을 관리하는 것도 가능합니다.
camel 실행예시

camel 관리예시

camel 을 가볍게 시작하시고 싶으시거나, 빠르게 ui 개발만 하고 실제 route 처리는
jbang camel 에게 위임하는 방식의 개발을 하고 싶으신 분들에게 매우 유용합니다.
더 다양한 예시는 https://github.com/apache/camel-jbang-examples 참고!