天天看點

java factory mode_WebSphere MQ Java 應用開發簡單執行個體(下篇)(client mode和binding mode)...

* MQ伺服器的配置參加上篇*

4-綁定模式連接配接

Java應用程式必須和MQ伺服器安裝在同一個機器上,通過程序間通信機制,不通過TCP/IP進行通信,減少網絡開銷。

MQ提供32和64位版本的MQ JNI庫,預設在/opt/mqm/java/lib/和/opt/mqm/java/lib64/目錄下:

mqjbnd.so: 該庫為應用提供綁定模式連接配接MQ;

修改代碼:其實變動更小,隻要将主機位址和端口去掉即可,因為綁定模式不需要網絡通信,此外也不需要TCP通道參數。

import java.io.IOException;

import com.ibm.mq.MQEnvironment;

import com.ibm.mq.MQException;

import com.ibm.mq.MQGetMessageOptions;

import com.ibm.mq.MQMessage;

import com.ibm.mq.MQPutMessageOptions;

import com.ibm.mq.MQQueue;

import com.ibm.mq.MQQueueManager;

import com.ibm.mq.constants.CMQC;

import com.ibm.mq.constants.MQConstants;

public class MQTest2 {

public static void main(String[] args) throws MQException, IOException

{

//發送消息給隊列

put();

//從隊列讀取消息

get();

}

static void put() throws MQException, IOException

{

//配置MQ伺服器連接配接參數

//使用者名和密碼

MQEnvironment.userID = "mquser1";

MQEnvironment.password = "mqtest2016";

//設定應用名稱,友善伺服器MQ 檢視應用連接配接

MQEnvironment.properties.put(MQConstants.APPNAME_PROPERTY, "MQ Test By Java");

//設定綁定模式通信

MQEnvironment.properties.put(CMQC.TRANSPORT_PROPERTY,CMQC.TRANSPORT_MQSERIES_BINDINGS);

//建立執行個體,連接配接隊列管理器

MQQueueManager queueManager = new MQQueueManager("JAVA.QUEUE.MANAGER.1");

//以可寫的方式通路隊列管理器已定義的隊列QUEUE1,當然也可以建立隊列

MQQueue putQueue = queueManager.accessQueue("QUEUE1", CMQC.MQOO_OUTPUT);

//建立并發送消息給隊列

MQMessage myMessage = new MQMessage();

String name = "MePlusPlus's 部落格2";

myMessage.writeUTF(name);

//使用預設的消息選項

MQPutMessageOptions pmo = new MQPutMessageOptions();

//發送消息

putQueue.put(myMessage, pmo);

putQueue.close();

//斷開連接配接

queueManager.disconnect();

}

static void get() throws MQException, IOException

{

//配置MQ伺服器連接配接參數

MQEnvironment.userID = "mquser1";

MQEnvironment.password = "mqtest2016";

//設定應用名稱,友善伺服器MQ 檢視應用連接配接

MQEnvironment.properties.put(MQConstants.APPNAME_PROPERTY, "MQ Test By Java");

MQEnvironment.properties.put(CMQC.TRANSPORT_PROPERTY, CMQC.TRANSPORT_MQSERIES_BINDINGS);

//建立執行個體,連接配接隊列管理器

MQQueueManager queueManager = new MQQueueManager("JAVA.QUEUE.MANAGER.1");

//以可讀的方式通路隊列管理器已定義的隊列QUEUE1

MQQueue getQueue = queueManager.accessQueue("QUEUE1", CMQC.MQOO_INPUT_AS_Q_DEF);

//從隊列讀取消息

MQMessage theMessage = new MQMessage();

MQGetMessageOptions gmo = new MQGetMessageOptions();

getQueue.get(theMessage, gmo);

String name = theMessage.readUTF();

System.out.println(name);

getQueue.close();

//斷開連接配接

queueManager.disconnect();

}

}

編譯運作:

# cd /home/mq

# /home/jdk1.8.0_102/bin/javac -cp /opt/mqm/java/lib/com.ibm.mq.allclient.jar MQTest2.java

# /home/jdk1.8.0_102/bin/java -cp /opt/mqm/java/lib/com.ibm.mq.allclient.jar:/home/mq MQTest2

出現以下錯誤:

Exception in thread "main" com.ibm.mq.MQException: MQJE001: 完成代碼為 '2',原因為 '2495'。

at com.ibm.mq.MQSESSION.(MQSESSION.java:2065)

at com.ibm.mq.MQSESSION.getSession(MQSESSION.java:2105)

