为啥我对 QueueConnectionFactory 的 JNDI 查找返回 null?

Posted

技术标签:

【中文标题】为啥我对 QueueConnectionFactory 的 JNDI 查找返回 null?【英文标题】:Why is my JNDI lookup for a QueueConnectionFactory returning null?为什么我对 QueueConnectionFactory 的 JNDI 查找返回 null? 【发布时间】:2010-11-22 22:10:35 【问题描述】:

我正在尝试通过 Geronimo 的 JNDI 查找 QueueConnectionFactoryQueueQueue 返回正常,但 QueueConnectionFactory 查找始终返回 null。它不会抛出 NamingException,如果 JNDI 名称不正确,这是我所期望的。

谁能看到我做错了什么?下面的测试代码输出:

真的 错误的

import javax.jms.Queue;
import javax.jms.QueueConnectionFactory;
import javax.naming.InitialContext;
import javax.naming.NamingException;

public class JndiTest

    private final static String QUEUE_NAME = "jca:/org.apache.geronimo.configs/activemq-ra/JCAAdminObject/SendReceiveQueue";
    private final static String FACTORY_NAME = "jca:/org.apache.geronimo.configs/activemq-ra/JCAManagedConnectionFactory/DefaultActiveMQConnectionFactory";

    public static void main(String[] args) throws NamingException
    
        InitialContext ctx = new InitialContext();
        QueueConnectionFactory factory = (QueueConnectionFactory) ctx.lookup(FACTORY_NAME);
        Queue queue = (Queue)ctx.lookup(QUEUE_NAME);
        System.out.println(factory == null);
        System.out.println(queue == null);      
    


以防万一:我在课堂上添加了 openejb-client-3.0.1.jar、geronimo-ejb_3.0_spec-1.0.1.jar 和 activemq-core-4.1.2-G20090207.jar路径,我的 jndi.properties 文件具有以下属性:

java.naming.factory.initial = org.apache.openejb.client.RemoteInitialContextFactory java.naming.provider.url = ejbd://127.0.0.1:4201

【问题讨论】:

【参考方案1】:

它没有抛出异常的原因是-访问资源时会出现ClassLoadException。

发生这种情况的原因是类:com.sun.jndi.url.jca.jcaURLContextFactory 正在被 ResourceManager 调用的 ClassLoader 搜索。

如果您将工厂名称更改为其他名称,那么您将看到 NamingException - 但在查找的情况下,对于 ClassNotFound/IllegalState 等异常 - 不会引发异常。

因此需要分析 ActiveMQ 的依赖关系。 Update1:​​可能的原因之一是工厂对象只能在托管环境中实例化。您是否将代码作为应用程序客户端运行?

Update2:找到了导致此行为的其他一些指针:

仅 openejb jndi 实现 公开 ejb,而不是任何其他资源。 如果您有 j2ee 应用程序客户端,并且 你想使用 jms,你需要 部署 activemq 适配器的副本 在客户端。然后,您可以使用 j2ee java:comp/env 上下文查找 你的东西。

在 ActiveMQ 网站上找到这个:

ActiveMQ 的 JNDI 实现不与命名服务器对话。它是 一个精简版的 JNDI 客户端,它只允许获取主题和 直接来自 JMS 实例的队列。因此,您必须提供 JMS 服务器地址,而不是提供命名服务器地址。大多数 JNDI 实现使用 java.naming.provider.url 属性来指定命名服务器的地址。 ActiveMQ 使用 brokerURL 之一。使用 java.naming.provider.url 会导致 ActiveMQ 尝试加载整个 Broker。

查看更多关于如何Connect using JNDI:

解释中使用的初始上下文工厂为:org.apache.activemq.jndi.ActiveMQInitialContextFactory

可以找到一些使用 JNDI 测试的示例代码 here

我写了一个简单的 java 客户端 - 注意提供者 url 下面是正在使用的 brokerURL。

    Properties props = new Properties();            
props.put(Context.INITIAL_CONTEXT_FACTORY,
             "org.apache.activemq.jndi.ActiveMQInitialContextFactory");
    //props.put(Context.PROVIDER_URL,"vm://localhost");//Either this or below
    props.put(Context.PROVIDER_URL,"tcp://localhost:65432"); 
    props.put("queue.SendReceiveQueue",
         "org.apache.geronimo.configs/activemq-ra/JCAAdminObject/SendReceiveQueue");
      
    InitialContext context = new InitialContext(props);   
    QueueConnectionFactory connectionFactory = (QueueConnectionFactory)context.lookup
                                                               ("ConnectionFactory");
    Queue q = (Queue) context.lookup("SendReceiveQueue");
    System.out.println("conn is : "  + connectionFactory.getClass().getName());
    System.out.println("queue is : " + q.getQueueName());

