1. 程式人生 > >Camel In Action 第七章 理解camel元件

Camel In Action 第七章 理解camel元件

第七章 理解camel元件 本章包括: camel元件預覽 使用file、database元件 JMS元件 CXF web service MINA元件 記憶體訊息 自動任務:Quartz 和 Timer元件 7.1 camel元件預覽 元件是Camel主要的擴充套件點。從Camel的第一個版本到Camel的當前版本,Camel的元件列表迅速增長,目前已有80多個元件,這些元件允許您整合不同的api、協議、資料格式等等,是你從繁重的編碼工作中解放出來,Camel到達的最初的目標:是系統整合更簡單。 那麼Camel的元件是什麼樣呢?如果把Camel的路由比作高速公路,那麼元件就類似公路上的坡道(上坡道、下坡道),一個路由中的訊息通過坡道旅行到另一個路由或者外部服務。圖7.1 元件使用CamelContext來建立端點(endpoint) 如果只看看Camel元件的API,那麼元件是非常簡單的,只是一個實現Component介面的類: public interface Component { Endpoint createEndpoint(String uri) throws Exception; CamelContext getCamelContext(); void setCamelContext(CamelContext context); } 一個元件的主要任務是:端點(endpoint)的工廠。為了完成這個任務,元件介面中有CamelContext的引用。CamelContext提供了Camel常用的功能,比如註冊元件、類載入、型別轉換。關係圖見圖7.1. 把一個元件新增到執行時的camel中有兩種方式:手工編寫程式碼新增到CamelContext中;自動載入。 7.1.1 手工新增元件 CamelContext context = new DefaultCamelContext(); context.addComponent("jms",JmsComponent.jmsComponentAutoAcknowledge(connectionFactory)); 上述程式碼中,你用JmsComponent.jmsComponentAutoAcknowledge方法建立了元件,註冊到CamelContext中,註冊的名字為jms。在路由中使用jms scheme可以引用到這個元件。 7.1.2 通過自動載入新增元件 自動載入的過程見圖7.2 自動載入是Camle中已有元件的註冊方式。為了載入新的元件,Camel會監控類路徑下的META-INF/services/org/ apache/camel/component目錄中的檔案。此目錄中的檔案描述了元件的名稱、元件類的全路徑名。 舉例:一個Bean元件,它對應META-INF/services/org/ apache/camel/component目錄中的名為bean的檔案,檔案內容: class=org.apache.camel.component.bean.BeanComponent 內容中的class屬性告訴Camel載入類org.apache.camel.component.bean.BeanComponent作為新元件,檔案的名稱bean作為元件名。 Camel中的大部分元件的java程式碼模組都與Camel的核心模組做了分離,因為他們常常依賴第三方的包,如果不分離,將會是Camel的核心包過度膨脹。例如:Atom元件依賴Apache Abdera的Atom專案,我們可能不想在每個專案中都依賴Apache Abdera的Atom專案,所以Atom元件包含在一個獨立於camel-core的模組中:camel-atom模組。 camel-core模組包含了13個非常有用的元件,見表7.2.
7.2 操作檔案(File元件和FTP元件) 

在系統集中過程中,經常要做的一種整合就是與檔案系統連線互動。你會發現這種情況比較奇怪,因為新系統通常提供不錯的web服務和其他遠端api作為整合點。整合的問題是,我們經常要處理舊的遺留系統,和基於檔案的整合是常見的。例如,您可能需要讀取由另一個應用程式建立的一個檔案,它可能是傳送一個命令,處理一個訂單,資料日誌記錄,或其他東西。這種資訊交換,如圖7.3所示,在EIP稱為檔案傳輸file transfer模式。 
基於檔案的整合非常普遍的另一個原因是,他們容易理解。即使是新手計算機使用者也瞭解檔案系統。儘管他們容易理解,基於檔案的整合是很難做到完全正確。開發人員通常都必須處理複雜的IO api,特定於平臺的檔案系統問題,併發訪問等。 
Camel對檔案系統互動提供了廣泛的支援。在本節中,我們將看看如何使用檔案元件讀取檔案並把它們寫到本地檔案系統。我們還將討論檔案處理的一些高階選項,討論如何使用FTP 元件訪問遠端檔案。 

7.2.1 使用File元件讀寫檔案 

使用URI的形式來配置File元件,常用的配置選項如表7.3所示,完整的配置選項參見文件:
http://camel.apache.org/file2.html


Camel如何讀取檔案 