at com.ibm.mq.MQManagedConnectionJ11.(MQManagedConnectionJ11.java:210)

at com.ibm.mq.MQBindingsManagedConnectionFactoryJ11._createManagedConnection(MQBindingsManagedConnectionFactoryJ11.java:187)

at com.ibm.mq.MQBindingsManagedConnectionFactoryJ11.createManagedConnection(MQBindingsManagedConnectionFactoryJ11.java:233)

at com.ibm.mq.StoredManagedConnection.(StoredManagedConnection.java:96)

at com.ibm.mq.MQSimpleConnectionManager.allocateConnection(MQSimpleConnectionManager.java:194)

at com.ibm.mq.MQQueueManagerFactory.obtainBaseMQQueueManager(MQQueueManagerFactory.java:767)

at com.ibm.mq.MQQueueManagerFactory.procure(MQQueueManagerFactory.java:715)

at com.ibm.mq.MQQueueManagerFactory.constructQueueManager(MQQueueManagerFactory.java:678)

at com.ibm.mq.MQQueueManagerFactory.createQueueManager(MQQueueManagerFactory.java:148)

at com.ibm.mq.MQQueueManager.(MQQueueManager.java:675)

at MQTest2.put(MQTest2.java:38)

at MQTest2.main(MQTest2.java:18)

Caused by: com.ibm.mq.jmqi.local.LocalMQ$3: CC=2;RC=2495;AMQ8598: 未能裝入 WebSphere MQ 本機 JNI 庫“'mqjbnd'”。

at com.ibm.mq.jmqi.local.LocalMQ.loadLib(LocalMQ.java:1236)

at com.ibm.mq.jmqi.local.LocalMQ$1.run(LocalMQ.java:280)

at java.security.AccessController.doPrivileged(Native Method)

at com.ibm.mq.jmqi.local.LocalMQ.initialise_inner(LocalMQ.java:268)

at com.ibm.mq.jmqi.local.LocalMQ.initialise(LocalMQ.java:231)

at com.ibm.mq.jmqi.local.LocalMQ.(LocalMQ.java:1318)

at com.ibm.mq.jmqi.local.LocalServer.(LocalServer.java:229)

at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)

at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)

at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)

at java.lang.reflect.Constructor.newInstance(Constructor.java:423)

at com.ibm.mq.jmqi.JmqiEnvironment.getInstance(JmqiEnvironment.java:672)

at com.ibm.mq.jmqi.JmqiEnvironment.getMQI(JmqiEnvironment.java:606)

at com.ibm.mq.MQSESSION.(MQSESSION.java:2058)

... 13 more

Caused by: java.lang.UnsatisfiedLinkError: no mqjbnd in java.library.path

at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1867)

at java.lang.Runtime.loadLibrary0(Runtime.java:870)

at java.lang.System.loadLibrary(System.java:1122)

at com.ibm.mq.jmqi.local.LocalMQ.loadLib(LocalMQ.java:1208)

... 26 more

很明顯,上述錯誤,是因為找不到MQ JNI庫,libmqjbnd.so,重新運作,指明該庫位置(注意本機是64位作業系統)

# /home/jdk1.8.0_102/bin/java -Djava.library.path=/opt/mqm/java/lib64 -cp /opt/mqm/java/lib/com.ibm.mq.allclient.jar:/home/mq MQTest2

再次出錯

Exception in thread "main" com.ibm.mq.MQException: MQJE001: 完成代碼為 '2',原因為 '2035'。

at com.ibm.mq.MQManagedConnectionJ11.(MQManagedConnectionJ11.java:251)

at com.ibm.mq.MQBindingsManagedConnectionFactoryJ11._createManagedConnection(MQBindingsManagedConnectionFactoryJ11.java:187)

at com.ibm.mq.MQBindingsManagedConnectionFactoryJ11.createManagedConnection(MQBindingsManagedConnectionFactoryJ11.java:233)

at com.ibm.mq.StoredManagedConnection.(StoredManagedConnection.java:96)

at com.ibm.mq.MQSimpleConnectionManager.allocateConnection(MQSimpleConnectionManager.java:194)

at com.ibm.mq.MQQueueManagerFactory.obtainBaseMQQueueManager(MQQueueManagerFactory.java:767)

at com.ibm.mq.MQQueueManagerFactory.procure(MQQueueManagerFactory.java:715)

at com.ibm.mq.MQQueueManagerFactory.constructQueueManager(MQQueueManagerFactory.java:678)

at com.ibm.mq.MQQueueManagerFactory.createQueueManager(MQQueueManagerFactory.java:148)

