我正在编写一个使用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>
如你所见,我正在设置:
使用此配置,我的应用程序在启动时失败:
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定义所有内容的重要原因。
答案 0 :(得分:1)
我认为问题在于Akka使用它自己的CamelContext而不是Spring配置中定义的CamelContext。在早期版本的Akka中似乎有可能to set the context used by 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上下文,这基于以下内容:
每个ActorSystem只加载一次CamelExtension, 这样可以安全地在你的任何一点调用CamelExtension 用于获取与其关联的Apache Camel对象的代码。有 每个ActorSystem都有一个CamelContext和一个ProducerTemplate 使用CamelExtension。
http://doc.akka.io/docs/akka/current/scala/camel.html#CamelExtension
答案 1 :(得分: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>