Apusic應用服務器文檔

JAVA 消息服務(JMS)定義了Java 中訪問消息中間件的接口。JMS 只是接口,並沒有給予實現,實現JMS 接口的消息中間件稱爲JMS Provider,Apusic應用服務器實現了JMS接口,用戶可以通過使用JMS接口,在Apusic中進行JMS編程。 Apusic支持JMS1.0.2版本,JMS1.0.2是本文檔書寫時JMS API最新發布版。有關JMS1.0.2的特性,參見JMS1.0.2特性

消息中間件提供企業數據的異步傳輸,通過消息中間件,一些原本互相孤立的業務組件可以組合成一個可靠的、靈活的系統。
    消息中間件大致分爲兩類:Point-to-Point(PTP) 和 Publish-Subscribe(Pub/Sub),Apusic支持這兩種模型。
    PTP是點對點傳輸消息,建立在消息隊列的基礎上,每個客戶端對應一個消息隊列,客戶端發送消息到對方的消息隊列中,從自己的消息隊列讀取消息。
    Pub/Sub是將消息定位到某個層次結構欄目的節點上,Pub/Sub 通常是匿名的並能夠動態發佈消息,Pub/Sub 必須保證某個節點的所有發佈者(Publisher)發佈的信息準確無誤地發送到這個節點的所有消息訂閱者(Subscriber)。

JMS 支持兩種消息類型PTP 和Pub/Sub,分別稱作:PTP Domain 和Pub/Sub Domain,這兩種接口都繼承統一的JMS Parent 接口,JMS 主要接口如下所示:
JMS Parent    PTPDomain Pub/Sub Domain
ConnectionFactory QueueConnectionFactory TopicConnectionFactory
Connection QueueConnection TopicConnection
Destination Queue Topic
Session QueueSession TopicSession
MessageProducer QueueSender TopicPublisher
MessageConsumer QueueReceiver,QueueBrowser TopicSubscriber

    以下是對這些接口的簡單描述:
    ConnectionFactory :連接工廠,JMS 用它創建連接
    Connection :JMS 客戶端到JMS Provider 的連接
    Destination :消息的目的地
    Session: 一個發送或接收消息的線程
    MessageProducer: 由Session 對象創建的用來發送消息的對象
    MessageConsumer: 由Session 對象創建的用來接收消息的對象

JDBC: JMS 客戶端可以使用JDBC 接口,可以將JDBC 和JMS 包含在一個事務裏。這種包含可以在EJB 裏,也可以直接調用JTA(Java Transaction API)接口實現。
    JavaBeans: JavaBeans可以用JMS Session 發送接收消息。
    EJB: EJB2.0 規範中定義了新的Message-Driven Beans 組件模型。
    JTA(Java Transaction API): JMS 客戶端可以用JTA 啓動事務。JMS Provider 可以選擇是否支持分佈式事務。
    JTS(Java Transaction Service): JMS 可以和JTS 一起組成一個分佈式事務,如將發送接收消息和更新數據庫包含在一個事務裏。
    JNDI: JMS客戶端通過JNDI 調用JMS 中的對象。

JMS 消息由以下幾部分組成:消息頭,屬性,消息體。
    消息頭(Header) - 消息頭包含消息的識別信息和路由信息,消息頭包含一些標準的屬性如:JMSDestination,JMSMessageID 等。
消息頭 由誰設置
JMSDestination send 或 publish 方法
JMSDeliveryMode send 或 publish 方法
JMSExpiration send 或 publish 方法
JMSPriority send 或 publish 方法
JMSMessageID send 或 publish 方法
JMSTimestamp send 或 publish 方法
JMSCorrelationID 客戶
JMSReplyTo 客戶
JMSType 客戶
JMSRedelivered JMS Provider
屬性(Properties) - 除了消息頭中定義好的標準屬性外,JMS 提供一種機制增加新屬性到消息頭中,這種新屬性包含以下幾種:
    1. 應用需要用到的屬性;
    2. 消息頭中原有的一些可選屬性;
    3. JMS Provider 需要用到的屬性。
    標準的JMS 消息頭包含以下屬性: 
   JMSDestination --消息發送的目的地 
   JMSDeliveryMode --傳遞模式, 有兩種模式: PERSISTENT 和NON_PERSISTENT,PERSISTENT 表示該消息一定要被送到目的地,否則會導致應用錯誤。NON_PERSISTENT 表示偶然丟失該消息是被允許的,這兩種模式使開發者可以在消息傳遞的可靠性和吞吐量之間找到平衡點。
   JMSMessageID 唯一識別每個消息的標識,由JMS Provider 產生。
   JMSTimestamp 一個消息被提交給JMS Provider 到消息被髮出的時間。
   JMSCorrelationID 用來連接到另外一個消息,典型的應用是在回覆消息中連接到原消息。
   JMSReplyTo 提供本消息回覆消息的目的地址。
   JMSRedelivered 如果一個客戶端收到一個設置了JMSRedelivered 屬性的消息,則表示可能該客戶端曾經在早些時候收到過該消息,但並沒有簽收(acknowledged)。
   JMSType 消息類型的識別符。
   JMSExpiration 消息過期時間,等於QueueSender 的send 方法中的timeToLive 值或TopicPublisher 的publish 方法中的timeToLive 值加上發送時刻的GMT 時間值。如果timeToLive值等於零,則JMSExpiration 被設爲零,表示該消息永不過期。如果發送後,在消息過期時間之後消息還沒有被髮送到目的地,則該消息被清除。
  