at com.ibm.mq.MQQueueManager.(MQQueueManager.java:675)

at MQTest2.put(MQTest2.java:38)

at MQTest2.main(MQTest2.java:18)

···

很明顯,表明MQ通路的權限錯誤,說明在綁定模式下,MQ未能充分授權,可能是隊列管理器、通道或者隊列對象。

經過搜尋和閱讀文檔,發現如下幾種方式,嘗試了以下方法,最終通過最後一種方法解決:

第一種嘗試(**該方法不起效果**):仔細檢查伺服器端隊列管理器和通道等配置發現,

> DEFINE CHANNEL(JAVA.CLIENT.CHANNEL1) CHLTYPE(SVRCONN) TRPTYPE(TCP)

> SET CHLAUTH(JAVA.CLIENT.CHANNEL1) TYPE(ADDRESSMAP) ADDRESS('127.0.0.1') MCAUSER('mquser1')

這裡的channel配置是針對IP位址進行過濾和限制通路的,建立的TCP通道,是以需要新增另一種類型的通道。

需要建立一個通道,非TCP通道,命名為JAVA.CLIENT.CHANNEL2

此外發現錯誤:少配置了 channel的參數,加入代碼:

MQEnvironment.channel = "JAVA.CLIENT.CHANNEL2";

# su - mqm

~ cd /opt/mqm/bin

~ source setmqenv -s

啟動腳本執行器,進行設定

~ runmqsc JAVA.QUEUE.MANAGER.1

DEFINE CHANNEL(JAVA.CLIENT.CHANNEL2) CHLTYPE(SVRCONN)

SET CHLAUTH('JAVA.CLINET.CHANNEL2') TYPE(USERMAP) ACTION(ADD) CLNTUSER('mquser1') USERSRC(MAP) MCAUSER('mquser1')

**仍然出錯,其實通過綁定模式,不需要通道參數,因為它不通過網絡進行通信,是利用程序間通信機制進行消息傳遞,上述方法無法解決**

第二種方法:(**該方法有效**)

在尋找文檔和查找MQ Java API文檔以後,發現了以下說明,進而找到解決方法,主要是因為執行MQTest2時,

需要切換到特定使用者,本文定義隊列管理器和隊列時授權給mquser1。

此外,在類 com.ibm.mq.MQEnvironment的

