Bean definition example for JndiDestinationResolver using jndi look up - spring

I'm working on sending and receiving messages to/from an IBM MQ queue using JmsTemplate. My application is installed on a WebSphere application server 8.0 and, in order to retrieve the connection, I use a jndi lookup.
I have 6 queues from where I need to pick/drop xmls depending on scenarios. Also I have added these queue in WAS. I need help to understand two things:
I should use DynamicDestinationResolver or JndiDestinationResolver?
As per my understanding I should use JndiDestinationResolver; if that is right how I can define that in my context file and refer jndi-lookup for each queue so that I can retrieve it from my code while using jmsTemplate send/receive?
Please see below my application context file:
<bean id="jmsQueueConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="jms/CPC.TapQueueConnCPC" />
<property name="lookupOnStartup" value="false" />
<property name="cache" value="true" />
<property name="proxyInterface" value="javax.jms.QueueConnectionFactory" />
</bean>
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="jmsQueueConnectionFactory" />
<property name="receiveTimeout" value="10000" />
<property name="sessionAcknowledgeMode" value="1" />
<property name="destinationResolver" ref="jmsDestResolver"/>
</bean>
<bean id="fileTransferServiceImpl" class="org.kp.cpc.service.FileTransferServiceImpl" >
<constructor-arg name="jmsTemplate" ref="jmsTemplate" />
</bean>
<bean id="jmsDestResolver" class=" org.springframework.jms.support.destination.JndiDestinationResolver"/>
<jee:jndi-lookup id="drop278" jndi-name="jms/CPC.SEND.AUTHREQ278" />
<jee:jndi-lookup id="drop275" jndi-name="jms/CPC.SEND.AUTHREQ275" />
<jee:jndi-lookup id="recev278" jndi-name="jms/CPC.RECE.AUTHREQ278" />
<jee:jndi-lookup id="recev275" jndi-name="jms/CPC.RECE.AUTHREQ275" />
<jee:jndi-lookup id="preAuthStatus" jndi-name="jms/CPC.RECE.PREAUTH.STSUPD278"/>
<jee:jndi-lookup id="succ278" jndi-name="jms/CPC.RECE.SUCC.AUTHRESP278" />

The whole point of the JndiDestinationResolver is that you don't need to do manual lookups. In other words when using the JndiDestinationResolver you don't need the <jee:jndi-lookup /> as that is al handled by the DestinationResolver.
The name of the destination would be the JNDI name. So in your JMS code you would use the following.
jmsTemplate.convertAndSend("jms/CPC.SEND.AUTHREQ278", "Your-Message-Here");
The JndiDestinationResolver will use the name of the destination to do a JNDI lookup.
If you really want to keep the JNDI names out of your code and want to use the <jee:jndi-lookup /> then use the [BeanFactoryDestinationResolver]. This will use the name of the destination to lookup a bean from the BeanFactory (in this case the ApplicationContext). Your JMS code would then point to the bean name instead of the JNDI name.
jmsTemplate.convertAndSend("drop278", "Your-Message-Here");
So which one to use depends on your preferences.

Related

ActiveMQ 5.15.5, Camel ActiveMQ component brokerURL to connect two standalone brokers