JMSPriority 消息優先級,從0-9 十個級別,0-4 是普通消息,5-9 是加急消息。JMS 不要求JMS Provider 嚴格按照這十個優先級發送消息,但必須保證加急消息要先於普通消息到達。

   
消息體(Body) - JMS API 定義了5種消息體格式,也叫消息類型,你可以使用不同形式發送接收數據並可以兼容現有的消息格式,下面描述這5種類型:
消息類型 消息體
TextMessage java.lang.String對象,如xml文件內容
MapMessage 名/值對的集合,名是String對象,值類型可以是Java任何基本類型
BytesMessage 字節流
StreamMessage Java中的輸入輸出流
ObjectMessage Java中的可序列化對象
Message 沒有消息體,只有消息頭和屬性。
下例演示創建併發送一個TextMessage到一個隊列:
TextMessage message = queueSession.createTextMessage();
message.setText(msg_text); // msg_text is a String
queueSender.send(message);
下例演示接收消息並轉換爲合適的消息類型:
Message m = queueReceiver.receive();
if (m instanceof TextMessage) {
TextMessage message = (TextMessage) m;
System.out.println("Reading message: " + message.getText());
} else {
// Handle error
}

消息的同步接收
同步接收是指客戶端主動去接收消息,JMS 客戶端可以採用MessageConsumer 的receive方法去接收下一個消息。
    消息的異步接收
異步接收是指當消息到達時,主動通知客戶端。JMS 客戶端可以通過註冊一個實現MessageListener 接口的對象到MessageConsumer,這樣,每當消息到達時,JMS Provider 會調用MessageListener中的onMessage 方法。

PTP(Point-to-Point)模型是基於隊列的,發送方發消息到隊列,接收方從隊列接收消息,隊列的存在使得消息的異步傳輸成爲可能。和郵件系統中的郵箱一樣,隊列可以包含各種消息,JMS Provider 提供工具管理隊列的創建、刪除。JMS PTP 模型定義了客戶端如何向隊列發送消息,從隊列接收消息,瀏覽隊列中的消息。
    下面描述JMS PTP 模型中的主要概念和對象:
名稱 描述
Queue 由JMS Provider 管理,隊列由隊列名識別,客戶端可以通過JNDI 接口用隊列名得到一個隊列對象。
TemporaryQueue 由QueueConnection 創建,而且只能由創建它的QueueConnection 使用。
QueueConnectionFactory  客戶端用QueueConnectionFactory 創建QueueConnection 對象。
QueueConnection  一個到JMS PTP provider 的連接,客戶端可以用QueueConnection 創建QueueSession 來發送和接收消息。
QueueSession  提供一些方法創建QueueReceiver 、QueueSender、QueueBrowser 和TemporaryQueue。如果在QueueSession 關閉時,有一些消息已經被收到,但還沒有被簽收(acknowledged),那麼,當接收者下次連接到相同的隊列時,這些消息還會被再次接收。
QueueReceiver  客戶端用QueueReceiver 接收隊列中的消息,如果用戶在QueueReceiver 中設定了消息選擇條件,那麼不符合條件的消息會留在隊列中,不會被接收到。
QueueSender  客戶端用QueueSender 發送消息到隊列。
QueueBrowser  客戶端可以QueueBrowser 瀏覽隊列中的消息,但不會收走消息。
QueueRequestor  JMS 提供QueueRequestor 類簡化消息的收發過程。QueueRequestor 的構造函數有兩個參數:QueueSession 和queue,QueueRequestor 通過創建一個臨時隊列來完成最終的收發消息請求。
可靠性(Reliability) 隊列可以長久地保存消息直到接收者收到消息。接收者不需要因爲擔心消息會丟失而時刻和隊列保持激活的連接狀態,充分體現了異步傳輸模式的優勢。

JMS Pub/Sub 模型定義瞭如何向一個內容節點發布和訂閱消息,這些節點被稱作主題(topic)。
    主題可以被認爲是消息的傳輸中介,發佈者(publisher)發佈消息到主題,訂閱者(subscribe)從主題訂閱消息。主題使得消息訂閱者和消息發佈者保持互相獨立,不需要接觸即可保證消息的傳送。
    下面描述JMS Pub/Sub 模型中的主要概念和對象:
