用JMS實現消息的發送和接收時,經常會用到JNDI。因爲JNDI這種方式比較靈活,對於編程也比較簡單。 |
在安裝了MQSeries Client for Java之後,在/java/bin目錄下找到JMSAdmin.config文件。該文件主要用來說明Context的存儲方式及存儲地址,對應於文件中的兩個參數INITIAL_CONTEXT_FACTORY和PROVIDER_URL。典型的JMSAdmin.config文件內容如下: |
#INITIAL_CONTEXT_FACTORY=com.sun.jndi.ldap.LdapCtxFactory |
INITIAL_CONTEXT_FACTORY=com.sun.jndi.fscontext.RefFSContextFactory |
#INITIAL_CONTEXT_FACTORY=com.ibm.ejs.ns.jndi.CNInitialContextFactory |
# |
#PROVIDER_URL=ldap://polaris/o=ibm,c=us |
PROVIDER_URL=file:/d:/temp |
#PROVIDER_URL=iiop://localhost/ |
# |
SECURITY_AUTHENTICATION=none |
INITIAL_CONTEXT_FACTORY表示JMSAdmin Tool使用的服務提供商。當前有三種受支持的值。com.sun.jndi.ldap.LdapCtxFactory用於LDAP,如果使用它就必須安裝一個LDAP服務器。com.sun.jndi.fscontext.RefFSContextFactory用於文件系統上下文,它只需要使用者提供存放上下文的文件路徑。com.ibm.ejs.ns.jndi.CNInitialContextFactory是專門爲websphere提供的,它需要和websphere的CosNaming資源庫一起使用。 |
PROVIDER_URL表示會話初始上下文的URL,由JMSAdmin tool實現的所有JNDI操作的根。它和INITIAL_CONTEXT_FACTORY一一對應。 |
ldap://hostname/contextname 用於LDAP |
file:[drive:]/pathname 用於文件系統上下文 |
iiop://hostname[:port]/[?TargetContext=ctx] 用於訪問websphere CosNaming名稱空間 |
最後還有一個參數SECURITY_AUTHENTICATION,用於說明JNDI是否把安全性憑證傳遞給了您使用的服務供應商。只有當使用了LDAP服務供應商時,才使用此參數。此參數有三個值,none(匿名認證)、simple(簡單認證)和CRAM-MD5認證機制。如果沒有提供有效值,缺省值爲none。 |
確認配置文件之後,可以在/java/bin目錄下啓動JMSAdmin控制檯。也可以在任何目錄下用下面的命令來啓動控制檯: |
JMSAdmin –cfg MQ_JAVA_INSTALL_PATH/java/bin/JMSAdmin.config |
其中MQ_JAVA_INSTALL_PATH爲MQSeries Client for Java安裝的根目錄。 |
若啓動失敗,則好好檢查一下您的環境變量是否設置正確。根據我個人的經驗,除了把com.ibm.mq.jar和com.ibm.mqjms.jar加入到環境變量外,還要把fscontext.jar和providerutil.jar加入到環境變量。 |
進入JMSAdmin控制檯後,您可以自由定義sub context。對於子上下文的操作,主要有一下命令: |
display ctx |
define ctx(ctxname) |
change ctx(ctxname) |
change ctx(=up) |
change ctx(=init) |
delete ctx(ctxname) |
當然,在這裏的主要任務並非是用來定義sub context,而是用來定義以下幾個對象: |
MQQueueConnectionFactory |
MQTopicConnectionFactory |
MQQueue |
MQTopic |
(還有其它的一些對象,如MQXAQueueConnectionFactory等,不常用到,在此不作說明。) |
可以使用很多動詞來操縱目錄名稱空間中的受管理對象。ALTER、DEFINE、DISPLAY、DELETE、COPY和MOVE,它們的用法都算比較簡單,這裏只列舉一二以作說明。 |
例一:定義一QueueConnectionFactory,連接主機10.10.10.18,端口1414 |
DEFINE QCF(EXAMPLEQCF)+ |
DESC(Example Queue Connection Factory)+ |
TRAN(CLIENT)+ |
HOST(10.10.10.18)+ |
QMGR(QM_EXAMPLE)+ |
CHAN(S_EXAMPLE)+ |
PORT(1414)+ |
CCSID(1381) |
例二:定義一Queue,其對應於MQ中的Q_EXAMPLE |
DEFINE Q(EXAMPLEQL)+ |
DESC(Local queue)+ |
QMGR(QM_EXAMPLE)+ |
QUEUE(Q_EXAMPLE)+ |
CCSID(1381) |
四.用JMS實現MQ編程 |
上面我們說明了怎樣用JMSAdmin Tool定義MQ對象的上下文。我們的最終目的是要用JMS來實現MQ編程,以實現在程序中對MQ隊列進行收、發消息。所以,下面我們將重點討論一下MQ的JMS實現。 |
如果您對JMS編程很熟悉,那麼您也就會用JMS來實現MQ編程,因爲用JMS來編寫MQ程序與編寫一般的JMS程序沒有太大的差別。舉個例子,當我們想發送一條消息到MQ的隊列中,再從該隊列中取回消息時,我們編程時主要有四個步驟。首先我們要初始化在程序中要用到的對象,然後纔可以發送消息到隊列中去,再就是收取消息了,最後要清除那些永久對象。這些都和普通的JMS程序相當。程序的源代碼如下: |
import java.util.Hashtable; |
import javax.jms.*; |
import javax.naming.*; |
import javax.naming.directory.*; |
public class sample { |
protected QueueConnectionFactory factory=null; |
protected QueueConnection connection; |
protected QueueSession queueSession; |
protected TextMessage outMessage; |
protected QueueSender queueSender; |
protected QueueReceiver queueReceiver; |
public static final String qcfLookup="EXAMPLEQCF"; |
public static final String qLookup="EXAMPLEQL"; |
public static final String icf = "com.sun.jndi.fscontext.RefFSContextFactory"; |
public String url ="file:/d:/temp"; |
public void sampleInit() throws Exception { |
Hashtable environment = new Hashtable(); |
environment.put(Context.INITIAL_CONTEXT_FACTORY, icf); |
environment.put(Context.PROVIDER_URL, url); |
environment.put(Context.REFERRAL, "throw"); |
Context ctx=new InitialDirContext(environment); |
factory = (QueueConnectionFactory)ctx.lookup(qcfLookup); |
Queue q1=null; |
q1=(Queue)ctx.lookup(qLookup); |
connection = factory.createQueueConnection(); |
queueSession = connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); |
queueSender = queueSession.createSender(q1); |
queueSender.setDeliveryMode(DeliveryMode.NON_PERSISTENT); |
outMessage = queueSession.createTextMessage(); |
queueReceiver = queueSession.createReceiver(q1); |
connection.start(); |
} |
public void sendMessageOut(String message) throws JMSException { |
outMessage.setText(message); |
queueSender.send(outMessage); |
} |
public String receiveMessage() throws Exception{ |
return ((TextMessage)queueReceiver.receive()).getText(); |
} |
public void sampleClose() throws JMSException { |
queueSession.close(); |
connection.close(); |
} |
public static void main(String[] args){ |
String rec; |
sample sp = new sample(); |
try { |
sp.sampleInit(); |
sp.sendMessageOut("Hello World!"); |
java.lang.Thread.sleep(4000); |
rec=sp.receiveMessage(); |
System.out.println("Receive text is : "+rec); |
sp.sampleClose(); |
}catch(Exception e) { |
e.printStackTrace(); |
} |
} |
} |
五.遠程管理 |
MQ在WINDOWS平臺下具有圖形化管理界面,但在UNIX平臺下卻只能通過命令行來進行操作。這樣就給使用者帶來很大的不便。我們都希望能通過圖形界面來進行管理配置。爲了實現我們的想法,我們就必須建立遠程管理。 |
實現遠程管理有以下幾個步驟: |
1.被管理隊列管理器上的命令隊列SYSTEM.ADMIN.COMMAND.QUEUE存在並可用。對於MQ 2版本應執行 amqscoma.tst 腳本來創建。 |
2.使用strmqcsv命令來啓動被管理隊列管理器上的命令服務器。 |
3.確定被管理隊列管理器上的服務器連接通道SYSTEM.ADMIN.SVRCONN是否存在,如果不存在則創建它。 |
4.一般Unix、Linux平臺中MQ默認的字符集爲819,而Windows平臺爲1381,所以你必須改變其字符集,使兩邊的字符集相同。一般改被管理的字符集。 |
5.如果被管理隊列管理器上的操作用戶與管理隊列管理器上的操作用戶不同,那麼你首先要確認管理隊列管理器上的操作用戶在被管理隊列管理器上存在並且有管理MQ的權限,再者,你需要修改服務器連接通道SYSTEM.ADMIN.SVRCONN的MCAUSER屬性爲管理隊列管理器上的操作用戶。 |
6.啓動被管理隊列管理器上的偵聽器。 |
做完這些工作之後,直接在管理隊列管理器的MQ管理工具中顯示被管理隊列管理器即可。然後你就可以象操作本地隊列管理器一樣,在被管理隊列管理器上定義你需要的MQ對象。 |
六.通道維護 |
在配置遠程連接的時候,我們曾經創建過進程定義。那我們爲什麼要去創建進程定義呢?這就涉及MQ通道維護的概念。 |
通道長時間沒有消息觸發就會自動斷開連接,不再保持運行狀態。時間的長短可以由自己設定,默認值爲6000秒。消息請求再次來臨的時候,就必須再次啓動通道。有些通道,如服務器連接通道、接收方通道等是自動觸發啓動的。當消息請求發送到通道後,通道立即啓動,進入運行狀態。但也有一些通道不會自動啓動,最典型的就是發送方通道。當有消息請求需要使用通道進行消息傳遞的時候,發送方通道也不會自動啓動並把消息發送到遠程隊列,而是把消息留在了與其相關聯的傳輸隊列中。 |
但是,在實際應用中我們又不可能每過一段時間去啓動一次通道,或當有消息來再去啓動通道。那應該怎麼辦?首先我們創建一個進程定義,這個進程定義的目的就是用來啓動發送方通道。然後我們在傳輸隊列的進程名稱屬性欄指定剛纔定義的進程定義名稱,再把觸發器控制開關打開。這樣,當有消息進入傳輸隊列後,傳輸隊列的觸發器會啓動觸發執行指定的進程,從而啓動發送方通道,把消息傳輸到遠程隊列中去。 |