3

我正在编写一个使用 Akka、Akka-Camel 和 Spring 进行配置的应用程序。应用程序需要针对各种应用程序服务器充当独立的 JMS 客户端,为此它需要使用 JNDI 设置 JMS 连接工厂。我正在用 jBoss 对此进行测试。我对 jBoss 5 和 6 有同样的问题(这似乎是客户端 Spring 问题,与 jBoss 无关)。

我正在用这个 xml 配置 Spring bean:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:camel="http://camel.apache.org/schema/spring"
       xmlns:jee="http://www.springframework.org/schema/jee"
       xsi:schemaLocation="
         http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
         http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd
         http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
         ">

    <camelContext id="camel" trace="false" xmlns="http://camel.apache.org/schema/spring">
        <jmxAgent id="agent" disabled="true"/>
    </camelContext>

    <jee:jndi-lookup id="jmsConnectionFactory" jndi-name="ConnectionFactory">
        <jee:environment>
            java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
            java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
            java.naming.provider.url=jnp://192.168.0.109:1099
        </jee:environment>
    </jee:jndi-lookup>

    <bean name="jms" class="org.apache.camel.component.jms.JmsComponent">
        <property name="connectionFactory" ref="jmsConnectionFactory" />
    </bean>

</beans>

如您所见,我正在设置:

  • 通过 JNDI 初始化的 ConnectionFactory,称为 jmsConnectionFactory
  • 一个 JmsComponent,其 connectionFactory 属性设置为前一个 bean

使用此配置,我的应用程序在启动时失败:

java.lang.IllegalArgumentException: connectionFactory must be specified
    at org.apache.camel.util.ObjectHelper.notNull(ObjectHelper.java:294) ~[camel-core.jar:2.10.4]
    at org.apache.camel.component.jms.JmsConfiguration.createConnectionFactory(JmsConfiguration.java:1053) ~[camel-jms.jar:2.10.4]
    at org.apache.camel.component.jms.JmsConfiguration.getConnectionFactory(JmsConfiguration.java:416) ~[camel-jms.jar:2.10.4]
    at org.apache.camel.component.jms.JmsConfiguration.createListenerConnectionFactory(JmsConfiguration.java:1062) ~[camel-jms.jar:2.10.4]
    at org.apache.camel.component.jms.JmsConfiguration.getListenerConnectionFactory(JmsConfiguration.java:435) ~[camel-jms.jar:2.10.4]
    at org.apache.camel.component.jms.JmsConfiguration.configureMessageListenerContainer(JmsConfiguration.java:889) ~[camel-jms.jar:2.10.4]
    at org.apache.camel.component.jms.JmsConfiguration.createMessageListenerContainer(JmsConfiguration.java:379) ~[camel-jms.jar:2.10.4]

这来自 JmsConfiguration.java 中的这段代码:

protected ConnectionFactory createConnectionFactory() {
    ObjectHelper.notNull(connectionFactory, "connectionFactory");
    return null;
}

因此,看起来 Spring bean 初始化未能按照此处的说明关联/连接 bean(从先前粘贴的完整 XML Spring 配置中提取):

    <bean name="jms" class="org.apache.camel.component.jms.JmsComponent">
        <property name="connectionFactory" ref="jmsConnectionFactory" />
    </bean>

我还尝试创建一个中间 JmsConfiguration bean,并设置 JmsComponent 的配置属性,而不是直接设置 connectionFactory 属性,但是在两种设置中我得到了相同的结果。

顺便说一句,我可以通过代码连接 bean 好吧。我的意思是:

val connFactory = springContext.getBean[javax.jms.ConnectionFactory]("jmsConnectionFactory", classOf[javax.jms.ConnectionFactory])
camelContext.addComponent("jms", JmsComponent.jmsComponentAutoAcknowledge(connFactory))

工作得很好。所以我知道我从 JNDI 获得 ConnectionFactory,只是我无法使用正确的 Spring 配置来将其连接到 XML 中。