名稱 描述
訂閱(subscription) 消息訂閱分爲非持久訂閱(non-durable subscription)和持久訂閱(durable subscrip-tion),非持久訂閱只有當客戶端處於激活狀態,也就是和JMS Provider 保持連接狀態才能收到發送到某個主題的消息,而當客戶端處於離線狀態,這個時間段發到主題的消息將會丟失,永遠不會收到。持久訂閱時,客戶端向JMS 註冊一個識別自己身份的ID,當這個客戶端處於離線時,JMS Provider 會爲這個ID 保存所有發送到主題的消息,當客戶再次連接到JMS Provider時,會根據自己的ID 得到所有當自己處於離線時發送到主題的消息。
Topic 主題由JMS Provider 管理,主題由主題名識別,客戶端可以通過JNDI 接口用主題名得到一個主題對象。JMS 沒有給出主題的組織和層次結構的定義,由JMS Provider 自己定義。
TemporaryTopic 臨時主題由TopicConnection 創建,而且只能由創建它的TopicConnection 使用。臨時主題不能提供持久訂閱功能。
TopicConnectionFactory 客戶端用TopicConnectionFactory 創建TopicConnection 對象。
TopicConnection TopicConnection 是一個到JMS Pub/Sub provider 的連接,客戶端可以用TopicConnection創建TopicSession 來發布和訂閱消息。
TopicSession TopicSession 提供一些方法創建TopicPublisher、TopicSubscriber、TemporaryTopic 。它還提供unsubscribe 方法取消消息的持久訂閱。
TopicPublisher 客戶端用TopicPublisher 發佈消息到主題。
TopicSubscriber 客戶端用TopicSubscriber 接收發布到主題上的消息。可以在TopicSubscriber 中設置消息過濾功能,這樣,不符合要求的消息不會被接收。
Durable TopicSubscriber 如果一個客戶端需要持久訂閱消息,可以使用Durable TopicSubscriber,TopSession 提供一個方法createDurableSubscriber創建Durable TopicSubscriber 對象。
恢復和重新派送(Recovery and Redelivery) 非持久訂閱狀態下,不能恢復或重新派送一個未簽收的消息。只有持久訂閱才能恢復或重新派送一個未簽收的消息。
TopicRequestor JMS 提供TopicRequestor 類簡化消息的收發過程。TopicRequestor 的構造函數有兩個參數:TopicSession 和topic。TopicRequestor 通過創建一個臨時主題來完成最終的發佈和接收消息請求。
可靠性(Reliability) 當所有的消息必須被接收,則用持久訂閱模式。當丟失消息能夠被容忍,則用非持久訂閱模式。
Apusic JMS 配置文件包括兩部分: /usr/apusic/config/apusic.conf 和/usr/apusic/config/jms.xml,假設Apusic 的安裝目錄是/usr/apusic。
廣義上說,一個JMS 應用是幾個JMS 客戶端交換消息,開發JMS 客戶端應用由以下幾步構成:
  • 用JNDI 得到ConnectionFactory 對象;
  • 用JNDI 得到目標隊列或主題對象,即Destination 對象;
  • 用ConnectionFactory 創建Connection 對象;
  • 用Connection 對象創建一個或多個JMS Session;
  • 用Session 和Destination 創建MessageProducer 和MessageConsumer;
  • 通知Connection 開始傳遞消息。
PTP 模型主要包含消息的發送和接收,下面我們分別舉例說明:

發送消息

第一,啓動Apusic應用服務器。

第二,打開管理工具:在瀏覽器中輸入http://localhost:6882,缺省管理員爲admin,密碼也是admin。

第三,配置隊列連接創建器:在管理工具中點擊“基本配置”-->“消息”,點擊“隊列連接創建器”,如果存在JNDI名爲 “jms/QueueConnectionFactory”的隊列連接創建器,則無須創建,否則點擊“創建”,添加必要信息,“隊列連接創建器名稱”爲 “QueueConnectionFactory”,“隊列連接創建器JNDI名”爲“jms/QueueConnectionFactory”,“連接 是否可以匿名訪問”爲“是”,其它項爲缺省設置,點擊“保存”。如下:

第四,配置隊列:在管理工具中點擊“基本配置”-->“消息”,點擊“隊列”,如果存在JNDI名爲 “jms/QueueConnectionFactory”的隊列,則無須創建,否則點擊“創建”,添加必要信息,“隊列名稱”爲 “testQueue”,“隊列JNDI名”爲“myQueue”,其它項爲缺省設置,點擊“保存”。如下:

第五,編寫發送消息客戶端代碼:Send.java