I have two standalone broker consuming message from another system.
The client consumer uses Camel to consume messages, below is the part of the configuration where it used camel-activemq component.
I am trying to provide both the host information within the borkerURL but throwing some exception.
using jars of
spring-core 4.2.4,
spring-boot: 1.3.1,
activemq-camel: 5.12.1,
activemq-broker: 5.12.1, etc.
...
<bean id="jmsConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="(tcp://host1:61616,tcp://host2:61616)?wireFormat.maxInactivityDuration=500000" />
</bean>
<bean id="jmsPooledConnectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory" init-method="start" destroy-method="stop">
<property name="maxConnections" value="10" />
<property name="connectionFactory" ref="jmsConnectionFactory" />
</bean>
<bean id="jmsConfig" class="org.apache.camel.component.jms.JmsConfiguration">
<property name="connectionFactory" ref="jmsPooledConnectionFactory" />
<property name="concurrentConsumers" value="1" />
<property name="maxConcurrentConsumers" value="1" />
</bean>
<bean id="jms" class="org.apache.activemq.camel.component.ActiveMQComponent">
<property name="configuration" ref="jmsConfig" />
<property name="transacted" value="true" />
</bean>
...
The process throw exception, when i run the camel main with the context.
:PropertyAccessException 1: org.springframework.beans.MethodInvocationException: Property 'brokerURL' threw exception; nested exception is java.lang.IllegalArgumentException: Invalid broker URI: (tcp://host1:61616,tcp://host2:61616)?wireFormat.maxInactivityDuration=500000
Is there any way to pass in the borker URL of two standalone brokers?
If the brokers are part of a failover system, use the "failover" protocol of the ActiveMQ client.
If the brokers have different queues, create two different commponents with different configurations and broker URLs.
refer to jms1 or jms2 in your route.

Connecting to multiple ActiveMQ Servers using Spring Integration XML Configuration

I have been trying for couple days to set up multiple ActiveMQ connections in Spring Integration using XML configuration.
I am using spring boot, SI looks for a bean called jmsConnectionFactory in the context and uses it. But what if I have to send/listen to jms messages from/to different ActiveMQ servers?
What I have right now is this:
<bean id="jmsConnectionFactory1"
class="org.springframework.jms.connection.CachingConnectionFactory">
<property name="targetConnectionFactory">
<bean class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://localhost:61616" />
</bean>
</property>
<property name="sessionCacheSize" value="10" />
<property name="cacheConsumers" value="false" />
</bean>
<bean id="jmsConnectionFactory2"
class="org.springframework.jms.connection.CachingConnectionFactory">
<property name="targetConnectionFactory">
<bean class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://192.168.1.59:61616" />
</bean>
</property>
<property name="sessionCacheSize" value="10" />
<property name="cacheConsumers" value="false" />
</bean>
...
<jms:message-driven-channel-adapter channel="jmsInChannel" destination-name="queue.demo" />
<int:channel id="jmsInChannel" />
...
When trying to start the spring boot app I get this error:
***************************
APPLICATION FAILED TO START
***************************
Description:
A component required a bean named 'jmsConnectionFactory' that could not be found.
The following candidates were found but could not be injected:
- Bean method 'jmsConnectionFactory' in 'ActiveMQXAConnectionFactoryConfiguration' not loaded because #ConditionalOnClass did not find required class 'javax.transaction.TransactionManager'
Action:
Consider revisiting the entries above or defining a bean named 'jmsConnectionFactory' in your configuration.
I came across this solution, https://stackoverflow.com/a/43401330/3367392 it's a java configuration. Also I looked into this but it's using camel https://stackoverflow.com/a/13288312/3367392
Is there a way to achieve the same for Spring Integration using XML configuration?
Ok so I got it, for a simple case I just had to add the right connectionFactory to the adapters like this
<jms:message-driven-channel-adapter channel="jmsInChannel"
destination-name="queue.demo"
connection-factory="jmsConnectionFactory1" />

How to set rabbitmq connection properties using JNDI variables in Spring xml config?

I have an application in Spring with is using RabbitMQ. I'm using xml based context configuration and rabbit namespace in this xml.
So far it looked as this:
<rabbit:connection-factory id="rabbitConnectionFactory" host="localhost" port="5672"/>
but now I want to use JNDI variables for host and port properties.
I know how to use JNDI when I have "classic" bean definitions but I have know idea how to use JNDI and rabbit namespace at the same time.
I wasn't able to define any child elements of rabbit:connection element. I get an error saying that it can't have any child nodes.
Any help appreciated :)
EDIT
This is how I use JNDI lookup in "standard" beans (with no special namespace)
<bean id="connector" class="com.foo.ConnectionProvider">
<constructor-arg name="url">
<jee:jndi-lookup expected-type="java.lang.String" jndi-name="java:comp/env/service/url"/>
</constructor-arg>
</bean>
And in META-INF/context.xml:
<ResourceLink name="service/url" global="service/url" type="java.lang.String"/>
But as I said I don't know how to accomplish it using rabbit namespace. Have tried googling with no success..
I figured out I can use "standard" bean definition. I'm not completely happy with the solution, but at least, it works.
<bean id="rabbitConnectionFactory" class="org.springframework.amqp.rabbit.connection.CachingConnectionFactory">
<constructor-arg name="hostname" >
<jee:jndi-lookup expected-type="java.lang.String"
jndi-name="java:comp/env/rabbit/host"/>
</constructor-arg>
<constructor-arg name="port" >
<jee:jndi-lookup expected-type="java.lang.Integer"
jndi-name="java:comp/env/rabbit/port"/>
</constructor-arg>
<property name="username" value="guest"/>
<property name="password" value="guest"/>
</bean>
<rabbit:template id="amqpTemplate" connection-factory="rabbitConnectionFactory" exchange="mcs-notifications.topic"/>
<rabbit:admin connection-factory="rabbitConnectionFactory"/>
Another option is to use RMQConnectionFactory
<Resource name="jms/rabbitConnectionFactory" type="com.rabbitmq.jms.admin.RMQConnectionFactory"
factory="org.apache.naming.factory.BeanFactory" username="guest"
password="guest" />

<jee:jndi-lookup default-value and the use of classpath

I am really stuck on this one... Help! :)
I am using j2ee:jndi lookup for a property file. The following works fine:
<bean class="org.springframework.core.io.FileSystemResource">
<constructor-arg>
<jee:jndi-lookup id="myProps" jndi-name="myProps" resource-ref="true" />
</constructor-arg>
</bean>
However, I want to handle the case where the jndi lookup fails but will fall back on a default file located in WEB-INF/classes folder. If I use the default-value as below, the webapp throws an exception complaining that it cannot find the file "classpath:myprops.properties"
<bean class="org.springframework.core.io.FileSystemResource">
<constructor-arg>
<jee:jndi-lookup id="myProps" jndi-name="myProps" resource-ref="true"
default-value="classpath:myprops.properties" />
</constructor-arg>
</bean>
However, if I hard-code a specific path for default-value, then it works fine, but that is unacceptable as a final solution.
Thus, my issue is how to use "classpath:" so that it gets properly resolved?
This is the overall usage I'm employing:
<bean id="authServerProperties"
class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="ignoreResourceNotFound" value="true"/>
<property name="location">
<bean class="org.springframework.core.io.FileSystemResource">
<constructor-arg>
<jee:jndi-lookup id="myProps" jndi-name="myProps" resource-ref="true"
default-value="classpath:myprops.properties" />
</constructor-arg>
</bean>
</property>
.....
</bean>
Let Spring use its built-in PropertyEditor support to decide on the type of resource, rather than supplying an explicit FileSystemResource bean as this won't work with classpath resources (it needs to be configured with a path on the file system). Instead you should use something like
<bean id="authServerProperties"
class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="ignoreResourceNotFound" value="true"/>
<property name="location" ref="myProps" />
</bean>
<jee:jndi-lookup id="myProps" jndi-name="myProps" resource-ref="true"
default-value="classpath:myprops.properties"/>
Here we are setting the location to be a string value and allowing Spring to convert that to the appropriate resource type, so if you have
<env-entry>
<env-entry-name>myProps</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>file:///Users/something/myProps.properties</env-entry-value>
</env-entry>
in your web.xml, it will use a UrlResource with the given file URL, otherwise it will create a ClasspathResource to look for the file myprops.properties.

