当前页面: 开发资料首页 → J2EE 专题 → JBoss EJB3(Message Driven Beans)備忘記
摘要: 從前由於不同廠家的 MOM(Message-oriented middleware) 系統有自己一套的 API, 這阻礙了不同的系統間 messaging system 不能跨平台, JMS (Java Message Service)的出現就是解決這個問題.
原本來自: http://blog.matrix.org.cn/page/joeyta?entry=jboss_ejb3_message_driven_beans
第一編介紹如何安裝 JBoss 及建立第一個 Stateless Session Beans HelloWorld:
http://blog.matrix.org.cn/page/joeyta?entry=jboss_ejb3_helloworld_%E5%82%99%E5%BF%98%E8%A8%98
第二編介紹 Stateful Session Beans:
http://blog.matrix.org.cn/page/joeyta?entry=jboss_ejb3_stateful_session_beans
第三編介紹 Entity Beans:
http://blog.matrix.org.cn/page/joeyta?entry=jboss_ejb3_entity_beans_%E5%82%99%E5%BF%98%E8%A8%98
前面 EJB remote interfaces 的例子使用了 RMI-IIOP 遠端協定.
RMI-IIOP (Remote Method Invocation over the Internet Inter-ORB Protocol) 有下列缺點:
(1) 典型的 RMI-IIOP 客戶端每次請求必須等候系統回應.
(2) RMI-IIOP 客戶端與系統過於偶合, 這使得客戶端難於與系統分離.
(3) 當 RMI-IIOP 客戶端呼叫系統時, 這時系統或網路發生故障, 所有資料可能流失, 客戶沒有得到預期的執行結果.
(4) RMI-IIOP 限制了在一定的時間內每個客戶端只能訪問單一的系統, 並沒有提供多數的客戶廣播事件給多數的系統.
EJB 的 messaging 解決以上所有問題, 這是一種輕量級傳輸訊息的實作,
保證了接收者能夠接收到發送者發送的信息.
從前由於不同廠家的 MOM(Message-oriented middleware) 系統有自己一套的 API,
這阻礙了不同的系統間 messaging system 不能跨平台,
JMS (Java Message Service)的出現就是解決這個問題.
JMS 為 messaging 的標準, JMS 分為兩部份, 第一部份為傳送及接收訊息的 API,
第二部份則為 SPI (Service Provider Interface), 這嵌入於 JMS providers,
JMS providers 知道怎樣與 MOM 系統溝通, JMS 確保了只需要學習一次便能應用於各種不同的 MOM 系統.
Messaging 可分為兩類:
發佈 / 訂購 [Publish / Subscribe]: 多個發送者將不同的 messages 發送到 middleware,
middleware 將這些 messages 發送到不同的訂閱者, 當全部發送完成後刪除這些 messages.
這種形式為可 多發送 及 每個訊息可有多位接收者.
點對點 [Point-to-point]:發送者將 message 發送給 middleware,
middleware 將這 message 發送給接收者, 然後取消這 message.
這種形式為可 多發送, 但每個訊息只能有一個接收者.
EJB 的 message driven bean 可以接收 JMS messages 及其他種類的 messages.
這裡並沒有對 Message driven beans 作太多的說明, 有興趣的可參閱 Mastering EJB3.
下面的例子實作了 Publish / Subscribe 的 internal 及 external 的 message driven beans.
開始備忘記:
[1] Eclipse 啟動 JBoss Server
[2] Eclipse 建立 HelloWorldMdbEJB3 Project
[3] 建立 JBoss MBean 定義檔
[4] 建立 Message Driven Beans [即 Server 端 Consumer]
[5] 建立 Client 端 Consumer
[6] 建立 Client 端 Producer
[7] 使用 ANT 建立 EJB-JAR 並執行 Client 程式
[1] Eclipse 啟動 JBoss Server:
Eclipse: Windows -> Show View -> Other
> JBoss-IDE -> Server Configuration 就會顯示 JBoss Server Configuration console
然後 right client 它按 start , JBoss 就會啟動
[2] Eclipse 建立 HelloWorldMdbEJB3 Project:
Eclipse: File -> New -> Other -> EJB 3.0 -> EJB 3.0 Project
Project Name: HelloWorldMdbEJB3 -> Next
選擇上一編已建立的 JBoss 4.0.x: jboss_configuration [default](not running)
打開後右鍵點選 JBoss 4.0.x -> new
然後按 Finish. HelloWorldMdbEJB3 project 就建立了
[3] 建立 JBoss MBean 定義檔: [4] 建立 Message Driven Beans [即 Server 端 Consumer]: import javax.annotation.PreDestroy; public HelloWorldMdbBean(){ public void onMessage(Message msg) { // 這是 MessageListener 裡必須實作的 method, @PreDestroy // 為 callback method, 當 instance 消除前呼叫這函數 import java.util.Properties; import javax.annotation.PreDestroy; public class HelloWorldConsumerClient implements MessageListener { public static void main(String[] args) throws Exception { public static InitialContext getInitialContext() Properties p = new Properties(); public HelloWorldConsumerClient() throws Exception { ConnectionFactory factory = (ConnectionFactory) jndiContext.lookup("ConnectionFactory"); Connection connect = factory.createConnection(); System.out.println("Remote Client listening for messages on HelloWorldConsumerClient..."); public void onMessage(Message msg) { @PreDestroy // 為 callback method, 當 instance 消除前呼叫這函數 import java.util.Properties; import javax.jms.Connection; public class HelloWorldProducerClient { Properties p = new Properties(); public static void main(String[] args) throws Exception { Connection connection = factory.createConnection(); Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE); Topic topic = (Topic) ctx.lookup("topic/jms/HelloWorldMdbTopic"); MessageProducer producer = session.createProducer(topic); producer.close(); // 關閉 producer 項目結構如下圖所示: 執行 ANT Task: JBoss Console 的輸出結果為: 如下圖所示: run.mdb.consumer.client Console 的輸出結果為: 如下圖所示: @Resource(mappedName="jms/HelloWorldMdbTopic") @Remove TextMessage msg = session.createTextMessage(); 這次 JBoss EJB3 Message Driven Beans 教學己到了終點. 由於 Java Persistence 內容太多了, 所以放在最後. J2EE tutorial Sun EJB Documentation page: JSR 220:
/*------------------- HelloWorldMdbBean.java -----------------*/
package ejb3.joeyta.mdb;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
@MessageDriven(activationConfig = {
@ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Topic"),
@ActivationConfigProperty(propertyName = "destination", propertyValue = "topic/jms/HelloWorldMdbTopic")
})
// MessageDriven 定義這 class 為 Mesasge Driven Beans, 必須繼承 MessageListener
// destinationType 定義使用 javax.jms.Topic, 如果是 queue 則使用 javax.jms.Queue
// destination 定義 目的地 是 topic/jms/HelloWorldMdbTopic
public class HelloWorldMdbBean implements MessageListener {
System.out.println("Local Server initialized on HelloWorldMdbBean...");
}
if (msg instanceof TextMessage) {
TextMessage tm = (TextMessage) msg; // 這裡將 Message 轉換成 TextMessage
try {
String text = tm.getText();
System.out.println("Local Server HelloWorldMdbBean received message : " + text);
} catch (JMSException e) {
e.printStackTrace();
}
}
}
public void remove() {
System.out.println("Local Server HelloWorldMdbBean destroyed.");
}
}
/*------------------- HelloWorldMdbBean.java -----------------*/
[5] 建立 Client 端 Consumer:
/*------------------- HelloWorldConsumerClient.java -----------------*/
package ejb3.joeyta.clients;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;
import javax.naming.InitialContext;
new HelloWorldConsumerClient();
}
throws javax.naming.NamingException {
p.put(InitialContext.INITIAL_CONTEXT_FACTORY,
"org.jnp.interfaces.NamingContextFactory");
p.put(InitialContext.URL_PKG_PREFIXES,
" org.jboss.naming:org.jnp.interfaces");
p.put(InitialContext.PROVIDER_URL, "jnp://localhost:1099");
return new javax.naming.InitialContext(p);
}
InitialContext jndiContext = getInitialContext(); // 初始化 JNDI
// 1: 尋找 connection factory
// 2: 以 connection factory 建立 JMD connection
Session session = connect.createSession(false, Session.AUTO_ACKNOWLEDGE);
// 3: 以 connection 建立 session, false 表示不使用 transaction,
// Session.AUTO_ACKNOWLEDGE 為設定怎樣確定接收 message,
Topic topic = (Topic) jndiContext.lookup("topic/jms/HelloWorldMdbTopic");
// 4: 尋找 destination, 如果是 queue, 這裡只需將 topic 改成 queue, Topic 改成 Queue.
MessageConsumer consumer = session.createConsumer(topic);
// 5: 建立 message consumer
consumer.setMessageListener(this);
// 將這個 Class 加入到 MessageListener 裡
connect.start(); // 開始連結
}
if (msg instanceof TextMessage) {
TextMessage tm = (TextMessage) msg; // 這裡將 Message 轉換成 TextMessage
try {
String text = tm.getText();
System.out.println("Remote Client HelloWorldConsumerClient received message : " + text);
} catch (JMSException e) {
e.printStackTrace();
}
}
}
public void remove() {
System.out.println("Remote Client HelloWorldConsumerClient destroyed.");
}
}
/*------------------- HelloWorldConsumerClient.java -----------------*/
由於這次實作使用 Topic,
故 Remote Client 及 Local server 端均可接收 message.
如果使用的是 Queue, 則最後註冊 Listener 才能收到 message.
[6] 建立 Client 端 Producer:
/*------------------- HelloWorldProducerClient.java -----------------*/
package ejb3.joeyta.clients;
import javax.jms.ConnectionFactory;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;
import javax.naming.InitialContext;
public static InitialContext getInitialContext()
throws javax.naming.NamingException {
p.put(InitialContext.INITIAL_CONTEXT_FACTORY,
"org.jnp.interfaces.NamingContextFactory");
p.put(InitialContext.URL_PKG_PREFIXES,
" org.jboss.naming:org.jnp.interfaces");
p.put(InitialContext.PROVIDER_URL, "jnp://localhost:1099");
return new javax.naming.InitialContext(p);
}
InitialContext ctx = getInitialContext(); // 初始化 JNDI
ConnectionFactory factory = (ConnectionFactory) ctx.lookup("ConnectionFactory");
// 1: 尋找 connection factory
// 2: 以 connection factory 建立 JMD connection
// 3: 以 connection 建立 session, false 表示不使用 transaction,
// Session.AUTO_ACKNOWLEDGE 為設定怎樣確定接收 message, 這 client 為發送, 故不重要
// 4: 尋找 destination, 如果是 queue, 這裡只需將 topic 改成 queue, Topic 改成 Queue.
// Message Driven Bean 當然也要修改.
// 5: 建立 message producer
TextMessage msg = session.createTextMessage();
msg.setText("Joeyta try HelloWorld Message Driven Beans.");
producer.send(msg);
// 6: 上面三句建立及發送 message
System.out.println("Message produced.");
}
}
/*------------------- HelloWorldProducerClient.java -----------------*/
[7] 使用 ANT 建立 EJB-JAR 並執行 Client 程式:
點選 build -> Run As -> 3. Ant Build ->> copy.mq.service.xml
點選 build -> Run As -> 3. Ant Build ->> ejbjar
點選 build -> Run As -> 3. Ant Build ->> run.mdb.consumer.client
點選 build -> Run As -> 3. Ant Build ->> run.mdb.producer.client
這裡必須順序執行 ANT 裡的Task.
05:04:19,242 INFO [jms/HelloWorldMdbTopic] Bound to JNDI name: topic/jms/HelloWorldMdbTopic
05:05:15,256 INFO [Ejb3Deployment] EJB3 deployment time took: 297
05:05:15,443 INFO JmxKernelAbstraction installing MBean: jboss.j2ee:jar=HelloWorldMdb.jar,name=HelloWorldMdbBean,service=EJB3 with dependencies:
05:05:15,912 INFO EJBContainer STARTED EJB: ejb3.joeyta.mdb.HelloWorldMdbBean ejbName: HelloWorldMdbBean
05:05:16,224 INFO [EJB3Deployer] Deployed: file:/D:/jboss/server/default/deploy/HelloWorldMdb.jar
05:06:16,987 INFO STDOUT Local Server initialized on HelloWorldMdbBean...
05:06:17,190 INFO STDOUT Local Server HelloWorldMdbBean received message : Joeyta try HelloWorld Message Driven Beans.
Buildfile: D:\eclipse_wtp\workspace\HelloWorldMdbEJB3\build.xml
run.mdb.consumer.client:
[java] log4j:WARN No appenders could be found for logger (org.jboss.mq.referenceable.SpyConnectionFactoryObjectFactory).
[java] log4j:WARN Please initialize the log4j system properly.
[java] Remote Client listening for messages on HelloWorldConsumerClient...
[java] Remote Client HelloWorldConsumerClient received message : Joeyta try HelloWorld Message Driven Beans.
如下圖所示:
run.mdb.producer.client Console 的輸出結果為:
Buildfile: D:\eclipse_wtp\workspace\HelloWorldMdbEJB3\build.xml
run.mdb.consumer.client:
[java] log4j:WARN No appenders could be found for logger (org.jboss.mq.referenceable.SpyConnectionFactoryObjectFactory).
[java] log4j:WARN Please initialize the log4j system properly.
[java] Remote Client listening for messages on HelloWorldConsumerClient...
[java] Remote Client HelloWorldConsumerClient received message : Joeyta try HelloWorld Message Driven Beans.
這裡為了簡化 Message Driven Beans 備忘記,
故沒有延續前編的 Shopping Cart Entity Beans 備忘記,
才把它獨立建立 HelloWorld Message Driven Beans Project,
如需與 Shopping Cart 合備,
只需在 Entity Beans 備忘記 ShoppingCartBean.java 裡加入
@Resource(mappedName="ConnectionFactory")
private ConnectionFactory connectionFactory;
private Topic topic;
public void checkout() throws IncompleteConversationalState {
try {
Connection connect = topicFactory.createConnection( );
Session session = connect.createSession(true,0);
MessageProducer producer = session.createProducer(topic);
msg.setText("Joeyta try HelloWorld Message Driven Beans.");
producer.send(msg);
connect.close( );
} catch(Exception e) {
throw new EJBException(e);
}
}
正籌備 Transactions, Security, Timers, Clustering, Web Services, Interceptors(AOP-like) 教學.
不過我相信有了上面這個起點, 學下去也很容易.
如果想更了解 EJB3 , 這裡有免費的 Mastering EJB 3.0. 下載.
http://www.theserverside.com/tt/books/wiley/masteringEJB3/index.tss
http://java.sun.com/j2ee/1.4/docs/tutorial/doc/
http://java.sun.com/products/ejb/docs.html
http://www.jcp.org/en/jsr/detail?id=220
↑返回目录
前一篇: JBoss jBPM(Workflow Management Engine)備忘記
后一篇: JAVAEE 常见性能问题解决手册