import javax.jms.*;
import javax.naming.*;
import java.io.*;
import java.util.*;
import java.rmi.RemoteException;
/**
* Title: JMS
* Description: JMS Test
* Copyright: Copyright (c) 2002
* Company: Apusic
* @author Michael
* @version 1.0
*/

public class Send{

String queueName = "myQueue";
QueueConnectionFactory queueConnectionFactory = null;
Queue queue = null;
QueueConnection queueConnection = null;
QueueSession queueSession = null;
QueueSender queueSender = null;
TextMessage message = null;

public static void main(String[] args) throws Exception {

InitialContext ic = getInitialContext();
Send sender = new Send();
sender.init(ic) ;
sender.sendMessage();
sender.close();

}

public void init(InitialContext ctx) throws Exception{

queueConnectionFactory = (QueueConnectionFactory)ctx.lookup("jms/QueueConnectionFactory");
queueConnection = queueConnectionFactory.createQueueConnection();
queue = (Queue) ctx.lookup(queueName);
}

public void sendMessage() throws JMSException,RemoteException{

queueSession = queueConnection.createQueueSession(false,Session.AUTO_ACKNOWLEDGE);
queueSender = queueSession.createSender(queue);
queueSender.setDeliveryMode(DeliveryMode.PERSISTENT);
message = queueSession.createTextMessage();
message.setText("The Message from myQueue");
queueSender.send(message);
}

public void close() throws JMSException{

if(queueConnection!=null)
queueConnection.close();
}

private static InitialContext getInitialContext() throws NamingException{

Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,"com.apusic.jndi.InitialContextFactory");
env.put(Context.PROVIDER_URL, "rmi://localhost:6888");
return (new InitialContext(env));

}
}
接收消息

如果管理工具中沒有JNDI名爲“jms/QueueConnectionFactory”的隊列連接創建器和“myQueue”的隊列,則依照上面一到四步進行設置,否則可直接編寫接收消息客戶端代碼:Receive.java

import javax.jms.*;
import javax.naming.*;
import java.io.*;
import java.util.*;
import java.rmi.RemoteException;
/**
* Title: JMS
* Description: JMS Test
* Copyright: Copyright (c) 2002
* Company: Apusic
* @author Michael
* @version 1.0
*/

public class Receive{

String queueName = "myQueue";
QueueConnectionFactory queueConnectionFactory = null;
Queue queue = null;
QueueConnection queueConnection = null;
QueueSession queueSession = null;
QueueReceiver queueReceiver = null;
TextMessage message = null;

public static void main(String[] args) throws Exception {

InitialContext ic = getInitialContext();
Receive receiver = new Receive();
receiver.init(ic) ;
receiver.TBreceiveMessage();//你可以在此處調用YBreceiveMessage
receiver.close();
}

public void init(InitialContext ctx) throws Exception{

queueConnectionFactory = (QueueConnectionFactory)ctx.lookup("jms/QueueConnectionFactory");
queueConnection = queueConnectionFactory.createQueueConnection();
queue = (Queue) ctx.lookup(queueName);
}

public void TBreceiveMessage() throws NamingException, JMSException,RemoteException{

queueSession = queueConnection.createQueueSession(false,Session.AUTO_ACKNOWLEDGE);
queueReceiver = queueSession.createReceiver(queue);
queueConnection.start();
for (;;) {
message = (TextMessage) queueReceiver.receive();
System.out.println("Reading message: " + message.getText());
if (message.getText().equals("quit"))
break;
}
}

public void YBreceiveMessage() throws NamingException, JMSException,RemoteException,IOException{

queueSession = queueConnection.createQueueSession(false,Session.AUTO_ACKNOWLEDGE);
queueReceiver = queueSession.createReceiver(queue);
//register my textListener which comes from MessageListener
TextMessageListener textListener = new TextMessageListener();
queueReceiver.setMessageListener(textListener);
queueConnection.start();

System.out.println("To end program, enter Q or q, then ");
InputStreamReader reader = new InputStreamReader(System.in);
char answer = '/0';

while (!((answer == 'q') || (answer == 'Q')))
answer = (char)reader.read();
}

public void close() throws JMSException{

if(queueReceiver!=null)
queueReceiver.close();
if(queueSession!=null)
queueSession.close();
if(queueConnection!=null)
queueConnection.close();
}

private static InitialContext getInitialContext() throws NamingException{

Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.apusic.jndi.InitialContextFactory");
env.put(Context.PROVIDER_URL, "rmi://localhost:6888");
return (new InitialContext(env));

}
}
其中異步接收時使用的TextMessageListener代碼如下:
import javax.jms.MessageListener;
import javax.jms.Message;
import javax.jms.TextMessage;
import javax.jms.JMSException;

/**
* Title: JMS
* Description: JMS Test
* Copyright: Copyright (c) 2002
* Company: Apusic
* @author Michael
* @version 1.0
*/

