Apache Geronimo 通信基礎 —— 開發、部署和測試(下)
消息驅動 bean()
既然您已經實現了 PurchaseOrderEJB,接下來就可以實現 PurchaseOrderMDB 了。與 EJB 不同,MDB 是 JMS 偵聽器,偵聽 JMS 主題 或隊列上的 JMS 消息。根據 EJB 2.1 規范,MDB 不僅限於 JMS 偵聽器,而是可實現任意自定義偵聽器接口。在本系列的第 3 部分中, 您將再次回顧這一點,並修改 PurchaseOrderMDB,以便使用自定義偵聽器接口,將其與 JCA 資源適配器相集成。
在本教程中,您將實現 PurchaseOrderMDB,它偵聽 JMS 主題(POTopic)接收到的帶有采購請求的 JMS 消息。接收到采購請求消息時 ,它將調用 PurchaseOrderEJB 來創建新采購訂單。
實現 PurchaseOrderMDB
不同於實體 bean,MDB 不必實現遠程接口或主接口,而是要實現 MessageDrivenBean 和 MessageListener 接口 —— 因而必須實現 onMessage() 方法。清單 9 給出了 PurchaseOrderMDB 的代碼片段。
PurchaseOrderMDB 的源文件(.java)可在 $part1.home/src/examples/po/mdb 目錄下找到。
清單 9. MDB 源代碼
public class PurchaseOrderMDB
implements MessageDrivenBean, MessageListener {
private MessageDrivenContext ctx = null;
public void setMessageDrivenContext(MessageDrivenContext mdCtx)
throws EJBException {
ctx = mdCtx;
}
public void onMessage(Message msg) {
if (msg instance of ObjectMessage) {
Serializable bean = ((ObjectMessage) msg).getObject();
poBean = (PurchaseOrderBean) bean;
addPurchaseOrder(poBean);
}
}// end onMessage
在 PurchaseOrderMDB 的 onMessage() 方法中,您調用了 PurchaseOrderEJB 的 addPurchaseOrder() 方法在數據庫表中創建新采購 訂單行。
Geronimo 服務器的 JNDI 屬性如下:
java.naming.factory.initial=org.openejb.client.RemoteInitialContextFactory
(4201 是默認端口)
java.naming.provider.url=localhost:4201
java.naming.security.principal=system(默認用戶名)
java.naming.security.credentials=manager(默認口令)
清單 10. 查找實體 bean 並創建采購訂單
private void addPurchaseOrder (PurchaseOrderBean poBean) {
try {
//EJB JNDI Properties for Geronimo Server
Hashtable env = new Hashtable();
env.put("java.naming.factory.initial",
"org.openejb.client.RemoteInitialContextFactory");
env.put("java.naming.provider.url", "localhost:4201");
env.put("java.naming.security.principal", "system");
env.put("java.naming.security.credentials", "manager");
// create a new InitialContext
InitialContext ctx = new InitialContext(env);
// Lookup Purchase Order Entity Bean
Object o = ctx.lookup("PurchaseOrderEJB");
RemotePurchaseOrderHome home =
(RemotePurchaseOrderHome) PortableRemoteObject
.narrow(o, RemotePurchaseOrderHome.class);
// Create a new Purchase Order
RemotePurchaseOrder po = home. create ( poBean.getPurchaseOrderNum(),
poBean.getItem(),
poBean.getDescription(),
poBean.getUnitPrice(),
poBean.getQuantity(),
poBean.getRequestorEmail());
log.info("Purchase Order Number # :" + po.getPurchaseOrderNum());
} catch (Exception e) {
e.printStackTrace();
log.severe(""+e);
}}}
如 清單 10 所示,為了在數據表中創建一個采購訂單行,您在 JNDI 服務器中查找 PurchaseOrderEJB,並調用主接口上的 create() 方法。
定義 MDB 描述符
既然已經實現了 PurchaseOrderMDB,就該在描述符中定義 MDB 了。您不必為 MDB 定義單獨的描述符。它們是在與 EJB 相同的描述符 中定義的,也就是 ejb-jar.xml(參見 清單 11)和 openejb-jar.xml(參見 清單 12)。
清單 11. 定義 MDB 的 ejb-jar.xml 代碼片段
<message-driven>
<ejb-name>PurchaseOrderMDB</ejb-name>
<ejb-class>examples.po.mdb.PurchaseOrderMDB</ejb-class>
<transaction-type>Container</transaction-type>
<message-destination-type>javax.jms.Topic
</message-destination-type>
</message-driven>
在 openejb-jar.xml 中,您定義了 MDB 應偵聽的主題/隊列 JNDI 名(POTopic)(參見 清單 12)。您還要定義 MDB 在 ejb-ref 部 分中調用的 EJB。
清單 12. 定義 MDB 的 openejb-jar.xml 代碼片段
<message-driven>
<ejb-name>PurchaseOrderMDB</ejb-name>
<resource-adapter>
<target-name>
geronimo.server:J2EEApplication=null,J2EEServer=geronimo,
JCAResource=geronimo/activemq/1.0/car,j2eeType=JCAResourceAdapter,
name=ActiveMQ RA
</target-name>
</resource-adapter>
<activation-config>
<activation-config-property>
<activation-config-property-name>
destination
</activation-config-property-name>
<activation-config-property-value>
POTopic
</activation-config-property-value>
</activation-config-property>
<activation-config-property>
<activation-config-property-name>
destinationType
</activation-config-property-name>
<activation-config-property-value>
javax.jms.Topic
</activation-config-property-value>
</activation-config-property>
</activation-config>
<!--This is a reference to PurchaseOrderEJB CMP deployed in
Geronimo Server. -->
<ejb-ref>
<ref-name>ejb/PurchaseOrderEJB</ref-name>
<ejb-link>PurchaseOrderEJB</ejb-link>
</ejb-ref>
</message-driven>
PurchaseOrderMDB 在 addPurchaseOrder() 方法中調用 PurchaseOrderEJB,因此 EJB 是在 <ejb-ref> 部分中定義的。
Geronimo 服務器配置
既然已經實現了 PurchaseOrderMDB 和 PurchaseOrderEJB,現在就可以配置 Geronimo 服務器以便部署。首先使用 Geronimo 服務器 中的 Derby 為 PurchaseOrderEJB 配置數據庫表 PURCHASEORDER。
Geronimo 中不需要任何 JMS 配置,因為 JMS 主題/隊列是在部署期間由服務器動態創建的。Geronimo 為 JMS 主題或隊列使用 openejb-jar.xml 描述符中的 JNDI 名。
數據庫配置
運行 $GERONIMO_HOME/bin/startup.bat 啟動您的 Geronimo 服務器。如果您未在系統屬性中設置 JAVA_HOME,可在 $GERONIMO_HOME/bin/setjavaenv.bat 中進行設置。為 Geronimo 使用默認用戶名 system 和口令 manager。
在 http://localhost:8080/console 處可使用 Geronimo 管理控制台,在左窗格中的 Misc > Embedded DB 菜單下單擊 DB Manager。
創建 ExampleDatabase,如 圖 3 所示。
圖 3. 在 Geronimo 服務器中創建數據庫
成功創建了 ExampleDatabase 之後,您將會看到它出現在數據庫列表中,如 圖 4 所示。
圖 4. 數據庫創建成功
現在創建表 PURCHASEORDER,方法是在 SQL Command 文本區域中輸入 SQL 腳本(參見 清單 13)。務必確保首先在 Use DB 下拉列表 中選中 ExampleDatabase,然後再單擊 Run SQL 按鈕(參見 圖 5)。
清單 13. 創建表的 SQL 腳本
CREATE TABLE PURCHASEORDER (
PURCHASEORDERNUM VARCHAR(30) PRIMARY KEY,
ITEM VARCHAR(30) NOT NULL,
DESCRIPTION VARCHAR(255),
UNITPRICE INTEGER,
QUANTITY INTEGER,
REQUESTOREMAIL VARCHAR(30) NOT NULL
)
圖 5. 在 Geronimo 服務器中創建數據庫
成功創建了 PURCHASEORDER 表之後,您將看到它出現在 DB Viewer 中,如 圖 6 所示。
圖 6. 表創建成功
數據庫池配置
現在您可以使用 http://localhost:8080/console 處的 Geronimo 管理控制台在 Geronimo 中創建數據庫池 PurchaseOrderDataSource。
在左窗格中選擇 Database Pools,然後選擇 Using the Geronimo database pool wizard,如 圖 7 所示。完成後續步驟,如 圖 8 和 圖 9 所示。
圖 7. 創建數據庫池
鍵入 PurchaseOrderDataSource 作為數據庫池名、Derby Embedded 作為類型,如 圖 8 所示。
圖 8. 選擇數據庫類型
從 Driver JAR 下拉列表中選擇 org.apache.derby/derby/10.1.1.0/jar。您可將用戶名和口令字段留空。選擇 ExampleDatabase 作 為數據庫名(參見 圖 9)。
圖 9. 選擇數據庫驅動和數據庫名
只要將未填充的字段留空,即可使用默認值,單擊 Test Connection 按鈕來測試連接(參見 圖 10)。
圖 10. 測試數據庫連接池
現在,您應看到測試結果,表示您已連接到 Derby,如 圖 11 所示。
圖 11. 成功測試連接
選擇 Deploy 按鈕後,您應看到數據庫池已成功創建(參見 圖 12)。
圖 12. 數據庫池創建成功
部署
既然已完成了數據庫配置,那麼也就准備好在 Geronimo 中部署包含 EJB 和 MDB 的應用程序了(po-ejb.jar)。po-ejb.jar 文件位 於 $part1.home/deploy 目錄下。
您可以使用命令行,也可以使用基於控制台的部署,分別介紹如下。
命令行部署
下載文件 part1.zip 的 $part1.home 目錄中有用於部署(deploy.cmd)和取消部署(undeploy.cmd)的腳本。
務必在使用前修改命令文件中的 JAVA_HOME 和 GERONIMO_HOME。
控制台部署
使用 http://localhost:8080/console 處的 Geronimo 管理控制台部署 po-ejb.jar 文件,如 圖 13 所示。
圖 13. 部署新應用程序
成功部署應用程序後,您將接收到如 圖 14 所示的消息。
圖 14. 部署成功
測試
最後,您可創建一個客戶機或測試程序來測試您的應用程序。
創建客戶機
您的客戶機程序(參見 清單 14)將向 POTopic 發布一條帶有采購請求信息的 JMS 消息。由於 PurchaseOrderMDB 偵聽 POTopic,服 務器將調用 onMessage() 方法,此方法又會調用 PurchaseOrderEJB 在數據庫中創建采購訂單。
客戶機 PurchaseOrderPublisher.java 位於 $part1.home/src/examples/po/test 目錄中。
清單 14. 發布 JMS 消息的客戶機程序
public class PurchaseOrderPublisher {
public static void main(String a[]) throws Exception {
Logger log = Logger.getLogger("PurchaseOrderPublisher");
javax.jms.TopicConnection conn = null;
javax.jms.TopicPublisher publisher = null;
javax.jms.TopicSession session = null;
try {
// JMS JNDI Properties for Apache Geronimo Server
java.util.Hashtable env = new java.util.Hashtable();
env.put("java.naming.factory.initial",
"org.activemq.jndi.ActiveMQInitialContextFactory");
env.put("java.naming.provider.url",
"tcp://localhost:61616");
env.put("topic.POTopic", "POTopic");
env.put("connectionFactoryNames", "POConnectionFactory");
// create a new initial context with jndi properties
javax.naming.Context ctx =
new javax.naming.InitialContext(env);
// lookup the connection factory
javax.jms.TopicConnectionFactory factory =
(javax.jms.TopicConnectionFactory) ctx
.lookup("POConnectionFactory");
// create a new TopicConnection
conn = factory.createTopicConnection();
javax.jms.Topic mytopic =
(javax.jms.Topic) ctx.lookup("POTopic");
// create a new TopicSession
session = conn.createTopicSession(false,
javax.jms.Session.AUTO_ACKNOWLEDGE);
//create a Topic Publisher to publish messages to 'POTopic'
publisher = session.createPublisher(mytopic);
// start the topic connection
conn.start();
// create a new purchase order with test data
PurchaseOrderBean poBean = new PurchaseOrderBean();
Random random = new Random();
String poNum = "PO" + random.nextInt();
poBean.setPurchaseOrderNum(poNum);
poBean.setItem("Pens");
poBean.setDescription("Need pens for Marketing dept.");
poBean.setUnitPrice(new Integer(2));
poBean.setQuantity(new Integer(100));
poBean.setRequestorEmail("[email protected]");
// create a jms message
javax.jms.ObjectMessage poMsg =
session.createObjectMessage(poBean);
// publish the jms message to 'POTopic'
publisher.publish(poMsg);
} catch (Exception e) {
log.severe("Error Occurred: " + e);
} finally {
// release all jms resources
if (publisher != null)
publisher.close();
if (session != null)
session.close();
if (conn != null)
conn.close();
}
}// end main
}// end class
運行測試
下載的 part1.zip 文件中提供了一個腳本文件(參見 下載 部分),修改 $part1.home/runtester.cmd 中的 JAVA_HOME、J2EE_JAR 和 GERONIMO_HOME(參見 圖 15)。
圖 15. 測試應用程序
現在您應看到 Geronimo 控制台中顯示出一些日志記錄,如 圖 16 所示。
圖 16. Geronimo 服務器日志
如果 PURCHASEORDER 數據庫中添加了一條新記錄,則本測試將被視為成功。
結果
現在檢查數據庫,看看 PURCHASEORDER 表中是否有記錄項,如 圖 17 所示。
圖 17. 檢查數據庫表中是否有新記錄項
太棒了!您已經完成了本系列教程第 1 部分的學習。
在第 2 部分中,您將開發一個電子郵件應用程序,此應用程序將在 Apache James 中部署。
結束語
您已成功完成了這個共分三部分的系列教程的第 1 部分。您創建了一個 MDB 和一個帶有 CMP 的實體 bean。這些 bean 協同工作,向 系統中添加采購訂單。
請繼續關注第 2 部分,您將使用 Apache James 郵件應用程序平台來構建一個電子郵件應用程序;另外還有第 3 部分,在第 3 部分 中您要將這篇教程中創建的兩個 bean 以及第 2 部分中創建的電子郵件應用程序聯系在一起,完成整個應用程序。
本文配套源碼