public void configure() { 
from("file:data/inbox?noop=true").to("stream:out"); 

上述路由將從data/inbox目錄中讀取檔案,併發檔案內容列印到控制檯上(使用Stream元件向System.out流傳送訊息)。路由中的noop=true選項告訴Camel:保留原始檔案(不刪除原檔案);如果去掉noop選項,將會使用Camel的預設行為:把成功消費的原始檔案移到.camel目錄中,這個目錄名可以通過move配置選項來設定。如果想刪除原始檔,可以使用delete配置選項。 
預設情況,Camel將鎖定正在處理的檔案,知道路由處理完成。 

寫檔案 

public void configure() { 
from("stream:in?promptMessage=Enter something:").to("file:data/outbox"); 


上述路由使用Stream元件接收控制檯上的輸入。 
URI stream:in將指示Camel讀任何通過System.in輸入到控制檯上的內容,並建立一個訊息。promptMessage配置選項顯示一個輸入提示。 
URI file:data/outbox 指示Camel把訊息體寫到data/outbox目錄中的某個檔案中,如果某檔案不存在將會被建立。執行測試用例之後,你會發現生成了一個名稱像f6a3a5ee-536b-43c3-8307-1b96e1ae7778的檔案。這是因為你沒有顯示設定檔名,Camel選用了使用訊息ID作為檔名。 
可以使用fileName配置選項來為URI file:data/outbox設定檔名: 
public void configure() { 
from("stream:in?promptMessage=Enter something:") 
.to("file:data/outbox?fileName=prompt.txt"); 


此路由還有一個問題,預設情況下Camel會覆寫prompt.txt檔案的內容。在頻繁寫入的時候,你可以在每次建立新檔案,這樣就不會覆蓋原內容。在Camel中實現這一點,可以在檔名中使用表示式(使用表示式語言在檔名中設定當前日期時間資訊)。 
public void configure() { 
from("stream:in?promptMessage=Enter something:") 
.to("file:data/outbox?fileName=${date:now:yyyyMMdd-hh:mm:ss}.txt"); 


表示式:date:now返回當前日期,你可以使用java.text.SimpleDataFormat接受的任一格式化選項。 


7.2.2 使用FTP元件讀寫遠端檔案 

存取遠端伺服器上的檔案的常用方式是使用FTP,Camel支援三種形式的FTP: 

---Plain FTPmode transfer 
---SFTPfor secure transfer 
---FTPS (FTP Secure) for transfer with the Transport Layer Security (TLS) and 
Secure Sockets Layer (SSL) cryptographic protocols enabled 

FTP元件集成了File元件的所有特性和配置選項,並增加了許多新的選項,見表7.4,詳情見官方文件:
http://camel.apache.org/ftp2.html


因為FTP元件不是camel-core的一部分,需要新增第三方依賴包到專案中: 
<dependency> 
<groupId>org.apache.camel</groupId> 
<artifactId>camel-ftp</artifactId> 
<version>2.5.0</version> 
</dependency> 

我們使用Stream元件來演示存取遠端FTP伺服器上的檔案: 
<route> 
<from uri="stream:in?promptMessage=Enter something:" /> 
<to uri="ftp://rider:
[email protected]
:21000/data/outbox"/> 
</route> 
上述路由接收控制檯上輸入的內容,然後傳送到ftp伺服器上。 

7.3 非同步訊息(JMS元件) 

java訊息服務(JMS)是一種非常有用的整合技術。它促進鬆散耦合的應用程式設計,內建支援可靠的訊息傳遞,天生就是非同步的。 
Camel沒有為JMS提供provider,需要手工配置JMS provider(傳入connectionFactory例項): 
<bean id="jms" class="org.apache.camel.component.jms.JmsComponent"> 
<property name="connectionFactory"> 
<bean class="org.apache.activemq.ActiveMQConnectionFactory"> 
<property name="brokerURL" value="tcp://localhost:61616" /> 
</bean> 
</property> 
</bean> 

---傳入到ConnectionFactory中的tcp://localhost:61616 URI就是JMS provider。 
---本例中使用ActiveMQConnectionFactory,所以URI被ActiveMQ解析,URI告訴ActiveMQ使用TCP協議連線到broker:localhost:61616。 

預設的JMS ConnectionFactory沒有使用連線池技術,所以對於每一個訊息都會建立一個新的連線,連線到broker:localhost:61616。為了提高效能,ActiveMQ提供了ActiveMQ元件,此元件使用了連線池技術: 
<bean id="activemq" class="org.apache.activemq.camel.component.ActiveMQComponent"> 
<property name="brokerURL" value="tcp://localhost:61616" /> 
</bean> 

要使上述路由正常工作,需要引入activemq-camel模組: 
<dependency> 
<groupId>org.apache.activemq</groupId> 
<artifactId>activemq-camel</artifactId> 
<version>5.4.1</version> 
</dependency> 

Camel的JMS元件提供了大量的配置選項,大部分都只會用在特定的JMS只用場景。常用的配置選項見表7.5: 
為了在專案中使用Camel的JMS元件,你需要引入camel-jms模組和JMS provider的jar包到類路徑下: 
<dependency> 
<groupId>org.apache.camel</groupId> 
<artifactId>camel-jms</artifactId> 
<version>2.5.0</version> 
</dependency> 

7.3.1 傳送訊息 接收訊息 

圖7.4演示了一個例子:使用釋出/訂閱模式,在這個模式中,accounting和production這些監聽者可以訂閱主題xmlOrders,新的訂單會發布到主題xmlOrder上,此時,兩個監聽者都會收到訂單訊息的副本。 
在Camel中實現圖7.4中的例子,你需要配置兩個消費者,也就是說兩個路由: 
from("jms:topic:xmlOrders").to("jms:accounting"); 
from("jms:topic:xmlOrders").to("jms:production"); 
當一個訊息被髮送(釋出)到xmlOrders主題上,accounting 和production佇列都會收到訊息的副本。 

from("file:src/data?noop=true").to("jms:incomingOrders"); 
from("jms:incomingOrders") 
.choice() 
.when(header("CamelFileName").endsWith(".xml")) 
.to("jms:topic:xmlOrders") 
.when(header("CamelFileName").regex("^.*(csv|csl)$")) 
.to("jms:topic:csvOrders"); 
from("jms:topic:xmlOrders").to("jms:accounting"); 
from("jms:topic:xmlOrders").to("jms:production");  7.3.2 請求--響應型別的訊息 我們知道,JMS訊息預設是非同步的。客戶端傳送訊息到一個目的地之後,是不會等待訊息的響應的。但是很多時候,客戶端需要獲取所傳送訊息的響應。JMS支援這種型別的訊息,通過設定訊息頭部JMSReplyTo屬性,訊息接受者就會把響應傳送到JMSReplyTo屬性值上面,並且返回的訊息還會包含一個JMSCorrelationID屬性,在有多個訊息響應時,客戶端利用JMSCorrelationID來區分響應。訊息流程圖見圖7.5。 Camel負責這種風格的訊息,所以你不必建立特殊的應答佇列,回覆相關資訊,等等。通過把訊息的exchange模式改成InOut,Camel將啟用JMS的請求--響應(request-reply)模式。 為了演示這種情況,讓我們看看一個在騎士汽車零部件管理系統中的訂單驗證服務:檢查訂單,以確保訂單列出的是實際的產品。此服務暴露為一個名為validate的佇列: from("jms:validate").bean(ValidatorBean.class); ---當呼叫這個服務的時候,你只需要設定訊息的exchange模式為InOut,告訴Camel使用請求--響應型別的訊息。可以使用exchangePattern配置選項來設定: from("jms:incomingOrders").to("jms:validate?exchangePattern=InOut") 也可以在DSL中顯示宣告: from("jms:incomingOrders").inOut().to("jms:validate") 在inOut方法中,你也可以傳入端點URI引數: from("jms:incomingOrders").inOut("jms:validate") 設定InOut模式之後,Camel傳送訊息到validate佇列,然後用一個自動建立的臨時應答佇列來等待響應。當ValidatorBean返回一個訊息傳播回臨時應答佇列,之後路由繼續向下進行。 除了使用臨時建立的應答佇列,還可以顯示宣告應答佇列,使用JMSReplyTo頭部屬性或者在URI中使用replyTo配置選項。 單元測試一個請求--響應訊息,可以使用ProducerTemplate類的requestBody方法: Object result = template.requestBody("jms:incomingOrders", "<order name=\"motor\" amount=\"1\" customer=\"honda\"/>"); 7.3.3 訊息對映 在進行JMS訊息傳遞時,Camel隱藏了許多細節,這些細節一般你不需要關注。有一點需要知道:Camel將任意型別的訊息體和訊息頭部對映為特定的JMS型別。 訊息體對映 雖然Camel對訊息體中包含的資料型別沒有限制,但是JMS根據訊息體中資料型別的不同區分了訊息型別,見圖7.6 當一個exchange到達類似如下路由節點to("jms:jmsDestinationName")時,exchange將會轉換為5種訊息型別中的一種。轉換時,Camel會檢查訊息體的型別,進而決定轉換為何種訊息型別,接著建立相應的訊息型別,傳送到JMS目的地,表7.6顯示了body型別和JMS訊息型別之間的對映關係。這些換行由Camel完成。有時你可能需要保持JMS message完好無損。比如為了提高效能,不在每次都做訊息對映,意味著處理訊息會花費較少的時間;還有你儲存的物件型別在Camel專案的類路徑下不存在的時候,此時Camel進行發序列化時會報錯。此時你可以實現自己的訊息轉換器(org.springframework.jms.support.converter.MessageConverter介面);也可以設定URI配置選項mapJmsMessage為FALSE。 訊息頭對映 JMS的頭部比訊息體型別更加嚴格。在Camel中,一個header名稱可以為任意java字串;header型別可以為任意java物件。在向JMS端點發送或者接收訊息時,就有一些問題了。 JMS對訊息頭的限制: ---訊息頭的名稱都是以JMS開頭的保留字,你不可以使用這些訊息頭名稱; ---訊息頭名稱必須是合法的java標示符; ---訊息頭的值可以是原始型別及其包裝型別 為了處理這些限制,Camel做了很多事情: 1、使用者所設定的以JMS開頭的訊息頭名稱在傳送到JMS端點前被捨棄。Camel會試著將這些訊息頭名稱轉換為符合JMS規則的訊息頭名稱。任意一個點號(.)字元都會被”_DOT_“替換,任意一個連線符號(-)都會被“_HYPHEN_”替換。例如,訊息頭名稱為org.apache.camel.Test-Header將被轉換為org_DOT_apache_DOT_camel_DOT_Test_HYPHEN_Header。當Camel接收到JMS傳送的訊息,會相反方向轉換。 2、為了符合JMS規範,Camel會把非原始型別的訊息頭的捨棄,另外,Camel允許CharSequence, Date, BigDecimal, 和 BigInteger型別的訊息頭值,這些值將會轉換為符合JMS規範的String型別。 對你的JMS訊息傳遞應用程式Camel能做什麼,你現在應該有一個好的理解。 JMS訊息傳遞應用程式通常使用在一個組織內部。使用者在企業防火牆外很少傳送JMS訊息到內部系統。在外部可以使WebService技術。
7.4 Web service(CXF元件) 

你可能很難找到不使用某種型別的web服務現代企業專案。web服務是非常有用的分散式應用程式的整合技術。Web服務常與面向服務的體系結構(SOA)關聯,其中每個服務被定義為一個Web服務。 

可以把一個WebService看成網路上的一個API。這個API本身使用WSDL定義,WSDL檔案中聲明瞭可以呼叫的操作,輸入引數、輸出引數的型別等。WebService中的訊息通常是格式符合簡單物件訪問協議(SOAP)規範的XML。此外,這些訊息通過HTTP傳送。見圖7.7,web服務允許您編寫Java程式碼,通過網際網路使Java程式碼被呼叫。 
為了訪問和釋出web服務,Camel使用了Apache CXF。CXF是一個流行的webservice框架,支援大多數WebService標準。在這裡,我們關注的是使用JAX-WS API開發WebService。JAX-WS定義了許多註解,使WebService工具(如CXF)把一個POJO釋出為服務。 
WebService的開發有兩種方式: 
---協議優先 
使用WSDL定義WebService提供的服務和引數型別--這些通常被稱為WebService協議,與WebService互動,必須符合WebService協議。協議優先的意思是首先編寫WSDL檔案(手工編寫或者使用工具),然後根據wsdl生成java類(可使用CXF提供的工具實現這一步) 
---程式碼優先 
程式碼優先的意思是首先編寫java類。然後根據java類生成WSDL檔案。這是一種最簡單的開發WebService的方式。 

7.4.1 配置CXF 

有兩種方式來配置CXF元件URI:使用bean配置、直接使用URI配置。 

直接使用URI配置,模式如下: 
cxf://anAddress[?options] 

Address代表一個URL:http://rider.com:9999/order,options代表的是配置選項,配置選項見表7.8。 

使用bean配置 

在Spring中配置CXF endpoint bean,比URI配置方式具有更強大的功能:比如可以配置CXF攔截器、CXF匯流排、JAX-WS處理器等,配置形式如下: 
cxf:bean:beanName 

beanName是在Spring配置檔案中定義的CXF端點的ID的名稱,支援表7.8中的所有配置選項:程式碼列表7.2 
<beans xmlns="http://www.springframework.org/schema/beans
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance
xmlns:cxf="http://camel.apache.org/schema/cxf
xsi:schemaLocation=" 
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://camel.apache.org/schema/cxf
http://camel.apache.org/schema/cxf/camel-cxf.xsd"> 
<import resource="classpath:META-INF/cxf/cxf.xml"/> 
<import resource="classpath:META-INF/cxf/ 
? cxf-extension-soap.xml"/> 
<import resource="classpath:META-INF/cxf/ 
? cxf-extension-http-jetty.xml"/> 
<cxf:cxfEndpoint 
id="orderEndpoint" 
address="http://localhost:9000/order/" 
serviceClass="camelinaction.order.OrderEndpoint"/> 
</beans> 

在程式碼列表7.2中配置了一個CXF endpoint:orderEndpoint,你可以使用這個endpoint作為生產者或者消費者: 
cxf:bean:orderEndpoint 

在作為生產者和消費者使用的時候,兩者之間有一個顯著的差異。 
在一個WebService上下文中,camel中的一個生產者producer呼叫一個遠端的WebService。這個WebService可以使用camel提供,也可以使用其他框架提供。 
在camel中呼叫一個WebService,使用類似java DSL中的to方法: 
...to("cxf:bean:orderEndpoint"); 

消費者consumer把整個路由公開為一個WebService。這是一個重要的概念。Camel路由可以是一個非常複雜的處理過程,擁有多個分支,多個處理節點,但是呼叫者只看到一個web服務(擁有輸入引數和輸出響應)。 

假設你有以下路由,包含多個步驟: 
from("jms:myQueue"). 
to("complex step 1"). 
... 
to("complex step N"); 

為了把上述路由公開到web上面,可以在路由開頭新增一個CXF端點: 
from("cxf:bean:myCXFEndpointBean"). 
to("complex step 1"). 
... 
to("complex step N"); 
這樣,當由myCXFEndpointBean配置的WebService被呼叫時,整個路由都會被呼叫。 

如果你以前使用過SOA或使用web服務,您可能會被Camel中的消費者迷惑。在web服務的世界中,消費者通常是一個客戶端呼叫一個遠端服務。在Camel中,消費者是一個伺服器,所以定義發生了逆轉! 

7.4.2 使用協議優先的開始方式 

這種開發方式,你首先建立一個WSDL文件,接著利用WebService工具生成java程式碼,過程如圖7.8所示: 

為一個特定的web服務建立WSDL協議並非一項簡單的任務。開始前最好確定好方法,型別和引數等內容。例項見程式碼列表7.3. 
如清單7.3中可以看到,一個WSDL協議比較複雜!從頭編寫這樣的文件將會很難。通常,一個開始的好方法是使用一個嚮導或GUI工具。例如,在Eclipse中您可以使用File > New >其他> > WSDL Web服務嚮導來生成一個基於幾個選項的WSDL檔案框架。調整這個框架檔案比從零開始容易得多。CXF框架本身也提供了類似的命令列工具。 
WSDL 1.1 與 2.0版本 

如果你使用WSDL檔案之前,您可能已經注意到我們使用的版本的WSDL規範清單7.3所示。我們使用WSDL 1.1版本因為CXF的當前版本只支援1.1。這也是最常見的WSDL版本,您將看到使用。WSDL 2.0大幅改變,到目前為止很少有web服務工具(如CXF)支援它。 

選擇所呼叫WebService的某個操作 

在訊息傳送到CXF 端點(endpoint)之前,可以通過設定訊息的operationName頭部來選擇WebService中的某個操作,例如: 
<route> 
<from uri="direct:startOrder" /> 
<setHeader headerName="operationName"> 
<constant>order</constant> 
</setHeader> 
<to uri="cxf:bean:orderEndpoint"/> 
</route> 

7.4.3 程式碼優先的方式 
程式碼優先的開發方式通常被認為是一種比協議優先更容易的開發方式。程式碼優先開發方式中,首先編寫java程式碼,使用JAX-WS註解,接著生成WSDL協議檔案,過程如圖7.9. 
使用這種開發方式,你需要弄明白你需要的方法。型別、引數,定義一個介面,代表這個web服務: 
@WebService 
public interface OrderEndpoint { 
String order(String partName, int amount, String customerName); 

JAX-WS的javax.jws.WebService註解告訴CXF:這個介面是一個web服務。有許多註釋允許您調整生成的WSDL,但在許多情況下預設就好。為了使用這個介面,需要配置CXF端點: 
<cxf:cxfEndpoint id="orderEndpoint" 
address="http://localhost:9000/order/" 
serviceClass="camelinaction.order.OrderEndpoint"/> 
在路由中使用: 
<from uri="cxf:bean:orderEndpoint" />


7.6 與資料庫互動(JDBC和JPA元件) 

Camel提供了5個元件供訪問資料庫: 
1、JDBC元件---在Camel路由中訪問JDBC API; 
2、SQL元件---支援在元件的URI中直接寫sql語句,進行簡單的查詢; 
3、JPA元件---使用JPA框架對映java物件到關係資料庫中; 
4、Hibernate元件---使用hibernate元件序列化java物件。由於license的不相容,這個元件沒有隨camel專案一起釋出。你可以在Camel擴充套件專案中找到:http://code.google.com/p/camel-extra
5、ibatis元件---允許您將Java物件對映到關係資料庫。 

7.6.1 使用JDBC元件存取資料 
JDBC API定了java客戶端如何與資料庫互動。使用這個元件,需要新增camel-jdbc模組: 
<dependency> 
<groupId>org.apache.camel</groupId> 
<artifactId>camel-jdbc</artifactId> 
<version>2.5.0</version> 
</dependency> 

元件的URI配置選項見表7.10 
JDBC元件指向一個已載入的javax.sql.DataSource,URI語法: 
jdbc:dataSourceName[?options] 

URI指定後,元件就可以使用了。但您可能想知道實際的SQL語句如何指定。 
JDBC是一個動態元件,它幾乎不會向目的地傳送一個訊息,而是把訊息體的內容作為命令來執行。這樣,這些命令就是SQL語句。在企業整合模式中,這一類訊息被稱為命令訊息。因為JDBC端點接收一個命令,所以它作為消費者是沒有意義的,因而這個元件不能在from語句中使用。當然,你可以使用select語句作為命令訊息來提取資料,這種情況下,查詢結果將被作為出棧訊息新增到exchange中。 

為了演示的SQL命令訊息的概念,讓我們重溫路由騎士汽車零部件系統中的那個訂單路由。對於會計部門,當一個訂單到達JMS訊息佇列,會計的業務應用程式不能使用這些資料,因為會計程式只從資料庫中獲取資料。所以,任何到達的訂單需要儲存到關係資料庫中。使用Camel,一種解決方案如圖7.13,圖中關鍵點是使用bean把到達的訊息體轉成SQL語句。這是一種為JDBC元件提供命令訊息的常見方式。路由如下: 
from("jms:accounting") 
.to("bean:orderToSql") 
.to("jdbc:dataSource"); 
這裡有兩個地方需要進一步的解釋。1、配置的JDBC端點用於載入名為dataSource的javax.sql.DataSource型別的資料來源。名為orderToSql的bean端點用於將到達的訊息轉換為一個sql語句。 orderToSql bean的相關程式碼見程式碼列表7.8: public class OrderToSqlBean { public String toSql(@XPath("order/@name") String name, @XPath("order/@amount") int amount, @XPath("order/@customer") String customer) { StringBuilder sb = new StringBuilder(); sb.append("insert into incoming_orders "); sb.append("(part_name, quantity, customer) values ("); sb.append("'").append(name).append("', "); sb.append("'").append(amount).append("', "); sb.append("'").append(customer).append("') "); return sb.toString(); } } 程式碼中使用XPath解析了到達的訂單訊息,訊息體的格式類似: <?xml version="1.0" encoding="UTF-8"?> <order name="motor" amount="1" customer="honda"/> 轉成的sql語句如下: insert into incoming_orders (part_name, quantity, customer) values ('motor', '1', 'honda') 接著SQL語句成為傳給JDBC端點的訊息的訊息體。這樣,你就向資料庫中插入了一條新資料。此時不要期望有任何結果返回。但是,Camel會設定CamelJdbcUpdateCount(頭部屬性)的值為資料庫更新的條數。如果在執行sql語句時報錯,會丟擲SQLException異常。 如果你運行了一個查詢語句,Camel將以ArrayList<HashMap<String, Object>>物件的形式返回結果集。HashMap中的每一個key代表一個列名,value代表列值。Camel也會設定CamelJdbcRowCount(頭部屬性)的值為結果集中的記錄條數。 7.6.2 使用JPA元件持久化物件 騎士汽車零部件系統又收到了新的需求:使用POJO模型代替XML格式的訂單訊息。 第一步要做的就是把到達的XML訊息轉換為等價的POJO。此外,會計部門的訂單持久化路由需要更新,要能夠處理POJO型別的訊息體。你可以使用程式碼列表7.8中的方式提取XML訊息中的資訊,進而持久化。但是,對於持久化物件,有一個更好的解決方式。 Java永續性體系結構(JPA)是一個物件-關係對映(ORM)產品的包裝層,如Hibernate,OpenJPA,TopLink等。這些ORM產品實現了java物件與關係資料庫表中的資料的對映。 使用JPA元件,需要新增camel-jpa模組到專案中: <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-jpa</artifactId> <version>2.5.0</version> </dependency> 新增ORM產品和資料庫使用jar包到專案中,這個例子使用OpenJPA產品和HyperSQL資料庫,所以需要新增以下依賴: <dependency> <groupId>org.apache.openjpa</groupId> <artifactId>openjpa-persistence-jdbc</artifactId> <version>1.2.2</version> </dependency> <dependency> <groupId>hsqldb</groupId> <artifactId>hsqldb</artifactId> <version>1.8.0.7</version> </dependency> JPA元件的URI配置選項見表7.11: 在JPA中,需要對POJO類使用javax.persistence.Entity註解。術語entity是借用關係資料庫術語,大致翻譯為:面向物件程式設計的一個物件。也就是說如果想使用JPA持久化一個POJO型別的訂單物件,就要使用這個註解。訂單POJO程式碼列表7.9: @Entity public class PurchaseOrder implements Serializable { private String name; private double amount; private String customer; public PurchaseOrder() { } public double getAmount() { return amount; } public void setAmount(double amount) { this.amount = amount; } public String getName() { return name; } public void setName(String name) { this.name = name; } public void setCustomer(String customer) { this.customer = customer; } public String getCustomer() { return customer; } } POJO物件可以使用到達的XML訂單訊息物件轉換而成。為了測試,你可以使用producer模板傳送一個新的PurchaseOrder到會計JMS佇列: PurchaseOrder purchaseOrder = new PurchaseOrder(); purchaseOrder.setName("motor"); purchaseOrder.setAmount(1); purchaseOrder.setCustomer("honda"); template.sendBody("jms:accounting", purchaseOrder); 對應的路由如下: from("jms:accounting").to("jpa:camelinaction.PurchaseOrder"); 現在,路由已配置好,現在需要配置ORM工具。這是在Camel中使用JPA的時候必須配置的。 主要配置兩個地方:把ORM工具的實體管理器連到Camel的JPA元件、配置ORM工具連線到您的資料庫。為了方便演示,我們使用Apache OpenJPA這個ORM框架,但是你可以使用JPA允許的任何ORM框架。設定 OpenJPA實體管理器所需的bean,如清單7.10所示。 Spring bean配置檔案清單7.10所示 ---建立JpaComponent,宣告所使用的實體管理器entityManagerFactory ---實體管理器建立的時候,設定persistenceUnitName為Camel,這個持久化單元定義了什麼實體類應該被持久化,以及底層資料庫的連線資訊。在JPA,這個配置儲存在類路徑下META-INF目錄中的persistence.xml檔案中,見程式碼清單7.11. ---實體管理器連線上OpenJPA和HyperSQL資料庫 ---它還設定實體管理器,以便它可以參與事務 在程式碼清單7.11中,有兩個地方要注意:第一,需要持久化哪些類要定義在這裡;可以有多個class元素;第二如果你需要連線到其他資料庫,你需要在這裡改配置資訊; 7.7 記憶體中的訊息傳遞(Direct、SEDA和VM元件) 

Camel在核心包中提供了三個元件用於處理記憶體訊息。對於同步訊息的處理,可以使用Direct元件;對於非同步訊息的處理,可以使用SEDA和VM元件。SEDA和VM元件的區別在於:SEDA元件用於在CamelContext範圍內進行訊息傳遞,VM元件的範圍更廣一些,可以用於在JVM範圍內進行訊息傳遞。如果你的一個應用中載入了兩個CamelContext,你可以使用VM元件在兩個CamelContext直接傳送訊息。 

7.7.1 Direct元件的同步訊息傳遞 

Direct元件是最簡單的一個元件,但是它是非常有用的。Direct端點URI看起來像這樣: 
direct:endpointName 
沒有您可以指定或後端配置的選項,只有一個端點的名稱。可是這能給你帶來什麼呢?Direct元件允許您對一個路由進行同步呼叫,或者相反,將路由開放為同步服務。例如:你有一個路由,第一個端點是Direct元件 
from("direct:startOrder") 
.to("cxf:bean:orderEndpoint"); 

向direct:startOrder傳送一個訊息,將會呼叫一個WebService(由CXF元件定義),如下程式碼,使用ProducerTemplate傳送一個訊息到Direct元件: 
String reply = 
template.requestBody("direct:startOrder", params, String.class); 
ProducerTemplate將會在後臺建立一個Producer(生產者),傳送到direct:startOrder端點。在大多數其他元件,在生產者和消費者之間發生的一些處理。例如,在一個JMS元件,訊息將被髮送到一個佇列JMS代理(JMSbroker)。對於Direct元件,生產者直接呼叫消費者,直接,意味著生產者中有一個方法呼叫了消費者,這種簡單性和最小的開銷讓Direct元件成為開啟路由或者同步分解路由成多個碎片路由的一個好方法。但是,其同步的特性並不適合所有的應用程式。如果需要非同步操作,你需要SEDA或VM元件。 

7.7.2 使用SEDA和VM進行非同步訊息傳遞 

當你看到早些時候在本章中討論JMS元件的時候(7.3節),利用訊息佇列作為傳送訊息的一種手段有許多好處。您還了解了如何將一個路由應用程式被分解成許多的邏輯塊(碎片路由),並使用JMS佇列作為橋樑連線起來。但為此在一個主機上使用JMS應用程式為一些用例添加了不必要的複雜性。 
如果你想獲得非同步訊息傳遞的好處而不關心JMS規範一致性或JMS提供的內建的可靠性,你可能要考慮一個記憶體訊息的解決方案。拋棄規範一致性和任何通訊和訊息代理(可以是昂貴的),使用記憶體訊息可能是更快的解決方案。請注意,此時沒有訊息持久化到磁碟,像JMS那樣,所以在應用崩潰的時候有丟失訊息的風險,你的應用程式應該能夠接受這種風險。 
Camel提供了兩種記憶體中的佇列元件:SEDA和 VM。他們都在表7.12中列出了配置選項。 
SEDA元件在Camel應用中的最常見應用是連線過個路由組成一個路由應用程式。例如,複習7.3.1中給出的示例,您使用的JMS主題傳送副本傳入會計和生產部門。在這種情況下,您使用JMS佇列把路由連線到了一起,因為會計佇列或者生產部門佇列不在同一個主機上。如果在同一個主機上,可以有一種更高效的解決方案,見圖7.14: 

在同一個CamelContext中任何JMS訊息傳遞都可以使用SEDA來切換。你仍然需要為會計和生產使用JMS佇列,因為它們是物理上獨立的部門。從圖中可以看出SEDA佇列替換了JMS的xmlOrders主題。為了使SEDA佇列更像一個JMS主題(使用釋出/訂閱訊息模型),你需要設定URI的配置選項multipleConsumers為true。程式碼見程式碼列表7.12。 
這個例子和7.3.1節中的示例一樣,除了它使用SEDA端點,而不是JMS。另一個重要的細節在這個清單中的SEDA端點的uri,在消費者和生產者的場景中重用需要彼此完全相同。 

7.8 自動化的任務(Timer和Quartz元件) 
通常在企業專案需要安排任務在指定的時間或定期發生。Camel的Timer和Quartz元件支援這種服務。Timer元件是用於簡單重複的任務,但當你需要更多的控制時,就必須使用Quartz元件。 

7.8.1 Timer元件 

TImer元件在Camel的核心包中,使用了JRE中內建的timer機制,定期產生訊息。這個元件只支援消費,因為傳送一個計時器並沒有真正意義。URI配置選項見表格7.3 

作為一個例子,讓我們實現一個功能,每隔2秒向控制檯列印一條訊息。路由如下: 
from("timer://myTimer?period=2000") 
.setBody().simple("Current time is ${header.firedTime}") 
.to("stream:out"); 

URI配置使用java.util.Timer建立了一個名為myTimerand的定時器,每兩秒執行一次。 
當定時器觸發時,Camel會建立一個exchange,訊息體為空,傳送到路由上。在這種情況下,你使用表示式設定了訊息體。頭部的firedTime屬性值由Timer元件設定,更多的元件資訊參見文件:http://camel.apache.org/timer.html

注意:定時器物件和相應的執行緒可以共享。如果你在另一條路由中使用相同的計時器的名字,它將重用相同的定時器物件。 


7.8.2 企業級排程(Quartz元件) 

除了具備Timer元件已有的功能,Quartz元件可以讓你擁有更多控制。你也可以利用Quartz元件的許多其他企業特性。相關特性見網站 http://www.quartz-scheduler.org/ 常用的URI配置選項見表7.14。 

使用Quartz元件,需要引入第三方包: 
<dependency> 
<groupId>org.apache.camel</groupId> 
<artifactId>camel-quartz</artifactId> 
<version>2.5.0</version> 
</dependency>

相關推薦

Camel In Action 理解camel元件

第七章 理解camel元件 本章包括: camel元件預覽 使用file、database元件 JMS元件 CXF web service MINA元件 記憶體訊息 自動任務:Quartz 和 Timer元件 7.1 camel元件預覽 元件是Camel主要的擴充套件

Camel In Action camel單元測試

6.2.2 使用Mock元件進行單元測試  為了學習如何使用Mock元件,我們使用下面這個路由:  from("jms:topic:quote").to("mock:quote");  這個路由消費來自JMS主題quote的訊息,並把訊息路由到一個名為quote的mock端點。  mock端點在Camel中的

(譯)Netty In Action—事件迴圈和執行緒模型

請尊重勞動成果,未經本人允許,拒絕轉載,謝謝! 這章包涵以下內容 - 執行緒模型概覽 - 事件迴圈概念和實現 - 任務排程 - 實現細節 簡單地說,執行緒模型指定了OS、程式語言、框架或應用程式的上下文中的執行緒管理的關鍵方面。執行緒創造的方式和時間明顯對於應用程

R in action -- 1

以及 span spa rar clas start 數據分析工具 blog 有效 R in action -- 第1章 R語言介紹 R:一個數據分析和圖形顯示的程序設計環境 R 環境 R 環境由一組數據操作,計算和圖形展示的工具構成。相對其他同類軟件,它的 特色在於:

Thinking in Java學習筆記----復用類

obj class 都是 一個 per private gpo toon on() 復用代碼,即使用已經開發並調試好的類。組合和繼承是兩種實現方法。 組合語法:   在新類中創建現有類的對象。該方法只是復用了現有代碼的功能,而非它的形式。   組合的例子隨處可見,這裏不舉例

(譯)Netty In Action—channelhandler 和 channelpipeline

請尊重勞動成果,未經本人允許,拒絕轉載,謝謝! 這一章涵蓋以下內容: - ChannelHandler 和 ChannelPipeline的APIs介紹 - 資源洩漏檢測 - 異常處理 在前一章節你已經學習了ByteBuf——Netty的資料容器。隨著在這一章研究Ne

(譯)Netty In Action—單元測試

這章涵蓋一下內容 - 單元測試 - EmbeddedChannel概覽 - 使用EmbeddedChannel測試ChannelHandlers ChannelHandlers是Netty應用程式的重要因素,所以徹底測試它們應該是開發過程的標準部分。最佳實踐要求你進行測試不僅要證明

(譯)Netty In Action—Bootstrapping

這章涵蓋以下內容: - Bootstrapping客戶端和服務端 - 從Channel內bootstraping客戶端 - 增加ChannelHandlers - 使用ChannelOptions和屬性 已經深入學習了ChannelPipelines、ChannelHandler

Thinking in Java 3-1

關系 適合 重載 中文翻譯 system rbo 繼承 clean house Thinking in Java第七章研讀3-1總結 問題引入:如何復用代碼 1.新的類是由現有類的對象所組成,方法稱為組合。(該方法只是復用了現有程序代碼的功能,而非他的形式) 2.按照

Camel in action 第一譯文(請勿用於商業用途)

1 First steps 1.Camel 介紹 2.Camel路由 Firststeps Apache camel 是 一個開源的一體化框架,其目的是使一體化系統更容易。本書的第一章節我們將介紹camel及展示它適合大企事業單位的軟體。你將會學習到關於cam

深入.NET平臺和C#編程筆記 深入理解多態

定義 方式 目前 rtu ride 筆記 總結 理解 hello 第七章 深入理解多態 1.裏氏替換原則: 在一個軟件系統中,如果子類出現在父類出現的位置,而整個軟件功能又沒有影響,那麽咱們稱為裏氏替換。 父類變量指向子類對象!! 2.Is 和as Is

深入理解JVM虛擬機器讀書筆記【】虛擬機器類載入機制

7.1 概述 7.2 類載入的時機 7.3 類載入的過程 7.3.1 載入 7.3.2 驗證 1.檔案格式驗證 2.元資料驗證 3.位元組碼驗證

深入理解計算機系統--課後作業

對於問題一,輸出結果如下圖所示:   兩個都是弱定義值,所以連結器隨機選擇一個,而這時在c2.c中,g的值為37(強定義),故c1.c的g為37,呼叫函式f,g變為38 當執行第二條語句時,結果如圖:   多次定義變數g,程式在連結的時候報錯!!! 對

深入理解計算機系統----連結

原文連結 https://www.jianshu.com/p/7f27c0316355 目  錄 連結是將各種不同檔案的程式碼和資料部分收集(符號解析和重定位)起來並組合成一個單一檔案的過程。本章節我們將要學習連結器工作的詳細原理。通過對這一方面知識的學習,將有助於理解一些危

Thinking in Java 四版完整版 練習題 複用類

Thinking in Java 第四版完整版 第七章練習題,記錄一下(jdk1.8.0) 1. /** * 練習1:建立一個簡單的類。在第二個類中,將一個引用定義為第一個類 * 的物件。運用惰性初始化來例項化這個物件。 * @author admin11 *

《深入理解計算機系統》讀書筆記—— 連結

 連結 有兩個c檔案: /* main.c */ void swap(); int buf[2] = {1, 2}; int main() { swap(); return 0; } /* swap.c */ extern int buf[]; int *b

讀書筆記:深入理解計算機系統

int printf(const char * format,...); int global_init_var = 84; int global_uninit_var; void fun1(int i) { printf("%d\n",i); } int main

深入理解Magento – – 自定義Magento系統配置

Magento的資源配置系統允許你直接拷貝安裝指令碼和升級指令碼到伺服器上,Magento會根據當前模組的版本自動執行相應的指令碼。這樣你就只需 要維護一份資料庫遷移指令碼。我們先來看看“core_resource”資料表 mysql> select code,version from core_re

[深入理解Java虛擬機器] 類載入的過程

接下來我們詳細講解一下Java虛擬機器中類載入的全過程,也就是載入、驗證、準備、解析和初始化這5個階段所執行的具體動作。 載入 “載入”是“類載入”(Class Loading)過程的一個階段,希望讀者沒有混淆這兩個看起來很相似的名詞。在載入階段,虛擬機

【譯文連載】 理解Istio服務網格( 安全)

全書目錄 第一章 概述 第二章 安裝 第三章 流控 第四章 服務彈性 第五章 混沌測試 第六章 可觀測性   本文目錄 第7章 安全 7.1 身份認證     7.1.1 Kubernetes上的Istio的身份