public class TextMessageListener implements MessageListener {

public TextMessageListener() {}

public void onMessage(Message m) {

TextMessage msg = (TextMessage) m;
try {
System.out.println("Async reading message: " + msg.getText() +
" (priority=" + msg.getJMSPriority() + ")");
} catch (JMSException e) {
System.out.println("Exception in onMessage(): " + e.toString());
}
}
}
最後,運行程序:在Windows下打開兩個DOS窗口,首先運行Receive 然後運行Send,

如果是異步接收消息,在運行Receive 的窗口中可以看到輸出:
To end program, enter Q or q, then
Async reading message: The Second Message from testQueue (priority=4)

如果是同步接收消息,在運行Receive 的窗口中可以看到輸出:
Reading message: The Message from testQueue

Pub/Sub 模型主要包含消息的發佈和訂閱,下面我們分別舉例說明:

發佈消息

如果管理工具中沒有JNDI名爲“jms/TopicConnectionFactory”的隊列連接創建器和“myTopic”的隊列,則依照上面一到四步進行設置,否則可直接編寫發佈消息客戶端代碼:Published.java

import javax.jms.*;
import javax.naming.*;
import java.io.*;
import java.util.*;
import java.rmi.RemoteException;
/**
* Title: JMS
* Description: JMS Test
* Copyright: Copyright (c) 2002
* Company: Apusic
* @author Michael
* @version 1.0
*/

public class Published{

String topicName = "myTopic";
TopicConnectionFactory topicConnectionFactory = null;
Topic topic = null;
TopicConnection topicConnection = null;
TopicSession topicSession = null;
TopicPublisher topicPublisher = null;
String msgText = null;
TextMessage message = null;

public static void main(String[] args) throws Exception {

InitialContext ic = getInitialContext();
Published publisher = new Published();
publisher.init(ic) ;
publisher.publish();
publisher.close();
}

public void init(InitialContext ctx) throws Exception{

topicConnectionFactory = (TopicConnectionFactory)ctx.lookup("jms/TopicConnectionFactory");
topicConnection = topicConnectionFactory.createTopicConnection();
topic = (Topic) ctx.lookup(topicName);
}

public void publish() throws NamingException, JMSException,RemoteException{

topicSession = topicConnection.createTopicSession(false,Session.AUTO_ACKNOWLEDGE);
topicPublisher = topicSession.createPublisher(topic);
message = topicSession.createTextMessage();
msgText = "This is the published message";
message.setText(msgText);
topicPublisher.publish(message);
}

public void close() throws JMSException{

if(topicConnection!=null)
topicConnection.close();
}

private static InitialContext getInitialContext() throws NamingException{

Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,"com.apusic.jndi.InitialContextFactory");
env.put(Context.PROVIDER_URL, "rmi://localhost:6888");
return (new InitialContext(env));

}

}
訂閱消息
import javax.jms.*;
import javax.naming.*;
import java.io.*;
import java.util.*;
import java.rmi.RemoteException;
/**
* Title: JMS
* Description: JMS Test
* Copyright: Copyright (c) 2002
* Company: Apusic
* @author Michael
* @version 1.0
*/

public class Subscriber{

String topicName = "myTopic";
TopicConnectionFactory topicConnectionFactory = null;
TopicConnection topicConnection = null;
Topic topic = null;
TopicSession topicSession = null;
TopicSubscriber topicSubscriber = null;
TextMessage message = null;

String id = "durable";

public static void main(String[] args) throws Exception {

InitialContext ic = getInitialContext();
Subscriber subscriber = new Subscriber();
subscriber.init(ic) ;
subscriber.subscribe();
subscriber.close();
}

public void init(InitialContext ctx) throws Exception{

topicConnectionFactory = (TopicConnectionFactory)ctx.lookup("jms/TopicConnectionFactory");
topicConnection = topicConnectionFactory.createTopicConnection();
topicConnection.setClientID(id) ;
topic = (Topic) ctx.lookup(topicName);
}

public void subscribe() throws NamingException, JMSException,RemoteException{

topicSession = topicConnection.createTopicSession(false,Session.AUTO_ACKNOWLEDGE);
topicSubscriber = topicSession.createDurableSubscriber(topic,id);
topicConnection.start();
message = (TextMessage) topicSubscriber.receive();
System.out.println("SUBSCRIBER THREAD: Reading message: " + message.getText());

}

public void close() throws JMSException{

if(topicConnection!=null)
topicConnection.close();
}

private static InitialContext getInitialContext() throws NamingException{

Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,"com.apusic.jndi.InitialContextFactory");
env.put(Context.PROVIDER_URL, "rmi://localhost:6888");
return (new InitialContext(env));

}

}

