Apache Geronimo 通訊基礎 — 開發、部署和測試 (下)
既然您已經實作了 PurchaseOrderEJB,接下來就可以實作 PurchaseOrderMDB 了。與 EJB 不同,MDB 是 JMS 偵聽器,偵聽 JMS 主題或佇列上的 JMS 訊息。根據 EJB 2.1 規格,MDB 不僅限於 JMS 偵聽器,而是可實作任意自訂偵聽器介面。在本系列的第 3 部分中,您將再次回顧這一點,並修改 PurchaseOrderMDB,以便使用自訂偵聽器介面,將其與 JCA 資源適配器相整合。
在本教學中,您將實作 PurchaseOrderMDB,它偵聽 JMS 主題 (POTopic) 接收到的帶有採購請求的 JMS 訊息。接收到採購請求訊息時,它將呼叫 PurchaseOrderEJB 來建立新採購訂單。
不同於實體 Bean,MDB 不必實作遠端介面或主介面,而是要實作 MessageDrivenBean 和 MessageListener 介面——因而必須實作 onMessage() 方法。程式碼片段 9 給出了 PurchaseOrderMDB 的程式碼片段。
PurchaseOrderMDB 的原始檔 (.java) 可在 $part1.home/src/examples/po/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 instanceof ObjectMessage) {
Serializable bean = ((ObjectMessage) msg).getObject();
poBean = (PurchaseOrderBean) bean;
addPurchaseOrder(poBean);
}
}
}
在 PurchaseOrderMDB 的 onMessage() 方法中,您呼叫了 PurchaseOrderEJB 的 addPurchaseOrder() 方法在資料庫表中建立新採購訂單行。
Geronimo 伺服器的 JNDI 屬性如下:
java.naming.factory.initial=org.openejb.client.RemoteInitialContextFactory
java.naming.provider.url=localhost:4201 (4201 是預設連接埠)
java.naming.security.principal=system (預設使用者名稱)
java.naming.security.credentials=manager (預設密碼)
程式碼片段 10. 查找實體 Bean 並建立採購訂單
private void addPurchaseOrder(PurchaseOrderBean poBean) {
try {
// Geronimo 伺服器的 EJB JNDI 屬性
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");
// 建立新的 InitialContext
InitialContext ctx = new InitialContext(env);
// 查找 Purchase Order 實體 Bean
Object o = ctx.lookup("PurchaseOrderEJB");
RemotePurchaseOrderHome home = (RemotePurchaseOrderHome) PortableRemoteObject.narrow(o, RemotePurchaseOrderHome.class);
// 建立新的採購訂單
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() 方法。
既然已經實作了 PurchaseOrderMDB,就該在描述符中定義 MDB 了。您不必為 MDB 定義單獨的描述符。它們是在與 EJB 相同的描述符中定義的,也就是 ejb-jar.xml (程式碼片段 11) 和 openejb-jar.xml (程式碼片段 12)。
<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。
<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>
<!-- 這是對 Geronimo 伺服器中部署的 PurchaseOrderEJB CMP 的引用 -->
<ejb-ref>
<ref-name>ejb/PurchaseOrderEJB</ref-name>
<ejb-link>PurchaseOrderEJB</ejb-link>
</ejb-ref>
</message-driven>
PurchaseOrderMDB 在 addPurchaseOrder() 方法中呼叫 PurchaseOrderEJB,因此 EJB 是在 <ejb-ref> 部分中定義的。
既然已經實作了 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。
在 https://localhost:8080/console 可使用 Geronimo 管理主控台,在左窗格中的 Misc > Embedded DB 選單下點選 DB Manager。
建立 ExampleDatabase,如圖 3 所示。

成功建立 ExampleDatabase 後,您將會看到它出現在資料庫列表中,如圖 4 所示。

現在建立表 PURCHASEORDER,方法是在 SQL Command 文字區域中輸入 SQL 腳本 (程式碼片段 13)。務必確保首先在 Use DB 下拉清單中選中 ExampleDatabase,然後再點選 Run SQL 按鈕 (圖 5)。
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
)

成功建立 PURCHASEORDER 表後,您將看到它出現在 DB Viewer 中,如圖 6 所示。

現在您可以使用 https://localhost:8080/console 的 Geronimo 管理主控台在 Geronimo 中建立資料庫池 PurchaseOrderDataSource。
在左窗格中選擇 Database Pools,然後選擇 Using the Geronimo database pool wizard,如圖 7 所示。完成後續步驟,如圖 8 和 9 所示。

輸入 PurchaseOrderDataSource 作為資料庫池名、Derby Embedded 作為類型,如圖 8 所示。

從 Driver JAR 下拉清單中選擇 org.apache.derby/derby/10.1.1.0/jar。您可將使用者名稱和密碼欄位留空。選擇 ExampleDatabase 作為資料庫名 (圖 9)。

只要將未填寫的欄位留空,即可使用預設值,點選 Test Connection 按鈕來測試連接 (圖 10)。

現在,您應看到測試結果,表示您已連接到 Derby,如圖 11 所示。

選擇 Deploy 按鈕後,您應看到資料庫池已成功建立 (圖 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。
使用 https://localhost:8080/console 的 Geronimo 管理主控台部署 po-ejb.jar 檔案,如圖 13 所示。

成功部署應用程式後,您將接收到如圖 14 所示的訊息。

最後,您可建立一個客戶端或測試程式來測試您的應用程式。
您的客戶端程式 (程式碼片段 14) 將向 POTopic 發布一條帶有採購請求資訊的 JMS 訊息。由於 PurchaseOrderMDB 偵聽 POTopic,伺服器將呼叫 onMessage() 方法,此方法又會呼叫 PurchaseOrderEJB 在資料庫中建立採購訂單。
客戶端 PurchaseOrderPublisher.java 位於 $part1.home/src/examples/po/test 目錄中。
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 {
// Apache Geronimo 伺服器的 JMS JNDI 屬性
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");
// 使用 jndi 屬性建立新的 initial context
javax.naming.Context ctx = new javax.naming.InitialContext(env);
// 查找連接工廠
javax.jms.TopicConnectionFactory factory = (javax.jms.TopicConnectionFactory) ctx.lookup("POConnectionFactory");
// 建立新的 TopicConnection
conn = factory.createTopicConnection();
javax.jms.Topic mytopic = (javax.jms.Topic) ctx.lookup("POTopic");
// 建立新的 TopicSession
session = conn.createTopicSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE);
// 建立 Topic Publisher 發布訊息到 'POTopic'
publisher = session.createPublisher(mytopic);
// 啟動主題連接
conn.start();
// 建立帶有測試資料的新採購訂單
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("user1@localhost.com");
// 建立 JMS 訊息
javax.jms.ObjectMessage poMsg = session.createObjectMessage(poBean);
// 發布 JMS 訊息到 'POTopic'
publisher.publish(poMsg);
} catch (Exception e) {
log.severe("Error Occurred: " + e);
} finally {
// 釋放所有 JMS 資源
if (publisher != null) publisher.close();
if (session != null) session.close();
if (conn != null) conn.close();
}
}
}
下載的 part1.zip 檔案中提供了一個腳本檔案 (請參閱下載部分),修改 $part1.home/runtester.cmd 中的 JAVA_HOME、J2EE_JAR 和 GERONIMO_HOME (圖 15)。

現在您應看到 Geronimo 主控台中顯示出一些日誌記錄,如圖 16 所示。

如果 PURCHASEORDER 資料庫中新增了一條新記錄,則本測試將被視為成功。
現在檢查資料庫,看看 PURCHASEORDER 表中是否有記錄項,如圖 17 所示。

太棒了!您已經完成了本系列教學第 1 部分的學習。
在第 2 部分中,您將開發一個電子郵件應用程式,此應用程式將在 Apache James 中部署。
您已成功完成了這個共分三部分的系列教學的第 1 部分。您建立了一個 MDB 和一個帶有 CMP 的實體 Bean。這些 Bean 協同工作,向系統中新增採購訂單。
請繼續關注第 2 部分,您將使用 Apache James 郵件應用程式平台來建構一個電子郵件應用程式;另外還有第 3 部分,在第 3 部分中您要將這篇教學中建立的兩個 Bean 以及第 2 部分中建立的電子郵件應用程式串聯在一起,完成整個應用程式。