Error upon integrating JNDI with Spring

This was my first attempt at Spring with JNDI but getting the below mentioned exception when trying to create the ApplicationContext like:
ApplicationContext context = new ClassPathXmlApplicationContext("master-job.xml");
The Spring configuration file is as follows:
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="/jdbc/Eqpstatus"/>
<property name="resourceRef" value="true" />
</bean>
<bean id="masterDao" class="com.dao.MasterDao">
<property name="dataSource" ref="dataSource"/>
</bean>
On Server i have the required resource entry for the JNDI name.
<Resource auth="Container" driverClassName="oracle.jdbc.OracleDriver"
maxActive="10" maxIdle="2" maxWait="10000" name="jdbc/Eqpstatus"
password="xxxx" type="javax.sql.DataSource"
url="jdbc:oracle:thin:#(DESCRIPTION=(LOAD_BALANCE=on)(ADDRESS=(PROTOCOL=TCP)(HOST=xxxx) (PORT=1521))(ADDRESS=(PROTOCOL=TCP)(HOST=xxxx) (PORT=1521))(CONNECT_DATA=(SERVICE_NAME=xyz)))"
username="xxx"/>
The error i see is:
javax.naming.NameNotFoundException: Name jdbc is not bound in this Context
Would highly appreciate any inputs on this as am new to Spring-JNDI integration.
First, I think you should use the dedicated tag instead of declaring a "JndiObjectFactoryBean" bean :
<!-- JNDI DataSource for J2EE environments -->
<jee:jndi-lookup id="dataSource" jndi-name="java:jdbc/rppsDS-PUB-PROTO" default-ref="localDataSource" />
<!-- local dataSource for JUnit integration tests -->
<bean id="localDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="maxActive" value="100"/>
<property name="maxWait" value="1000"/>
<property name="poolPreparedStatements" value="true"/>
<property name="defaultAutoCommit" value="true"/>
</bean>
Then, you need a jndi.properties file (which could be directly in application server dir such as JBoss) with a content similar to :
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
java.naming.provider.url=localhost:1099
If you are using Tomcat and everything is fine with your Tomcat configuration; this should be enough:
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/dataSource" />
Where jdbc/dataSource is the name defined in your Tomcat config.
EDIT:
My bad, forgot about the jndi.properties; there are two possibilities:
either provide a jndi.properties on your classpath or
set the values in a in the <jee:jndi-environment/> element of <jee:jndi-lookup /> (see reference)
The java.naming.factory.initial property needs to be set for sure, to something like org.apache.naming.java.javaURLContextFactory, possibly some other values as well, like:
java.naming.factory.url.pkgs=org.apache.naming
java.naming.factory.url.pkgs.prefixes=org.apache.naming
java.naming.provider.url=org.apache.naming
Also see reference.

Resources