最後,運行程序:在Windows下打開兩個DOS窗口,首先運行Subscriber,然後運行Published,在運行Subscriber的窗口中可以看到輸出:

SUBSCRIBER THREAD: Reading message: This is the published message

利用Apusic JMS 配置文件(%Apusic_home%/config/jms.xml),你可以對隊列和主題設定訪問權限,可以詳細規定Apusic中的用戶和組,可以對隊列和主題進行何種操作。
    對於隊列,可以設定發送(send)、接收(receive)和瀏覽(browse)權限;對於主題,可以設定發佈(publish)、訂閱(subscribe)、持久訂閱(subscribe-durable)和取消持久訂閱(unsubscribe)權限。
    客戶端用QueueConnectionFactory 的createQueueConnection(java.lang.String userName,java.lang.String password)方法創建隊列連接時輸入用戶身份,或者用createTopicConnection(java.lang.String userName, java.lang.String password)方法創建主題連接時輸入用戶身份。
    下面的例子授權用戶larry 可以對隊列testQueue 進行接收和瀏覽:

首先創建testQueue隊列,如果Apusic不存在testQueue隊列,則通過管理工具創建,方法同上。
    建立安全角色(security-role)rbt,將該角色映射到用戶larry,使larry 擁有該角色包含的所有權限。如果Apuisc應用服務器不存在larry用戶,在管理工具中“用戶管理”-->“用戶/組” 中創建larry用戶。然後可參考apusic.jar中jms.dtd的定義在jms.xml中加入如下片段:       
    <security-role>
        <role-name>rbt</role-name>
        <principal>larry</principal>
    </security-role>

設置安全角色rbt的權限
    <destination-permission>
        <role-name>rbt</role-name>
        <destination-method>
            <queue-name>testQueue</queue-name>
            <method-name>receive</method-name>
            <method-name>browse</method-name>
        </destination-method>
    </destination-permission>