[文檔](https://www.ibm.com/support/knowledgecenter/SSFKSJ_7.5.0/com.ibm.mq.javadoc.doc/WMQJavaClasses/com/ibm/mq/MQEnvironment.html)

中有該表述:

> All the methods and attributes of this class apply to the WebSphere MQ classes for Java client connections,

> but only enableTracing(), disableTracing(), properties, version_notice, userID, connOptions and connTag apply to bindings connections.

翻譯出來,很簡單,對于綁定模式,MQEnvironment類中隻有 enableTracing(), disableTracing(), properties,

version_notice, userID, connOptions and connTag 這幾個方法和屬性有效,是以不用設定通道,主機名等屬性,

此外,properties裡面很多key也就是重寫了類中方法和屬性而已。

是以需要修改MQTest2.java檔案,删除不必要的屬性,修改後的代碼如下:

```java

import java.io.IOException;

import com.ibm.mq.MQEnvironment;

import com.ibm.mq.MQException;

import com.ibm.mq.MQGetMessageOptions;

import com.ibm.mq.MQMessage;

import com.ibm.mq.MQPutMessageOptions;

import com.ibm.mq.MQQueue;

import com.ibm.mq.MQQueueManager;

import com.ibm.mq.constants.CMQC;

import com.ibm.mq.constants.MQConstants;

public class MQTest2 {

public static void main(String[] args) throws MQException, IOException

{

//發送消息給隊列

put();

//從隊列讀取消息

get();

}

static void put() throws MQException, IOException

{

//設定應用名稱,友善伺服器MQ 檢視應用連接配接

MQEnvironment.properties.put(MQConstants.APPNAME_PROPERTY, "MQ Test By Java");

//設定綁定模式通信

MQEnvironment.properties.put(CMQC.TRANSPORT_PROPERTY,CMQC.TRANSPORT_MQSERIES_BINDINGS);

//建立執行個體,連接配接隊列管理器

MQQueueManager queueManager = new MQQueueManager("JAVA.QUEUE.MANAGER.1");

//以可寫的方式通路隊列管理器已定義的隊列QUEUE1,當然也可以建立隊列

MQQueue putQueue = queueManager.accessQueue("QUEUE1", CMQC.MQOO_OUTPUT);

//建立并發送消息給隊列

MQMessage myMessage = new MQMessage();

String name = "MePlusPlus's 部落格2";

myMessage.writeUTF(name);

//使用預設的消息選項

MQPutMessageOptions pmo = new MQPutMessageOptions();

//發送消息

putQueue.put(myMessage, pmo);

putQueue.close();

//斷開連接配接

queueManager.disconnect();

}

static void get() throws MQException, IOException

{

//設定應用名稱,友善伺服器MQ 檢視應用連接配接

MQEnvironment.properties.put(MQConstants.APPNAME_PROPERTY, "MQ Test By Java");

MQEnvironment.properties.put(CMQC.TRANSPORT_PROPERTY, CMQC.TRANSPORT_MQSERIES_BINDINGS);

//建立執行個體,連接配接隊列管理器

MQQueueManager queueManager = new MQQueueManager("JAVA.QUEUE.MANAGER.1");

//以可讀的方式通路隊列管理器已定義的隊列QUEUE1

MQQueue getQueue = queueManager.accessQueue("QUEUE1", CMQC.MQOO_INPUT_AS_Q_DEF);

//從隊列讀取消息

MQMessage theMessage = new MQMessage();

MQGetMessageOptions gmo = new MQGetMessageOptions();

getQueue.get(theMessage, gmo);

String name = theMessage.readUTF();

System.out.println(name);

getQueue.close();

//斷開連接配接

queueManager.disconnect();

}

}

編譯運作:

# cd /home/mq

# /home/jdk1.8.0_102/bin/javac -cp /opt/mqm/java/lib/com.ibm.mq.allclient.jar:/home/mq MQTest2.java

切換到mquser1執行:(**這個是關鍵**)

# su - mquser1

~ /home/jdk1.8.0_102/bin/java -Djava.library.path=/opt/mqm/java/lib64 -cp /opt/mqm/java/lib/com.ibm.mq.allclient.jar:/home/mq MQTest2

5-MQEnvironment類中的userID和password屬性作用初探

MQEnvironment類中的userID和password屬性:用來認證MQ應用的使用者身份。

如果設定了securityExit屬性,應用特定的安全驗證流程,則userID和password屬性将不起作用被忽略;

預設情況下,如果userID為空時,将預設發送JRE 屬性(user.name)作為該屬性值;如果設定了,則發送設定值;

此外,目前SecurityExit該接口已經過期,對于綁定模式(binding mode),SecurityExit接口不支援。

是以,userID和password表明應用身份,要和MQ伺服器端的安全設定配合使用,本文的示例程式中并沒有用上該屬性。

上述MQ設定:

SET AUTHREC PROFILE(QUEUE1) OBJTYPE(QUEUE) PRINCIPAL('mquser1') AUTHADD(PUT,GET)

principal屬性表明 使用者mquser1對于該隊列管理器中的隊列具有put,get權限。

SET AUTHREC OBJTYPE(QMGR) PRINCIPAL('mquser1') AUTHADD(CONNECT)

principal屬性表明mquser1對于隊列管理器具有連接配接的權限

DEFINE CHANNEL(JAVA.CLIENT.CHANNEL1) CHLTYPE(SVRCONN) TRPTYPE(TCP)

SET CHLAUTH(JAVA.CLIENT.CHANNEL1) TYPE(ADDRESSMAP) ADDRESS('127.0.0.1') MCAUSER('mquser1')

這條很重要,表明通過IP位址進行連接配接過濾,表明127.0.0.1這台機器可以通過通道JAVA.CLIENT.CHANNEL1,并以mquser1的身份通路隊列管理器。

也就是說127.0.0.1這台機器無需傳遞任何身份資訊。

那麼該userID和password作用有哪些?

實驗:對于上述執行個體中的TCP方式通路的代碼,1.不設定該屬性,正常運作;2.設定為mquser1和正确的password,正常運作;

3.設定mquser1和不設定password屬性,正常運作

4.設定為mquser1和錯誤的密碼無法通路 5.設定不存在的使用者mquser2,也無法通路; 6.添加使用者mquser2,正常運作

說明MQ伺服器對于應用傳遞過來的userID和password屬性并沒有忽略,而是一種方式在驗證,從上看出是以驗證作業系統使用者的方式在驗證。

此外配合MQ的權限設定,可以

SET CHLAUTH(' generic-channel-name ') TYPE (USERMAP) CLNTUSER(client-user-name) USERSRC(MAP) MCAUSER(user)

通過使用者映射,将應用傳遞過來的userID(也就是client-user-name) 映射成 系統的user(MCAUSER屬性)