Endpoint:即节点,即消息经过的地方
Camel支持的EndPoint
1.JMS队列
2.web service
3.文件
4.FTP服务
5.email地址
6.POJO
在基于Camel的应用中,你会创建一些EndPoint然后用路由将这些EndPoint连接起来。Camel定义了一个名这EndPoint的接口,每一个Camel支持的EndPoint都有一个类实现了该EndPoint接口,Camel提供了单独的Javadoc继承树为Camel支持的每一种通信技术。
CamelContext
一个CamelContext对象代表了Camel的运行时系统,典型的你会有一个CamelContext对象在一个应用中,一个典型的应用遵循下面的步骤:
1.创建一个CamelContext对象
2.添加Endpoints和可能的组件到该CamelContext对象中
3.添加路由到该CamelContext对象中去连接这些EndPoint
4.调用CamelContext的start()方法,这样会启动Camel内部线程,这些线程用于处理EndPoint中消息的发送,接收与处理
5.调用CamelContext的stop()方法,这样将会停止所有Camel内部线程
注意:CamelContext.start()方法,并不会无限制阻塞下去,而是该方法会为每一个组件与Endpoint开始内部线程然后返回。同理,CamelContext.stop()方法会等待每一个组件与EndPoint的所有内部线程都终止后该方法才会返回。
如果在你的应用中没有调用CamelContext.start()方法,消息将不会被处理因为内部线程还没有被创建。如果在停止你的应用之前你没有调用CamelContext.stop()方法,应用可能会以一种不一致的状态停止。如果在JUnit测试中没调用CamelContext.stop()方法,测试可能会因为消息没有机会完全被处理而失败。
CamelTemplate
Camel以前有一个叫CamelClient的类,但是现在已经更名为CamelTemplate了,这样做的原因是迎合其它开源项目的命名约定,如Spring中的TransactionTemplate和JmsTemplate。
CamelTemplate类是一个包装了CamelContext的精简类,它有发送消息或Exchange到EndPoint的方法。这样就提供了一个输入消息到源Endpoint的途径,所以消息会沿着路由移动直到目的Endpoint。
Component
Component是一个容易术语,用EndpointFactory可能会更恰当因为Component是一个用于创建EndPoint实例的工厂。例如,如果一个基于Camel的应用使用了多个JMS queues,这时应用将会创建一个JmsComponent(实现了Component接口)类的实例,然后应用调用其createEndpoint()方法来创建一个EndPoint实例。实际上,应用级别的代码不会直接调用Component.createEndpoint()方法,而通常会调用CamelContext.getEndpoint()方法来代替,该方法内部,CamelContext对象查找到相应的Component然后调用它的createEndpoint()方法。例如下面的代码:
myCamelContext.getEndpoint("pop3://[email protected]?password=myPassword");
getEndpoint()方法的参数是一个URI,这个URI的前缀指明了这个组件的名称(name)。在内部,CamelContext对象拥有一个映射名称到组件的映射表。在上面的例子的URI中,CamelContext对象很可能将pop3前缀映射到一个MailComponent的实例。然后CamelContext对象调用这个MailComponent对象的createEndpoint("pop3://[email protected]?password=myPassword")方法。这个createEndpoint()方法将会把这个URI分隔成若干个部分然后使用这些部分来配置这个EndPoint对象。
在以前的段落中,提到了一个CamelContext对象拥有一个Component name到Component对象的映射。这就引出了一个问题,这张映射表如何来映射这些组件,有两种方式:
一是应用级别调用CamelContext.addComponent(String componentName, Component component)方法。下面这个例子将一个MailComponent对象用三个不同的名称注册在映射表中:
Component mailComponent = new org.apache.camel.component.mail.MailComponent(); myCamelContext.addComponent("pop3", mailComponent); myCamelContext.addComponent("imap", mailComponent); myCamelContext.addComponent("smtp", mailComponent);</span>
二是(更好)让CamelContext对象中的映射表延迟初始化,这种方式要开发者实现Component接口时遵循一定的约定。例如,假设你写了个名为com.example.myproject.FooComponent的类,而且你想让Camel自动以"foo"来进行识别。为了达到这样的目的你必须写一个叫"META-INF/services/org/apache/camel/component/foo"的Properties文件(没有.properties文件后缀),该Properties只有一个条目class,该条目的值就是该组件的全路径名:
class=com.example.myproject.FooComponent
如果你还让com.example.myproject.FooComponent以name "bar"识别,你还得写另外一个properties文件(文件名为bar)在相同的目录中,而且含有相同的内容。一旦你写了这些Properties文件,你创建的jar包中含有com.example.myproject.FooComponent以及相应的Properties文件,并且这个jar包加入到了CLASSPATH下。这样在应用级别就可以用代码在CamelContext对象上调用createEndpoint("foo:...")方法,Camel将在CLASSPATH中找到"foo"properties文件,获取该文件中class条目的值,然后使用反射技术创建指定类的实例。
Camel支持多种开箱即用通信技术,该开箱即用支持由多个实现了Component接口的类以及它们相应的properties文件来实现,以供CamelContext对象来构建这张named Component对象映射表。
在前面的部分我给出了一个如下例子,调用CamelContext.getEndpoint().
myCamelContext.getEndpoint("pop3://[email protected]?password=myPassword");
当我第一次给出这个例子时,我说getEndpoint()方法的参数是一个URI,我之所以那样说的原因是Camel的在线帮助文档和Camel的源代码都声称这是一个URI。而实际上,这个参数严格上来说是一个URL。这是因为当Camel从参数中抽取component name时,它第一步找":",这是一个简单算法。要理解其原因,就得回到Section4.4("The meaning of URL,URI,URN and IRI"),一个URI可能是一个URL或者是一个URN,考虑下面的getEndpoint()方法调用:
myCamelContext.getEndpoint("pop3:...");
myCamelContext.getEndpoint("jms:...");
myCamelContext.getEndpoint("urn:foo:...");
myCamelContext.getEndpoint("urn:bar:...");
在上面的例子中Camel标识这些组件用的名称分别是:"pop3","jms","urn"和"urn".如果后两个组件用"urn:foo"和"urn:bar"来标识可能会更贴切,实际上用的是"urn"和"urn"(只取第一个":"号前面部分作为Component name)。所以在实际应用中你必须用一个URL("<schema>:...形式")来标识一个endpoint而不是用一个URN("urn:<schema>:..."形式)。因为这里缺少对URN的更多支持所以getEndpoint()方法的参数是一个URL而不是所谓的URI。
Message and Exchange
Message接口是一个消息的抽象,比如说一个请求,一个回复或者一个例外消息。
Camel为每一个支持的通信技术提供了Message接口的相应实现类。例如,JmsMessage类提供了JMS-specific的Message接口实现。公共接口Message提供了getter与setter方法来访问message id,消息休和消息头字段。
Exchange接口是消息交换的抽象,一个请求消息和它相应的应答或者例外消息。在Camel技术中,请求,应答和例外消息分别被称为输入(in),输出(out),和错误(fault)消息。
Camel也为第一种支持的通信技术提供了Exchange接口的实现。例如,JmsExchange类提供了JMS-specific的Exchange接口实现。Exchange公共接口的API有很大的局限性,这是有意设计成这样的。因为每一个实现Exchange接口的实现类将会提供具体的技术相关的方法。
在应用级别程序员几乎不会直接访问Exchange接口(或者其实现类)。然而,在Cmael的很多类是泛型类are instantiated on(a class that implements)Exchange。这样做的原因是Exchange接口出现在了许多泛型的类和方法中。
Processor
Processor接口代表一个可以处理消息的类,其签名如下:
package org.apache.camel;
public interface Processor {
void process(Exchange exchange) throws Exception;
}
注意process()方法的参数是一个Exchange对象而不是一个Message对象。这样提供了扩展性。例如,一个实现类的process()方法初始可能会调用exchange.getIn()方法得到输入消息并且进行处理。如果在处理的过程中发生了错误,该方法可以调用exchange.setException()方法。一个应用级别的开发者可能会开发一个类实现Process接口来执行一个业务逻辑,然后在Camel类库中已经有很多类实现了Processor接口来支持EIP book中的设计模式。例如,ChoiceProcessor实现了消息的路由
,它使用一种级联的if-then-else语句来路由一个来自队列的消息到多个输出队列中的一个。另外一个例子是FilterProcessor类,它将会抛弃那些不满足条件的消息。
Routes, RouteBuilders and Java DSL
一个路由是消息从一个输入队列一步一步地,通过任意类型的决断移动到一个目的队列。Camel为应用提供了两种方式供开发者指定路由,一种是将路由信息配置在一个XML文件当中,另一种方式是Camel中所谓的Java DSL(domain-specific language)。
Introduction to Java DSL
对很多人来说"domain-specific language"词语意味着一个编译器或解释器可以处理一个包含了关键字和具体语义的输入文件到一个特定的域中。但这不是Camel所采用的方式。Camel文档中坚持用“Java DSL”代替“DSL”,但这也不能完全避免潜在的混淆。Camel的“Java DSL”是一个类库,可以以一种与DSL很类似的方式使用,除非它包含很少的java语法。你可以看下面的例子,后面的注释解释了这个例子的构成。
RouteBuilder builder = new RouteBuilder() { public void configure() { from("queue:a").filter(header("foo").isEqualTo("bar")).to("queue:b"); from("queue:c").choice() .when(header("foo").isEqualTo("bar")).to("queue:d") .when(header("foo").isEqualTo("cheese")).to("queue:e") .otherwise().to("queue:f"); } }; CamelContext myCamelContext = new DefaultCamelContext(); myCamelContext.addRoutes(builder);</span>
第一行创建了一个RouteBuilder的匿名子类对象,覆盖了configure()方法。
The CamelContext.addRoutes(RouterBuilder builder)方法中调用了builder.setContext(this),所以RouterBuilder对象知道它自己关联的CamelContext对象,然后调用builder.configure()方法,configure()方法体调用的例如from(),filter(),choice(),when()isEqualTo(),otherwise()和to(). RouteBuilder.from(String uri)方法调用CamelContext对象的getEndpoint(uri),并且与RouteBuilder对象一起查找指定的Endpoint,然后放置一个FromBuilder包装器到这个Endpoint。FromBuilder.filter(Predicate
predicate)方法为Predicate(条件)创建一个FilterProcessor对象,从header("foo").isEqualTo("bar")语句构建而来。这样上些操作递增地构造了一个Route对象并且将它添加进CamelContext。