JMS 是J2EE 規範中提出的消息中間件服務(Java Message Service™)規範,提供應用程序間 異步或同步的消息傳遞和管理服務,Apusic 應用服務器中包含了高效、可靠的消息服務。Apusic應用服務器中的消息服務接口完全遵循JMS API規範。提高了企業應用中各組件的可移植性、鬆耦合特性,同時更加高效可靠;爲分佈式企業應用異步交換關鍵業務數據和事件提供了可靠而靈活的服務。
Apusic消息服務提供對消息隊列(Queue)和消息主題(Topic)的管理,消息的發送由消息服務負責完成,原理如下:
Client----->Computer A(Router x)----------Computer B(Router z)
| Connector 3 |
| Connector 1 |Connector 2
| |
Computer C(Router y)--------------------
Computer A,B,C 分別爲提供消息服務的應用服務器,稱爲Router,他們之間的連接被稱爲Connector,客戶端直接與Computer A 連接,客戶端要把消息傳送到Computer B 的 隊列和主題時,只需指明接收消息的主機名和隊列或主題的名稱發送消息即可,Computer A 中的消息服務將會查找配置文件中的設置,如從A 到B 間有通路連通,消息服務將會按照 最短路徑發送消息到指定服務器(Computer B) 的指定隊列或主題,如A 到B 將無有效連接, 消息將被保存在某一連通的服務器上,待有有效通路之後再發送消息。
根據J2EE1.3規範,Apusic應用服務器提供了兩個主要的消息服務方面的新特性:
多個提供消息服務的應用服務器可以組成一個虛擬的JMS網絡,每個提供消息服務的應用服務器可被視爲一個JMS網絡中的節點,而節點之間的連接則通過聲明消息路由提供。只要用戶連接到JMS網絡中任何一個節點,即可向網絡中的其他任意節點發送消息。
爲提供消息服務的可靠性,和JMS網絡中消息的暫時存儲,Apusic應用服務器使用了一個可靠的消息存儲機制。
Apusic消息服務消息路由與存儲配置的部分存在於Apusic應用服務器目錄下config子目錄中,apusic.conf文件中的相關配置段。apusic.conf 文件中可對消息服務的消息存儲目錄和消息路由進行配置。
Apusic應用服務器的消息存儲的配置,通過編輯Apusic應用服務器目錄下,config子目錄中的apusic.conf 配置文件的相關配置段進行。缺省的配置段如下:
...
<SERVICE
CLASS="com.apusic.jms.server.JMSServer">
<ATTRIBUTE NAME="BackStoreDirectory" VALUE="store/jms"/>
</SERVICE>
...
此配置段指定了消息服務及其使用的消息存儲目錄,其中,名爲BackStoreDirectory屬性的值指定了消 息存儲目錄的路徑,可使用絕對路徑如:c:/message_store/或usr/message_store來指定,亦可使用相對於Apusic安裝 目錄的相對路徑如:store/my_message_store來指定,如指定目錄不存在,應用服務器將創建指定的目錄。
在一個由多臺提供消息服務的應用服務器組成的JMS網絡上,每個提供消息服務的應用服務器可被視爲一個JMS網絡中 的節點,消息路由意味着節點之間的路徑。只要用戶連接到JMS網絡中任何一個節點,即可向網絡中的其他任意節點發送消息,由消息服務根據消息路由提供消息 在JMS網絡中的最佳傳輸機制。如下圖所示:
A、B、C、D、E 五個節點上都是提供消息服務的應用服務器,五個節點連通成一個網絡,連接到 A 節點的客戶端可以發消息到任意五個節點,消息服務會自動尋找一條最佳路徑傳遞消息到目標節點。如從A 發消息到D,有兩條連通路徑:A->B->C->D 和A->E->D,其中 A->E->D 經過的節點最少,該路徑爲最佳路徑,應用服務器將根據此路徑對消息進行傳遞。
在一個JMS網絡中,如上圖所示,每個節點都是一個消息路由器(Router),每個消息路由器有一個路由器名,以區別於其他的路由器。路由器名可以和主機名相同, 也可以不同。
每兩個節點之間的直接路徑被稱爲路由連接器(RoutingConnector),網絡中每兩個節點間的直接路徑必 須事先進行申明性的定義。每個路由連接器的申明是單向的,如在A節點上申明到B的路由連接器,說明可以由A到B,但如果需要由B到A,則需要在B節點上申 明到A的路由連接器。
假設上圖中的五個節點A、B、C、D、E 是某個實際網絡中五個提供消息服務的Apusic應用服務器主機,實際網絡中的主機名分別是computerA、computerB、 computerC、computerD、computerE,在由這五個節點組成的JMS網絡中,對應的路由器名分別是routerA、 routerB、routerC、routerD、routerE,要使這五個節點結成類似於上圖的JMS網絡,即每個節點都有一條指向下一個節點的路 徑,最後一個節點的路徑則指向最開始的節點,形成一個閉合的環,而每個節點都知道環中的其他節點,消息客戶連接到環中的任意節點,都可向其他節點發送消 息,則需要在每個節點上申明其他節點,同時申明此節點到其他節點的路徑,如:A節點上需要申明B、C、D、E四個節點,表示由A節點可以接收向其他四個節 點發送消息的請求,同時需要申明由A到B的路徑,表示由A節點到B節點有一條通路;節點B則需要申明A、C、D、E四個節點,同時需要申明由B到C的路 徑,如此類推。
如消息路由圖示-2中,如節點B未申明到節點C的路徑,消息客戶連接到了節點A,發送消息到節點C,由於此時無有效的由A到C的路徑,則消息將會被保存在網絡中,等待當有有效的路徑時再行發送。
對消息服務中路由的配置是通過Apusic應用服務器目錄中子目錄config下的apusic.conf配置文件進行的。通過apusic.conf中的相關配置段可以對消息路由器和路由連接器進行管理和配置。
路由器的配置 其它路由器的配置依次類推。 路由連接器的配置
每個JMS網絡中的消息路由器都是通過路由連接器進行連接的,每個路由器可以擁有多個路由連接器連接到其它節點。對路由連接器的配置是通過在apusic.conf配置文件中增加路由連接器服務實現的。以消息路由圖示-2中的A節點爲例,需要申明到節點B的路由連接器,則可在apusic.conf文件中加入如下配置段:
...
<SERVICE
CLASS="com.apusic.jms.routing.RoutingConnector" NAME="Connector:Name=toB">
<ATTRIBUTE NAME="RemoteHost" VALUE="computerB"/>
<ATTRIBUTE NAME="RemotePort" VALUE="6888"/>
</SERVICE>
...
其中computerB是B的主機名,toB是此路由連接器區別於其他路由連接器的自由定義名字,6888是默認的JMS服務端口,具體使用中,可將其更改爲實際的路由連接器名、主機名與端口。
消息路由圖示-2中其它節點上的路由連接器配置如此類推。
消息服務的管理是通過配置消息管理對象的屬性完成的。消息管理對象包含了管理員生成的消息配置信息,之後由消息客戶端使用。
消息服務中定義了兩個管理對象:
由於消息服務包含兩種消息模型,即Point-to-Point(PTP)和Publish-and- Subscribe(Pub/Sub)模型,Apusic應用服務器對這兩種消息模型提供了完整的支持,因此,對於連接創建器而言,提供了兩種可配置的連 接創建器類型,面向PTP消息模型的QueueConnectionFactory和麪向Pub/Sub模型的 TopicConnectionFactory;對於消息接收站而言,同樣提供了兩種可配置的消息接收類型站,面向PTP的隊列(Queue)站和麪向 Pub/Sub的主題(Topic)站。
通過對存在於Apusic應用服務器目錄下config子目錄中,名爲jms.xml的配置文件進行編輯,實現對Apusic應用服務器消息服務的管理。在此文件中,可以對連接創建器、消息接收站和消息服務安全策略進行配置。
jms.xml文件是一個xml文件,其文檔類型定義(DTD)爲jms-config_1_2.dtd。
Apusic應用服務器提供了缺省的jms.xml文件
在jms.xml文件中,每一個連接創建器配置信息對應一個connection-factory標記申明的xml元素,每個connection-factory元素可包含使用以下三種標記所申明的子元素:
實際應用中,當管理員爲連接創建器分配JNDI名之後,消息客戶即可使用JNDI在服務器的命名空間中對連接創建器進行查找並獲得引用,之後通過連接創建器取得與消息服務的連接。
連接創建器的配置屬性如下表:
屬性 描述 值類型 缺省值
type 連接創建器的消息模型(PTP和Pub/Sub)類型 “QueueConnectionFactory”或“TopicConnectionFactory”,分別對應PTP和Pub/Sub模型,此屬性必須定義
pooled 指定此連接創建器是否對其管理的連接使用連接池 “True”或“False” “False”
secure 指定連接創建器所提供連接的通訊方式 “True”或“False” “False”
anonymous 是否授權匿名用戶訪問此連接創建器 “True”或“False” “True”
client-id 由於標識連接客戶狀態的標識符,通常被用於Pub/Sub模型中的持久訂閱(Durable subscription) 字符串,此屬性是可選的
default-delivery-mode 使用由此連接創建器生成的連接發送消息時,缺省的發送方式 “persistent”或“non-persistent” “non-persistent”
default-priority 使用由此連接創建器生成的連接發送消息時,缺省的優先級 數字(0~9) “4”
default-time-to-live 使用由此連接創建器生成的連接發送消息時,對於已發送的消息,消息系統保留此消息的缺省時間長度,單位爲毫秒。 整型 0
min-pool-size 此連接創建器對應的連接池中,所保持的最少連接數 整型 5
min-pool-size 此連接創建器對應的連接池中,所保持的最少連接數 整型 30
idle-timeout 連接等待超時時間。當連接池中的某個連接等待被使用的實際時間超過此屬性數值時,連接池自動關閉此連接 整型,單位是秒 300
對應於PTP和Pub/Sub消息模型,Apusic應用服務器中的消息服務提供了兩種消息接收站,隊列 (Queue)和主題(Topic)。實際應用中,管理員爲消息接收站分配JNDI名,消息客戶即可使用JNDI在服務器的命名空間中對消息接收站進行查 找並獲得引用,在通過連接創建器取得與消息服務的連接之後,消息客戶即可向消息接收站同步或異步地發送或接收消息。
在jms.xml文件中,每一個消息接收站配置信息對應一個queue標記或topic標記申明的xml元素。
在jms.xml文件中,每一個隊列(Queue)配置信息對應一個queue標記申明的xml元素,每個申明的queue元素可包含三種標記所申明的子元素:
隊列的配置屬性如下表:
屬性 描述 值類型 缺省值
cache-size 隊列緩衝中保留的消息個數 整型 20
expiry-check-interval 系統檢測消息隊列中消息是否過期的時間間隔,單位是秒 整型 60
在jms.xml文件中,每一個主題(Topic)配置信息對應一個topic標記申明的xml元素,每個申明的topic元素可包含三種標記所申明的子元素:
主題的配置屬性如下表:
屬性 描述 值類型 缺省值
cache-size 主題緩衝中保留的消息個數 整型 20
expiry-check-interval 系統檢測消息主題中消息是否過期的時間間隔,單位是秒 整型 60
J2EE™體系中的JMS規範實際上並未包含有關安全方面的內容,因此,系統提供保證消息服務的安全和完整性的機制就極爲重要。
Apusic應用服務器提供了對消息服務方面的安全管理,主要根據J2EE™體系中的安全角色(Security Role)和消息客戶的操作對消息服務進行保護。
消息服務中基於安全角色的授權方式是指,系統管理員可定義一組安全角色,每個被定義的安全角色對應於系統中的一組用 戶或組,然後,根據消息客戶用戶可對消息接收站(Destination)進行的操作(如對於隊列,客戶可執行發送、接收、瀏覽等操作)對前面定義的安全 角色進行授權。
對於一個或者一組jms.xml中申明的消息接收站(Destination)和一個或者一組jms.xml中申明的安全角色,通過使用一個destination-permission標記申明的xml元素來設置這二者之間的對應關係, 每個申明的destination-permission元素可包含三種標記所申明的子元素:
下面範例授權安全角色foo可以對隊列bar 進行接收和瀏覽:
Last modified: 2002年5月27日 8:52:34
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章