JNDI 支援
ActiveMQ Classic 可與任何能夠儲存 Java 物件的 JNDI 提供者搭配使用。然而,通常需要一個 JNDI 初始環境才能運行許多 JMS 範例程式,例如Sun 的 JMS 教學。
因此,我們提供了一個簡單的 JNDI InitialContextFactory
,可用於查找 JMS 連接工廠物件以及目標物件。例如,如果您將此jndi.properties檔案放在您的 classpath 上,您可以在InitialContext
中查找ConnectionFactory
物件和Destinations
等。
java.naming.factory.initial = org.apache.activemq.jndi.ActiveMQInitialContextFactory
# use the following property to configure the default connector
java.naming.provider.url = vm://localhost
# use the following property to specify the JNDI name the connection factory
# should appear as.
#connectionFactoryNames = connectionFactory, queueConnectionFactory, topicConnectionFactry
# register some queues in JNDI using the form
# queue.[jndiName] = [physicalName]
queue.MyQueue = example.MyQueue
# register some topics in JNDI using the form
# topic.[jndiName] = [physicalName]
topic.MyTopic = example.MyTopic
您可以編輯jndi.properties
檔案來配置 ActiveMQConnectionFactory
的屬性,例如brokerURL
以及是否應該有嵌入式代理等等。有關詳細資訊,請參閱如何將代理嵌入連線中。
ActiveMQ Classic JNDI 教學
這是一個關於如何設定和使用 JNDI 建立與 ActiveMQ Classic 連線的快速單頁教學。第一件事是 ActiveMQ Classic 不提供完整的 JNDI 伺服器。這意味著 JMS 客戶端需要使用屬性檔案來建立 JNDI IntialContextFactory
。如果您需要範例屬性檔案,可以查看來源發行版 https://github.com/apache/activemq/blob/master/activemq-unit-tests/src/test/resources/jndi.properties。在繼續之前,這裡有一些屬性。
名稱 | 值 |
java.naming.factory.initial |
org.apache.activemq.jndi.ActiveMQInitialContextFactory |
java.naming.provider.url |
tcp://hostname:61616 |
topic.MyTopic |
example.MyTopic |
請務必將 activemq-_<version>_.jar
和 spring-1.x.jar
新增至您的 classpath。如果程式庫不在 classpath 中,您將在執行時收到 ClassNotFoundException
。如果收到 ClassNotFoundException
,請嘗試列印出 classpath 並檢查它是否存在。您也可以使用 -verbose
選項執行 ActiveMQ Classic 以驗證 jar 是否已正確載入。
範例程式碼
// Create a new intial context, which loads from jndi.properties file:
javax.naming.Context ctx = new javax.naming.InitialContext();
// Lookup the connection factory:
javax.jms.TopicConnectionFactory factory = (javax.jms.TopicConnectionFactory)ctx.lookup("ConnectionFactory");
// Create a new TopicConnection for pub/sub messaging:
javax.jms.TopicConnection conn = factory.getTopicConnection();
// Lookup an existing topic:
javax.jms.Topic mytopic = (javax.jms.Topic)ctx.lookup("MyTopic");
// Create a new TopicSession for the client:
javax.jms.TopicSession session = conn.createTopicSession(false,TopicSession.AUTO_ACKNOWLEDGE);
// Create a new subscriber to receive messages:
javax.jms.TopicSubscriber subscriber = session.createSubscriber(mytopic);
請注意,範例中的主題名稱為 MyTopic
。ActiveMQ Classic 將讀取 jndi.properties
檔案,並以延遲方式建立主題和佇列。主題和佇列的前置詞會被剝除,因此 JNDI 名稱從前置詞之後開始。
一旦您編輯並準備好 jndi.properties
,它需要可讓您的應用程式存取。最簡單的方法是將 jndi.properties
新增到 jar 檔案中。當呼叫 new InitialContext()
時,它將掃描資源並找到該檔案。如果收到 javax.naming.NamingException
,通常表示 jndi.properties
檔案無法存取。
您也可以嘗試使用屬性檔案的實例或 map 來建立新的初始環境。例如,JMS 規格建議的方法也能正常運作。
規格建議的範例
javaProperties props = new Properties();
props.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.activemq.jndi.ActiveMQInitialContextFactory");
props.setProperty(Context.PROVIDER_URL, "tcp://hostname:61616");
javax.naming.Context ctx = new InitialContext(props);
如果 ActiveMQ Classic 嵌入在 EJB 容器中,您需要查看容器的文件,以取得正確的 JNDI 值。
動態建立目標
為了使用基於 JNDI 的程式進行最簡單的配置,有兩個動態環境,分別是
dynamicQueues
dynamicTopics
這些可讓您使用 JNDI 查找佇列和主題,而無需任何配置。
例如,如果您使用以下名稱查找 JNDI
dynamicQueues/FOO.BAR
您將取回一個名為 FOO.BAR
的 ActiveMQQueue
。如果您可以輕鬆重新配置 JNDI 名稱以查找 JNDI 中的內容,但不想重複配置 jndi.properties
來匹配,這會非常方便。
使用嵌入式代理
在與 JMS 客戶端相同的 JVM 中使用嵌入式代理通常很有用。有關此資訊,請參閱如何將代理嵌入連線中。
如果您想將嵌入式代理與您的 JNDI 提供者一起使用,您可以只使用 VM 傳輸 連線到您的 URL 中的代理。例如,要建立純粹在 JVM 中的代理,請使用此 URI
vm://locahost
如果您想自訂代理,請使用類似的方法
vm:broker:(tcp://localhost:61616)
VM 傳輸參考中提供了更多選項
範例 Java 程式碼
一旦您在 classpath 上配置了 JNDI,您就可以執行任何普通的 JMS 應用程式,例如以下範例。請注意,Java 程式碼僅使用純 JMS API,而不是任何 ActiveMQ Classic 特定的程式碼
/**
* The SimpleQueueSender class consists only of a main method,
* which sends several messages to a queue.
*
* Run this program in conjunction with SimpleQueueReceiver.
* Specify a queue name on the command line when you run the
* program. By default, the program sends one message. Specify
* a number after the queue name to send that number of messages.
*/
package org.apache.activemq.demo;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A simple polymorphic JMS producer which can work with Queues or Topics which
* uses JNDI to lookup the JMS connection factory and destination.
*/
public final class SimpleProducer {
private static final Logger LOG = LoggerFactory.getLogger(SimpleProducer.class);
private SimpleProducer() {}
/**
* @param args the destination name to send to and optionally, the number of
* messages to send
*/
public static void main(String[] args) {
Context jndiContext;
ConnectionFactory connectionFactory;
Connection connection;
Session session;
Destination destination;
MessageProducer producer;
String destinationName;
final int numMsgs;
if ((args.length < 1) || (args.length > 2)) {
LOG.info("Usage: java SimpleProducer <destination-name> [<number-of-messages>]"); System.exit(1);
}
destinationName = args[0];
LOG.info("Destination name is " + destinationName);
if (args.length == 2) {
numMsgs = (new Integer(args[1])).intValue();
} else {
numMsgs = 1;
}
/*
* Create a JNDI API InitialContext object
*/
try {
jndiContext = new InitialContext();
} catch (NamingException e) {
LOG.info("Could not create JNDI API context: " + e.toString());
System.exit(1);
}
/*
* Look up connection factory and destination.
*/
try {
connectionFactory = (ConnectionFactory)jndiContext.lookup("ConnectionFactory");
destination = (Destination)jndiContext.lookup(destinationName);
} catch (NamingException e) {
LOG.info("JNDI API lookup failed: " + e);
System.exit(1);
}
/*
* Create connection. Create session from connection; false means
* session is not transacted. Create sender and text message. Send
* messages, varying text slightly. Send end-of-messages message.
* Finally, close the connection.
*/
try {
connection = connectionFactory.createConnection();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
producer = session.createProducer(destination);
TextMessage message = session.createTextMessage();
for (int i = 0; i < numMsgs; i++) {
message.setText("This is message " + (i + 1));
LOG.info("Sending message: " + message.getText()); producer.send(message);
}
/*
* Send a non-text control message indicating end of messages.
*/
producer.send(session.createMessage());
} catch (JMSException e) {
LOG.info("Exception occurred: " + e);
} finally {
if (connection != null) {
try {
connection.close();
} catch (JMSException ignored) {
}
}
}
}
}