这个程序给出了输出:

conn 是:org.apache.activemq.ActiveMQConnectionFactory 队列是:org.apache.geronimo.configs/activemq-ra/JCAAdminObject/SendReceiveQueue

【讨论】:

哇。感谢您的周到、彻底的回答。【参考方案2】:

我有一个有点等价的配置 Tomcat/Geronimo J2EE jar / Geronimo JMS Jar / ActiveMQ 4 我对你的 jndi.propertie 文件有点困惑。 我的看起来像这样:

java.naming.factory.initial = org.apache.activemq.jndi.ActiveMQInitialContextFactory

java.naming.provider.url = tcp://localhost:61616

connectionFactoryNames = connectionFactory , TopicConnectionFactory

明显的不同是你的初始上下文是远程的。除此之外,我必须提供一个 connectionFactoryNames,否则我会得到一个 NamingException。

【讨论】:

【参考方案3】:

我不知道为什么,但对我来说,使用上下文不起作用。好像发送了消息,但是我的消费者的onMessage没有被调用。

使用上下文不会抛出异常但不起作用:

import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;
import javax.jms.TopicSession;

public class HelloClient 

public static void main(String[] args) throws Exception 
    Properties ppt2 = new Properties();
    ppt2.put(Context.INITIAL_CONTEXT_FACTORY,
            "org.apache.activemq.jndi.ActiveMQInitialContextFactory");
    ppt2.put(Context.PROVIDER_URL, "tcp://localhost:61616");
    ppt2.put("topic.MessageDestinationTopic", "console.jms/TopicQueue/JCAAdminObject/MessageDestinationTopic");
    Context ctx2 = new InitialContext(ppt2);

    TopicConnectionFactory factory = (TopicConnectionFactory) ctx2.lookup("ConnectionFactory");
    TopicConnection connection = factory.createTopicConnection();
    TopicSession session = connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
    Topic topic = (Topic) ctx2.lookup("MessageDestinationTopic");

    MessageProducer producer = session.createProducer(topic);

    TextMessage msg = session.createTextMessage();
    msg.setText("this is a test message");
    producer.send(msg);
    producer.close();
    session.close();
    System.out.println("Message published. Please check application server's console to see the response from MDB");
    ctx2.close();
    System.exit(0);




使用下面的代码(没有上下文)效果很好:

import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;
import javax.jms.TopicSession;

public class HelloClient 

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

        TopicConnectionFactory factory = new org.apache.activemq.ActiveMQConnectionFactory("tcp://localhost:61616");
        TopicConnection connection = factory.createTopicConnection();
        TopicSession session = connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
        Topic topic = session.createTopic("MessageDestinationTopic");

        MessageProducer producer = session.createProducer(topic);

        TextMessage msg = session.createTextMessage();
        msg.setText("this is a test message");
        producer.send(msg);
        producer.close();
        session.close();
        System.out.println("Message published. Please check application server's console to see the response from MDB");
        System.exit(0);

    


【讨论】:

【参考方案4】:

这里有两个参与者,您正在 JNDI 中寻找一些东西。别人不得不把它放在那里。我不知道您的环境的具体情况,但我对此类问题的处理方法是

探索命名空间 - 那里有什么?您有任何 JNDI 浏览工具吗? 仔细查看应该向 JNDI 注册的服务的日志,它是否报告任何错误?

【讨论】:

Geronimo 提供了一个JNDI 浏览工具,两个JNDI 名称似乎都在那里。在我的测试工具中更改名称会产生一个 NamingException,因此它必须找到 something 是的,但它发现了什么?是谁把它放在那里的?对我来说,所有这些都表明问题出在 JMS 提供程序而不是您的代码中。

以上是关于为啥我对 QueueConnectionFactory 的 JNDI 查找返回 null?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 NS 谓词无法解析我对 NSArray 索引的条件语句

为啥 Fluent NHibernate 会忽略我对组件的唯一约束?

我不明白为啥我对 Lombok 的 toString 有问题[重复]

当我对 args() 使用切入点时,为啥 tomcat 会抛出 NullPointErexception?

为啥我对朋友函数有未定义的引用?

我对所有值求和,但为啥它不适用于所有动态总计字段