新闻资讯

新闻资讯

实时气象信息

气象信息

今日明日油价

明日油价

航班起降信息

航班信息

TTC动态时刻表

TTC时刻表

超市特价信息

超市特价

商场特价信息

商场特价

中外食谱

中外食谱

技术IT

技术IT

中外名画

中外名画

超强游戏

超强游戏

幽默笑话

幽默笑话

漫画卡通

漫画卡通

JBoss EJB3(Message Driven Beans)備忘記

主编发表于2010年3月14日 · 发表评论
归档于: J2EE, 技术IT 

原本來自: 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]:
/*——————- HelloWorldMdbBean.java —————–*/
package ejb3.joeyta.mdb;

import javax.annotation.PreDestroy;
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 {

public HelloWorldMdbBean(){
System.out.println(”Local Server initialized on HelloWorldMdbBean…”);
}

public void onMessage(Message msg) { // 這是 MessageListener 裡必須實作的 method,
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();
}
}
}

@PreDestroy // 為 callback method, 當 instance 消除前呼叫這函數
public void remove() {
System.out.println(”Local Server HelloWorldMdbBean destroyed.”);
}
}
/*——————- HelloWorldMdbBean.java —————–*/

[5] 建立 Client 端 Consumer:
/*——————- HelloWorldConsumerClient.java —————–*/
package ejb3.joeyta.clients;

import java.util.Properties;

import javax.annotation.PreDestroy;
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;

public class HelloWorldConsumerClient implements MessageListener {

public static void main(String[] args) throws Exception {
new HelloWorldConsumerClient();
}

public static InitialContext getInitialContext()
throws javax.naming.NamingException {

Properties p = new Properties();
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);
}

public HelloWorldConsumerClient() throws Exception {
InitialContext jndiContext = getInitialContext(); // 初始化 JNDI

ConnectionFactory factory = (ConnectionFactory) jndiContext.lookup(”ConnectionFactory”);
// 1: 尋找 connection factory

Connection connect = factory.createConnection();
// 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 裡

System.out.println(”Remote Client listening for messages on HelloWorldConsumerClient…”);
connect.start(); // 開始連結
}

public void onMessage(Message msg) {
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();
}
}
}

@PreDestroy // 為 callback method, 當 instance 消除前呼叫這函數
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 java.util.Properties;

import javax.jms.Connection;
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 class HelloWorldProducerClient {
public static InitialContext getInitialContext()
throws javax.naming.NamingException {

Properties p = new Properties();
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);
}

public static void main(String[] args) throws Exception {
InitialContext ctx = getInitialContext(); // 初始化 JNDI

ConnectionFactory factory = (ConnectionFactory) ctx.lookup(”ConnectionFactory”);
// 1: 尋找 connection factory

Connection connection = factory.createConnection();
// 2: 以 connection factory 建立 JMD connection

Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
// 3: 以 connection 建立 session, false 表示不使用 transaction,
// Session.AUTO_ACKNOWLEDGE 為設定怎樣確定接收 message, 這 client 為發送, 故不重要

Topic topic = (Topic) ctx.lookup(”topic/jms/HelloWorldMdbTopic”);
// 4: 尋找 destination, 如果是 queue, 這裡只需將 topic 改成 queue, Topic 改成 Queue.
// Message Driven Bean 當然也要修改.

MessageProducer producer = session.createProducer(topic);
// 5: 建立 message producer
TextMessage msg = session.createTextMessage();
msg.setText(”Joeyta try HelloWorld Message Driven Beans.”);
producer.send(msg);
// 6: 上面三句建立及發送 message

producer.close(); // 關閉 producer
System.out.println(”Message produced.”);
}
}
/*——————- HelloWorldProducerClient.java —————–*/

項目結構如下圖所示:

[7] 使用 ANT 建立 EJB-JAR 並執行 Client 程式:

執行 ANT Task:
點選 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.

JBoss Console 的輸出結果為:
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.

如下圖所示:

run.mdb.consumer.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.

如下圖所示:

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;

@Resource(mappedName=”jms/HelloWorldMdbTopic”)
private Topic topic;

@Remove
public void checkout() throws IncompleteConversationalState {
try {
Connection connect = topicFactory.createConnection( );
Session session = connect.createSession(true,0);
MessageProducer producer = session.createProducer(topic);

TextMessage msg = session.createTextMessage();
msg.setText(”Joeyta try HelloWorld Message Driven Beans.”);
producer.send(msg);
connect.close( );
} catch(Exception e) {
throw new EJBException(e);
}
}

這次 JBoss EJB3 Message Driven Beans 教學己到了終點.

正籌備 Transactions, Security, Timers, Clustering, Web Services, Interceptors(AOP-like) 教學.

由於 Java Persistence 內容太多了, 所以放在最後.
不過我相信有了上面這個起點, 學下去也很容易.

如果想更了解 EJB3 , 這裡有免費的 Mastering EJB 3.0. 下載.
http://www.theserverside.com/tt/books/wiley/masteringEJB3/index.tss

J2EE tutorial
http://java.sun.com/j2ee/1.4/docs/tutorial/doc/

Sun EJB Documentation page:
http://java.sun.com/products/ejb/docs.html

JSR 220:
http://www.jcp.org/en/jsr/detail?id=220

【“身边的加国”专栏作家风采】

本文所属类别其他文章

J2EE:

技术IT:


发表评论

您希望以注册用户身份发表评论吗?登录 »

                - 或者 -

非注册用户身份发表评论 »