第 295 章 Camel SCR (已弃用)
从 Camel 2.15 开始提供
SCR 代表服务组件运行时,是 OSGi Declarative Services 规格的实施。SCR 允许任何普通旧 Java 对象公开和使用没有样板代码的 OSGi 服务。
OSGi 框架通过查看其捆绑包中的 SCR 描述符文件来知道您的对象,这些文件通常是通过 org.apache.felix:maven-scr-plugin
等 Java 注解生成的。
在 SCR 捆绑包中运行 Camel 是 Spring DM 和 Blueprint 基于蓝图的解决方案,你和 OSGi 框架之间代码行要少得多。使用 SCR 您的捆绑包可在 Java 世界中完全保留;无需编辑 XML 或属性文件。这可让您完全控制一切,这意味着您选择的 IDE 知道您的项目中的具体内容。
295.1. Camel SCR 支持
camel-scr 捆绑包没有包括在 Apache Camel 之前的版本 2.15.0 中,但工件本身可以从 2.12.0 开始用于任何 Camel 版本。
org.apache.camel/camel-scr
捆绑包提供基本类 AbstractCamelRunner
,它管理您和帮助程序类 ScrHelper
,用于在单元测试中使用 SCR 属性。Camel-scr 功能可用于定义在 SCR 捆绑包中运行 Camel 所需的所有功能和捆绑包。
AbstractCamelRunner
类将 CamelContext 的生命周期与 Service 组件的生命周期相关联,并使用 Camel 的 PropertiesComponent 来处理配置。从 java 类中使一个 Service 组件从 AbstractCamelRunner
进行扩展,并在类级别上添加以下 org.apache.felix.scr.annotations
:
添加所需的注解
@Component @References({ @Reference(name = "camelComponent",referenceInterface = ComponentResolver.class, cardinality = ReferenceCardinality.MANDATORY_MULTIPLE, policy = ReferencePolicy.DYNAMIC, policyOption = ReferencePolicyOption.GREEDY, bind = "gotCamelComponent", unbind = "lostCamelComponent") })
然后实施 getRouteBuilders ()
方法,该方法返回您要运行的 Camel 路由:
实施 getRouteBuilders ()
@Override protected List<RoutesBuilder> getRouteBuilders() { List<RoutesBuilder> routesBuilders = new ArrayList<>(); routesBuilders.add(new YourRouteBuilderHere(registry)); routesBuilders.add(new AnotherRouteBuilderHere(registry)); return routesBuilders; }
最后提供默认配置:
注解中的默认配置
@Properties({ @Property(name = "camelContextId", value = "my-test"), @Property(name = "active", value = "true"), @Property(name = "...", value = "..."), ... })
这一切。并且如果您使用 camel-archetype-scr
来生成一个项目,则这已经需要注意。
以下是由 camel-archetype-scr 生成的完整的 Service 组件类示例:
CamelScrExample.java
// This file was generated from org.apache.camel.archetypes/camel-archetype-scr/2.15-SNAPSHOT package example; import java.util.ArrayList; import java.util.List; import org.apache.camel.scr.AbstractCamelRunner; import example.internal.CamelScrExampleRoute; import org.apache.camel.RoutesBuilder; import org.apache.camel.spi.ComponentResolver; import org.apache.felix.scr.annotations.*; @Component(label = CamelScrExample.COMPONENT_LABEL, description = CamelScrExample.COMPONENT_DESCRIPTION, immediate = true, metatype = true) @Properties({ @Property(name = "camelContextId", value = "camel-scr-example"), @Property(name = "camelRouteId", value = "foo/timer-log"), @Property(name = "active", value = "true"), @Property(name = "from", value = "timer:foo?period=5000"), @Property(name = "to", value = "log:foo?showHeaders=true"), @Property(name = "messageOk", value = "Success: {{from}} -> {{to}}"), @Property(name = "messageError", value = "Failure: {{from}} -> {{to}}"), @Property(name = "maximumRedeliveries", value = "0"), @Property(name = "redeliveryDelay", value = "5000"), @Property(name = "backOffMultiplier", value = "2"), @Property(name = "maximumRedeliveryDelay", value = "60000") }) @References({ @Reference(name = "camelComponent",referenceInterface = ComponentResolver.class, cardinality = ReferenceCardinality.MANDATORY_MULTIPLE, policy = ReferencePolicy.DYNAMIC, policyOption = ReferencePolicyOption.GREEDY, bind = "gotCamelComponent", unbind = "lostCamelComponent") }) public class CamelScrExample extends AbstractCamelRunner { public static final String COMPONENT_LABEL = "example.CamelScrExample"; public static final String COMPONENT_DESCRIPTION = "This is the description for camel-scr-example."; @Override protected List<RoutesBuilder> getRouteBuilders() { List<RoutesBuilder> routesBuilders = new ArrayList<>(); routesBuilders.add(new CamelScrExampleRoute(registry)); return routesBuilders; } }
CamelContextId
和 active
属性控制 CamelContext 的名称(默认为 "camel-runner-default")以及是否要启动它(默认为 "false"。除了这些之外,您还可以添加和使用您想要的相同属性。Camel 的 PropertiesComponent 处理递归属性,并在没有问题的情况下使用回退前缀。
AbstractCamelRunner
将为您的 RouteBuilders 提供这些属性,并提供 Camel 的 PropertiesComponents,它还会在名称匹配时将这些值注入到您的 Service 组件和 RouteBuilder 字段中。可以使用任何可见性级别声明字段,并且支持许多类型(字符串、int、布尔值、URL、…)。
以下是由 camel-archetype-scr
生成的 RouteBuilder 类的示例:
CamelScrExampleRoute.java
// This file was generated from org.apache.camel.archetypes/camel-archetype-scr/2.15-SNAPSHOT package example.internal; import org.apache.camel.LoggingLevel; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.impl.SimpleRegistry; import org.apache.commons.lang.Validate; public class CamelScrExampleRoute extends RouteBuilder { SimpleRegistry registry; // Configured fields private String camelRouteId; private Integer maximumRedeliveries; private Long redeliveryDelay; private Double backOffMultiplier; private Long maximumRedeliveryDelay; public CamelScrExampleRoute(final SimpleRegistry registry) { this.registry = registry; } @Override public void configure() throws Exception { checkProperties(); // Add a bean to Camel context registry registry.put("test", "bean"); errorHandler(defaultErrorHandler() .retryAttemptedLogLevel(LoggingLevel.WARN) .maximumRedeliveries(maximumRedeliveries) .redeliveryDelay(redeliveryDelay) .backOffMultiplier(backOffMultiplier) .maximumRedeliveryDelay(maximumRedeliveryDelay)); from("{{from}}") .startupOrder(2) .routeId(camelRouteId) .onCompletion() .to("direct:processCompletion") .end() .removeHeaders("CamelHttp*") .to("{{to}}"); from("direct:processCompletion") .startupOrder(1) .routeId(camelRouteId + ".completion") .choice() .when(simple("${exception} == null")) .log("{{messageOk}}") .otherwise() .log(LoggingLevel.ERROR, "{{messageError}}") .end(); } } public void checkProperties() { Validate.notNull(camelRouteId, "camelRouteId property is not set"); Validate.notNull(maximumRedeliveries, "maximumRedeliveries property is not set"); Validate.notNull(redeliveryDelay, "redeliveryDelay property is not set"); Validate.notNull(backOffMultiplier, "backOffMultiplier property is not set"); Validate.notNull(maximumRedeliveryDelay, "maximumRedeliveryDelay property is not set"); } }
让我们一起查看 CamelScrExampleRoute
。
// Configured fields private String camelRouteId; private Integer maximumRedeliveries; private Long redeliveryDelay; private Double backOffMultiplier; private Long maximumRedeliveryDelay;
这些字段的值通过匹配其名称来使用来自属性的值。
// Add a bean to Camel context registry registry.put("test", "bean");
如果需要将一些 Bean 添加到 CamelContext 的 registry 中用于路由,您可以执行以下操作:
public void checkProperties() { Validate.notNull(camelRouteId, "camelRouteId property is not set"); Validate.notNull(maximumRedeliveries, "maximumRedeliveries property is not set"); Validate.notNull(redeliveryDelay, "redeliveryDelay property is not set"); Validate.notNull(backOffMultiplier, "backOffMultiplier property is not set"); Validate.notNull(maximumRedeliveryDelay, "maximumRedeliveryDelay property is not set"); }
最好检查是否设置了所需的参数,并在允许路由启动前具有有意义的值。
from("{{from}}") .startupOrder(2) .routeId(camelRouteId) .onCompletion() .to("direct:processCompletion") .end() .removeHeaders("CamelHttp*") .to("{{to}}"); from("direct:processCompletion") .startupOrder(1) .routeId(camelRouteId + ".completion") .choice() .when(simple("${exception} == null")) .log("{{messageOk}}") .otherwise() .log(LoggingLevel.ERROR, "{{messageError}}") .end();
请注意,路由中的一切都配置有属性。这基本上使您的 RouteBuilder 成为模板。SCR 允许您通过提供替代配置来创建更多路由实例。有关使用 Camel SCR 捆绑包作为模板 的更多信息。