考虑以下 mule 配置并将 mule 嵌入到 Web (Java EE) 应用程序中:
<jms:connector
name="jmsConnector"
connectionFactory-ref="jmsConnectionFactory"> <!-- From spring -->
<!-- JNDI Name Resover here? -->
</jms:connector>
<flow name="mainTestFlow">
<jms:inbound-endpoint connector-ref="jmsConnector"
queue="jms/MessageQueue" />
<logger level="INFO" category="mule.message.logging" message="Message arrived." />
</flow>
jmsConnectionFactory 指的是 Spring 中定义的一个 JMS Connection Factory,来自:
<bean id="jmsConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="jms/QueueConnectionFactory" />
</bean>
队列连接工厂已经过测试并且正在运行。
jms/MessageQueue队列名称是指在 Web 应用程序web.xml文件中定义的资源引用。此 JNDI 引用在容器级别绑定到由应用程序服务器管理并连接到适当消息传递服务器(在本例中为 ActiveMQ)的javax.jms.Queue 。
但是,Mule 不会将queue=""属性视为 JNDI 目标,而是将其视为队列名称本身。因此,当上述代码初始化时,它实际上在 ActiveMQ 中创建了一个名为“jms/MessageQueue”的新队列。我真正想要的是它正确地从 Web 应用程序描述符中的 JNDI 引用中检索到队列。
好的,你可以说,我所要做的就是在 JMS 连接器上配置一个 JNDI 名称解析器,并向它添加jndiDestinations="true"和forceJndiDestinations="true"属性。
这是可以接受的:
<jms:default-jndi-name-resolver
jndiProviderUrl="tcp://localhost:1099"
jndiInitialFactory="???"/>
真正的问题是我不想将真正的 Initial Context Factory 类名放在jndiInitialFactory中,因为它会落入特定于容器的定义中。但是,我的应用程序有时会部署到 JBoss 4.2.3 中,有时会部署到 WebSphere 7 中。由于我们的开发过程,不能选择拥有 2 个配置和 2 个 EAR 包。
无论如何,是否有可能告诉 Mule-ESB 将当前容器(因为它处于嵌入式模式)假定为默认的 JNDI 初始工厂进行查找,或者提供一个“通用”JNDI 初始工厂来识别容器的 JNDI 环境?这应该不是问题,因为 Web 应用程序可以引用它的容器 JNDI 环境而无需额外的(甚至是可见的)配置。
如果不可能,我是否可以让我的jms:inbound-endpoint引用 Spring 中定义的javax.jms.Queue,就像jms:connector对 JMS Connection Factory 所做的那样?这实际上会相当优雅和干净,因为 Mule 对 Spring 友好。
谢谢大家!
解决方案
经过深思熟虑,我终于通过创建一个连接到 spring JNDI 工具(例如 JndiTemplate)的自定义 JndiNameResolver 解决了我的问题。这远不是最好的解决方案,但我发现这是最不会干扰和篡改 Mule 和 Spring 内部复杂性的解决方案。
也就是说,这里是类:package com.filcobra.mule;
import javax.naming.NamingException;
import org.mule.transport.jms.jndi.AbstractJndiNameResolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.jndi.JndiObjectLocator;
import org.springframework.jndi.JndiTemplate;
public class SpringJndiNameResolver extends AbstractJndiNameResolver implements InitializingBean {
private static Logger logger = LoggerFactory.getLogger(SpringJndiNameResolver.class);
private JndiTemplate jndiTemplate;
@Override
public void afterPropertiesSet() throws Exception {
if (jndiTemplate == null) {
jndiTemplate = new JndiTemplate();
}
}
@Override
public Object lookup(String name) throws NamingException {
Object object = null;
if (name != null) {
logger.debug("Looking up name "+name);
object = jndiTemplate.lookup(name);
logger.debug("Object "+object+" found for name "+name);
}
return object;
}
public JndiTemplate getJndiTemplate() {
return jndiTemplate;
}
public void setJndiTemplate(JndiTemplate jndiTemplate) {
this.jndiTemplate = jndiTemplate;
}
}
这样,配置就会恢复到通常的状态:
<spring:bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate" />
<jms:connector
name="jmsConnector"
connectionFactoryJndiName="java:comp/env/jms/MyConnectionFactory" <!-- from Resource-Ref -->
jndiDestinations="true"
forceJndiDestinations="true"
specification="1.1" >
<jms:custom-jndi-name-resolver class="com.filcobra.mule.SpringJndiNameResolver">
<spring:property name="jndiTemplate" ref="jndiTemplate"/>
</jms:custom-jndi-name-resolver>
</jms:connector>
有了这个,我终于能够不将我的 Mule ESB 安装绑定到特定的 JMS 供应商/实现。事实上,JMS(队列和工厂)配置都由应用服务器负责。
然而,有一件事仍然很奇怪。我希望 JMS 端点也使用我的 Jndi 名称解析器来从资源引用或其 JNDI 名称中查找队列,这与使用连接工厂的方式相同。那无论如何都行不通。我最终通过放置队列名称本身来解决这个问题,就像在 JMS 服务器中创建的那样:
<flow name="mainTestFlow">
<jms:inbound-endpoint connector-ref="jmsConnector" queue="queue/myQueue"/> <!-- Queue Name, not JNDI Name -->
那行得通。因此,我假设 JMS 连接器不会尝试查找队列,而只是使用连接工厂(是否查找)直接访问 JMS 服务器。
问候!