docs - Cómo hacer que Spring conecte mi JmsComponent
taglib spring jsp (2)
Dado que su búsqueda parece estar funcionando, es probable que sea más de lo que está buscando, pero así es como obtengo una fábrica de conexiones a través de jndi usando jndiTemplate (primavera 3.1). Tenga en cuenta que proporciono la fábrica de conteos a través de la configuración (a través del administrador de transacciones). Lo siento por cualquier error tipográfico, pero entenderás.
<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>
Estoy escribiendo una aplicación usando Akka, Akka-Camel y Spring para la configuración. La aplicación debe actuar como un cliente JMS independiente frente a una variedad de servidores de aplicaciones, para lo cual necesita configurar la fábrica de conexiones JMS utilizando JNDI. Estoy probando esto con jBoss. Tengo el mismo problema con jBoss 5 y 6 (esto parece ser un problema de primavera del lado del cliente, no relacionado con jBoss).
Estoy configurando los beans de Spring con este xml:
<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>
Como puedes ver, estoy configurando:
- Una ConnectionFactory inicializada a través de JNDI, llamada jmsConnectionFactory
- Un JmsComponent con su propiedad connectionFactory establecida en el bean anterior
Con esta configuración, mi aplicación falla al inicio con esto:
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]
Esto proviene de este código en JmsConfiguration.java:
protected ConnectionFactory createConnectionFactory() {
ObjectHelper.notNull(connectionFactory, "connectionFactory");
return null;
}
Por lo tanto, parece que la inicialización de Spring beans no asocia / cablea los beans como se indica aquí (extraídos de la configuración completa de XML Spring previamente pegada):
<bean name="jms" class="org.apache.camel.component.jms.JmsComponent">
<property name="connectionFactory" ref="jmsConnectionFactory" />
</bean>
También intenté crear un bean JmsConfiguration intermedio y establecer la propiedad de configuración de JmsComponent, en lugar de establecer directamente la propiedad connectionFactory, pero obtengo el mismo resultado en ambas configuraciones.
Por cierto, puedo conectar los beans por código. Quiero decir que esto:
val connFactory = springContext.getBean[javax.jms.ConnectionFactory]("jmsConnectionFactory", classOf[javax.jms.ConnectionFactory])
camelContext.addComponent("jms", JmsComponent.jmsComponentAutoAcknowledge(connFactory))
funciona perfectamente bien Así que sé que estoy obteniendo ConnectionFactory de JNDI, es solo que no puedo presionar la configuración correcta de Spring para conectarlo en XML.
Necesito que esta aplicación sea muy configurable sin volver a compilar, por lo que hacer que XML funcione es una necesidad para mí.
En caso de que no esté claro, la pregunta es: ¿cómo puedo hacer que Spring configure mi bean JmsComponent, con connectionFactory configurado en la fábrica obtenida por JNDI?
EDITAR: El objetivo de usar Camel es que debería permitirme intercambiar este componente incluso por otro de un tipo diferente. Así que hoy estoy usando JMS, tal vez mañana usaré TCP. Es por eso que sería importante poder definir todo en XML.
Creo que el problema es que Akka usa su propio CamelContext en lugar del definido en la configuración de Spring. En versiones anteriores de Akka parecía que era posible establecer el contexto utilizado por Akka , pero ya no parece posible en las últimas versiones.
Me he encontrado con el mismo problema, uso la configuración basada en anotaciones, en Java (en lugar de Scala), y he solucionado el problema usando este código:
@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;
}
Esto inyecta una dependencia de ConnectionFactory definida en otro lugar y usa esto para agregar el componente jms al camelContext utilizado por Akka.
Otra solución puede ser extender el código CamelExtension de alguna manera para permitir que se inyecte la dependencia camelContext, como era posible anteriormente. Sin embargo, supongo que tenían una buena razón para el cambio, y lo dejo en paz. Creo que es para que puedan garantizar que el contexto no se puede cambiar, por lo que el Actor System siempre usa el mismo contexto de Camel, esto se basa en lo siguiente:
One CamelExtension solo se carga una vez por cada ActorSystem, lo que hace que sea seguro llamar a CamelExtension en cualquier punto de tu código para acceder a los objetos de Apache Camel asociados. Hay un CamelContext y un ProducerTemplate para cada ActorSystem que utiliza una CamelExtension.
http://doc.akka.io/docs/akka/current/scala/camel.html#CamelExtension