我需要这个应用程序在不重新编译的情况下是非常可配置的,所以让 XML 工作对我来说是必须的。

如果不清楚,问题是:如何让 Spring 设置我的 JmsComponent bean,并将其 connectionFactory 设置为 JNDI 获得的工厂?

编辑:使用 Camel 的目的是它应该允许我将这个组件交换为另一个不同类型的组件。所以今天我使用 JMS,也许明天我将使用 TCP。这就是为什么能够在 XML 中定义所有内容很重要的原因。

4

2 回答 2

1

我相信问题在于 Akka 使用它自己的 CamelContext 而不是 Spring 配置中定义的。在 Akka 的早期版本中,似乎可以设置 Akka 使用的上下文,但在最新版本中似乎不再可能。

我遇到了同样的问题,我在 Java(而不是 Scala)中使用基于注释的配置,并使用以下代码解决了这个问题:

@Bean
public ActorSystem getCamelActorSystem(ConnectionFactory factory) {

    ActorSystem system = ActorSystem.create("some-system");

    Camel camel = CamelExtension.get(system);
    CamelContext camelContext = camel.context();
    camelContext.addComponent("jms", JmsComponent.jmsComponentAutoAcknowledge(factory));

    return system;
}

这会注入在别处定义的 ConnectionFactory 依赖项,并使用它来将 jms 组件添加到 Akka 使用的 camelContext 中。

另一种解决方案可能是以某种方式扩展 CamelExtension 代码以允许注入 camelContext 依赖项,就像以前可能的那样。但是,我假设他们有充分的理由进行更改,因此不理会它。我认为是这样他们可以确保上下文不能更改,因此 Actor System 始终使用相同的 Camel 上下文,这是基于以下内容:

一个 CamelExtension 只为每个 ActorSystem 加载一次,这样可以安全地在代码中的任何位置调用 CamelExtension 以访问与其关联的 Apache Camel 对象。每一个使用 CamelExtension 的 ActorSystem 都有一个 CamelContext 和一个 ProducerTemplate。

http://doc.akka.io/docs/akka/current/scala/camel.html#CamelExtension

于 2013-10-02T09:03:30.013 回答
0

由于您的查找似乎正在工作,这可能比您正在寻找的要多,但这是我使用 jndiTemplate(spring 3.1)通过 jndi 获得连接工厂的方法。请注意,我通过配置(通过事务管理器)提供连接工厂。抱歉有任何错别字,但你会明白的。

<bean id="jndiDestinationResolver" class="org.springframework.jms.support.destination.JndiDestinationResolver"/>
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
     <property name="location">
        <value>classpath:connection.properties</value>
     </property>
</bean>
<bean name="jms" class="org.apache.camel.component.jms.JmsComponent">
    <property name="configuration" ref="jmsConfig" />
</bean>
<bean id="txManager" class="org.springframework.jms.connection.JmsTransactionManager">
   <property name="connectionFactory" ref="springConnectionFactory"/>
<bean>
<bean id="jmsConfig" class="org.apache.camel.component.jms.JmsConfiguration">
   <property name="connectionFactory" ref="springConnectionFactory" />
   <property name="transactionManager" ref="txManager" />
   <property name="testConnectionOnStartup" value="true" />
   <property name="destinationResolver" ref="jndiDestinationResolver" />
</bean>
<bean id="springConnectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">
   <property name="clientId" ref="SubCid" />
   <property name="reconnectOnException" ref="true" />
   <property name="targetConnectionFactory">
        <bean parent="jndiObjectFactory"/>
   </property>
   <property name="sessionCacheSize" value="1"/>
 </bean>

 <bean id="jndiObjectFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
   <property name="jndiName" value="TopicConnectionFactory"/>
   <property name="jndiTemplate">
        <ref bean="jndiTemplate"/>
   </property>
 </bean>
 <bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate">
       <property name="environment">
           <props>
                <prop key="java.naming.provider.url">${db.jms.JNDIServerName}</prop>
           </props>
       </property>
  </bean>
于 2013-06-13T14:55:14.317 回答