JMS Message Selector doesn't work in Multi-Server Environment - java

I've implemented spring jms with HornetQ. It works perfectly If Its one app-server to HornetQ. However If two app servers are accessing the HornetQ. I doesn't work. Inorder to identify different server I've introduced clientId, still it doesn't work. Here is my flow.
Prepare message for send with clientId(unique serverId which is sending message) with correlation Id and send it to HorentQ with Quename 'Q1' with ProducerCallback and wait for the response.
request.setStringProperty("clientId", clientId);
request.setJMSCorrelationID(correlationId);
In the Consumder side I've DefaultMessageListenerContainer which will be listening for the queue Q1. Once the message is received I process it and set correlaionId and clientId back in the response object and send it to another Q 'Q2'
final String clientId = request.getStringProperty("clientId");
final String correlationId = request.getJMSCorrelationID();
response.setJMSCorrelationID(correlationId);
response.setStringProperty("clientId", clientId);
In Sender Side I've another DefaultMessageListenerContainer which will be listening for the Q2. with message selector to select message based on the clientId.
<bean id="jmsContatiner"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="messageSelector" value="clientId='server1'" />
<property name="concurrency" value="1" />
<property name="messageListener" ref="responseListener" />
<property name="destinationName" value="Q2" />
<property name="connectionFactory" ref="connectionFactory" />
</bean>
The code works fine when one server is envolved. But second server's message is never received by the listener in that server, that listens to Q2(as I see in JConsole of HornetQ). Here is what happens.
Server1 sends message to Q1 and consumer receives message processes it and puts on Q2 and Server1 receives the message from Q1 by selecting message on clientId server1. However when Server2 sends the message to Q1 and consumer receives message processes it and puts on to Q2.
However Server2 listener is not able to select message based on clientId server2. When I restart the server and start of my test with server2, notw server2 works but server1 doesn't pick up the message.

Fixed the issue added cache level to CACHE_CONNECTION and client Id. It started working perfectly.
<bean id="jmsContatiner"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="messageSelector" value="clientId='server1'" />
<property name="concurrency" value="1" />
<property name="clientId" value="server1" />
<property name="cacheLevel" value="1" />
<property name="messageListener" ref="responseListener" />
<property name="destinationName" value="Q2" />
<property name="connectionFactory" ref="connectionFactory" />
</bean>

Related

Put operation does not work for IBM MQ from camel when using JMSPoolXAConnectionFactory

We are implementing XA transaction between MQ and database and trying to create a connection factory as a service in karaf as per the below link.
https://access.redhat.com/documentation/fr-fr/red_hat_fuse/7.2/html/apache_karaf_transaction_guide/using-jms-connection-factories#manual-deployment-connection-factories
The MQ we are using is IBM and we are connecting to it through camel.
The karaf service is exposed from the same bundle that is going to use it. This is done through blueprint xml file present in the src/main/resources/OSGI-INF/blueprint folder.
When we use (through JNDI) the connection factory exposed as a service for setting the connection factory to be used by the JmsComponent of camel, we are able to get message from the queue but not able to put message into the queue. There is no error when the put operation fails and hence, the database gets updated with success. This happens specifically when using JmsPoolXAConnectionFactory as the pool connection factory. If we change it to JmsPoolConnectionFactory, the put operation works and the message is added to the queue.
Below are the sample routes for get and put to queue.
GET:
from("mq:queue:{{queueName}}")
.process(new CustomProcessor1())
.to("direct:call-sp")
.end();
from("direct:call-sp")
.to("sql-stored:call-sp")
.end();
PUT:
from("vm:send")
.process(new CustomProcessor2())
.to("mq:queue:{{queueName}}")
.to("sql-stored:update-sp")
.to("vm:nextroute")
.end();
Camel JmsComponent Configuration in camel-context.xml:
<reference id="ptm" interface="org.springframework.transaction.PlatformTransactionManager" />
<reference id="connectionFactory" interface="javax.jms.ConnectionFactory" filter="(osgi.jndi.service.name=jms/mq)" availability="optional" />
<bean id="jmsConfig" class="org.apache.camel.component.jms.JmsConfiguration">
<property name="transacted" value="false" />
<property name="connectionFactory" ref="connectionFactory" />
<property name="transactionManager" ref="ptm" />
</bean>
<bean id="mq" class="org.apache.camel.component.jms.JmsComponent">
<property name="configuration" ref="jmsConfig" />
<property name="destinationResolver" ref="customDestinationResolver" />
</bean>
<bean id="customDestinationResolver" class="com.example.CustomDestinationResolver">
</bean>
Is there any put related specific configuration that we are missing?
To coordinate XA transactions, you need a transaction manager which implements the Java Transaction API (JTA).
Therefore, I think you need to use a JtaTransactionManager rather than a org.springframework.transaction.PlatformTransactionManager.
Check this out:
https://tomd.xyz/camel-xa-transactions-checklist/

How to get the file from service-activator Message object in listener class

I need to pass the file to service layer which i am receiving in SFTP path.
below is configuration and i am seeing the message receiving in my service-activator like
GenericMessage [payload=com.jcraft.jsch.ChannelSftp$2#322906a2,
headers={closableResource=org.springframework.integration.sftp.session.SftpSession#379662a7,
id=704c58e7-1d93-3bef-0330-233c0f9af55c, file_remoteDirectory=/tmp/charge/,
file_remoteFile=Charge.txt, timestamp=1594158522576}]
<bean id="sftpSessionFactory"
class="org.springframework.integration.sftp.session.DefaultSftpSessionFactory">
<property name="host" value="hostname"/>
<property name="port" value="22"/>
<property name="user" value="vkp"/>
<property name="password" value="1234"/>
<property name="allowUnknownKeys" value="true"/>
</bean>
<int-sftp:inbound-streaming-channel-adapter id="sftpAdapterAutoCreate"
session-factory="sftpSessionFactory"
filename-pattern="*.txt"
channel="receiveChannel"
remote-directory="/tmp/charge/">
</int-sftp:inbound-streaming-channel-adapter>
<int:poller fixed-rate="25000" max-messages-per-poll="1" id="shippingChargePoller" default="true"/>
<int:channel id="receiveChannel">
<int:queue/>
</int:channel>
<int:stream-transformer id="withCharset" charset="UTF-8" input-
channel="receiveChannel" />
<int:service-activator id="FeedListener" input-channel="receiveChannel" method="onMessage">
<bean class="com.listener.ChargeFeedListener"/>
</int:service-activator>
public void onMessage(Message<?> message){
System.out.println(message.toString());
System.out.println( " Received File is "+message.getPayload());
}
But i am not receiving the file in my java class . What i need to do to get the file ?
Please, read documentation: https://docs.spring.io/spring-integration/docs/current/reference/html/sftp.html#sftp-streaming. The <int-sftp:inbound-streaming-channel-adapter> is not about files. It does open an InputStream for a remote entry (probably file on SFTP) and let you to do with this stream whatever you want. For example (also according that docs), there is a StreamTransformer which let's you to read that stream into a byte[] or string if you provide a charset. If you really want to deal with files, then you need to consider to switch to the <int-sftp:inbound-channel-adapter>. That one pull the remote entry and store its content into a local file. Then that java.io.File is sent to the channel for your consideration.
I think we had a chat with you on the matter in other your question: Spring SFTP Integration is not polling the file.
Please, let us know what is wrong with our docs that confuses you so you have to raise questions like this over here.

SFTP inbound channel adapter hangs at PollableChannel receive method

My context xml reads as follows:
<bean id="defaultSftpSessionFactory"
class="org.springframework.integration.sftp.session.DefaultSftpSessionFactory">
<property name="host" value="${host}"/>
<property name="password" value="${password}"/>
<property name="port" value="${port}"/>
<property name="user" value="${username}"/>
</bean>
<int-sftp:inbound-channel-adapter id="sftpInbondAdapter"
session-factory="sftpSessionFactory"
channel="receiveChannel"
filename-pattern="*.txt"
remote-directory="/loblawln"
local-directory="/local-dir"
auto-create-local-directory="true"
temporary-file-suffix=".writing"
delete-remote-files="false">
<int:poller fixed-rate="1000" max-messages-per-poll="1"/>
</int-sftp:inbound-channel-adapter>
<int:channel id="receiveChannel">
<int:queue/>
</int:channel>
The java file reads like this:
PollableChannel localFileChannel = context.getBean("receiveChannel", PollableChannel.class);
SourcePollingChannelAdapter adapter = context.getBean(SourcePollingChannelAdapter.class);
adapter.start();
System.out.println("Adapter started...");
Message<?> received = localFileChannel.receive();
System.out.println("Received first file message: " + received);
I have referred to the implementation provided by Gary Russell at Github and also followed spring docs for sftp integration. I think i am missing something important.
Moreover logs are also not showing anything at all.
The problem got solved by changing the key to read username.
This seemed a bit weird to me when I debugged the code and read values in defaultSessionFactory instance, I found it was not reading the username from the properties file, rather it read the value correspnding to System.getProperty("user.name"). When I changed the key from {username} to some thing else like {sftp.username} it read it correctly.

Spring Integration Messages sent but not received with activeMQ

I would like to create a simple test when messages are sent to / received from FORWARD queue.
Upon received message a service should be invoked.
Unfortunately, the messages are sent through Message<?> message = MessageBuilder.withPayload(params); forwardGateway.sendPremMessage(message); but not received.
This is my config:
<!-- SENDER -->
<si:gateway id="forwardGateway"
service-interface="com.ucware.ucpo.forward.jms.MessageGateway"
default-request-channel="inputChannel"/>
<si:channel id="inputChannel"/>
<!-- Subscriber to a channel -->
<int-jms:outbound-channel-adapter
channel="inputChannel"
connection-factory="connectionFactory"
destination-name="FORWARD" />
<!-- RECEIVER -->
<int:channel id="jmsInChannel"/>
<!-- Subscriber to jmsInChannel. Used instead of inboud channel adapter -->
<int-jms:message-driven-channel-adapter id="messageDrivenAdapter"
channel="jmsInChannel" destination-name="FORWARD" connection-factory="connectionFactory"
concurrent-consumers="1" auto-startup="true" acknowledge="auto"/>
// This service should be invoked but it is not
<si:service-activator id ="activator"
input-channel="jmsInChannel"
ref="messageService"
method="process"/>
The service is defined as :
#MessageEndpoint
public class MessageService {
public void process(Message<?> message )
}
and the gateway as :
public interface MessageGateway {
#Gateway
public void sendPremMessage(Message<?> message);
}
Your configuration works perfectly fine for me; I suggest you turn on TRACE level logging to see the message flow on both sides. (TRACE gives details of the message listener container activity on the receiving side).
If you are using in-memory ActiveMQ you need to be careful about the timing - don't send a message until the container has started. Alternatively be sure to use a CachingConnectionFactory.
<bean id="connectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">
<property name="targetConnectionFactory">
<bean class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="vm://localhost"/>
</bean>
</property>
</bean>
That way, the broker will stay running after the send, even if the container hasn't started yet.

Problem configuring Spring's MailSender for our SMTP-server (but GMail works)

I have some problems sending mails through SMTP using Spring's MailSender interface and the concrete implementation JavaMailSenderImpl. I'm able to send mail through GMail, but not through our company SMTP-server (Postfix).
Correct configurations
To see that I have the correct configuration I used the excellent mail sender ssmtp. It's a simple utility (which is able to emulate Sendmail) and is used solely for sending mail through SMTP.
Below are the two commands I used to send mail. The first one is for GMail and the second one for our company SMTP-server. Both mails arrived as they should and thus the configuration files that follow are correct.
$ ssmtp -C gmail-smtp.conf john.doe#gmail.com < gmail-message.txt
$ ssmtp -C other-smtp.conf john.doe#thecompany.net < other-message.txt
The contents of the ssmtp configuration files and the message files are listed below. How the configuration file is structured can be seen at: http://linux.die.net/man/5/ssmtp.conf:
gmail-message.txt:
To: john.doe#gmail.com
From: john.doe#gmail.com
Subject: Sent using the SMTP-server of GMail
Some content.
gmail-smtp.conf:
mailhub=smtp.gmail.com:587
UseSTARTTLS=yes
AuthUser=john.doe#gmail.com
AuthPass=john_password
other-message.txt:
To: john.doe#thecompany.net
From: john.doe#thecompany.net
Subject: Sent using the SMTP-server of TheCompany
Some content.
other-smtp.conf:
# No username or password = no authentication
hostname=thecompany.net
mailhub=mail.thecompany.net:25
MailSender configuration which works against GMail
I'm sucessful in sending mail through GMail using this Spring MailSender configuration:
...
<bean id="mailSender" class ="org.springframework.mail.javamail.JavaMailSenderImpl" >
<property name="host" value="smtp.gmail.com" />
<property name="port" value="587" />
<property name="username" value="john.doe#gmail.com" />
<property name="password" value="john_password" />
<property name="javaMailProperties">
<props>
<prop key="mail.smtp.starttls.enable">true</prop>
</props>
</property>
</bean>
...
The problem (sending through the company SMTP-server)
With this MailSender configuration:
...
<bean id="mailSender" class ="org.springframework.mail.javamail.JavaMailSenderImpl" >
<property name="host" value="mail.thecompany.net" />
<property name="port" value="25" />
</bean>
...
I get this exception:
org.springframework.mail.MailSendException; nested exceptions (1) are:
Failed message 1: javax.mail.SendFailedException: Invalid Addresses;
nested exception is:
com.sun.mail.smtp.SMTPAddressFailedException: 504 5.5.2 <rat>: Helo command rejected: need fully-qualified hostname
at org.springframework.mail.javamail.JavaMailSenderImpl.doSend(JavaMailSenderImpl.java:422)
at org.springframework.mail.javamail.JavaMailSenderImpl.send(JavaMailSenderImpl.java:308)
at org.springframework.mail.javamail.JavaMailSenderImpl.send(JavaMailSenderImpl.java:297)
... (The rest are methods I've created, which are irrelevant)
I also get 504 5.5.2 : Helo command rejected: need fully-qualified hostname if I remove hostname=thecompany.net from other-smtp.conf using ssmtp. I guess I have to provide the hostname somehow. My computers name is rat but it seems like it wants thecompany.net.
Any and all help appreciated!
To add to complete the previous answer by matt b, the correct option of the javamail is smtp.mail.localhost
<property name="javaMailProperties">
<props>
<prop key="mail.smtp.localhost">thecompany.net</prop>
</props>
</property>
Try to add the property "mail.smtp.localhost" with correct value (thecompany.net).
Can you pass in the extra properties (hostname) you need to set with your SMTP server in the javaMailProperties property?
<bean id="mailSender" class ="org.springframework.mail.javamail.JavaMailSenderImpl" >
<property name="host" value="mail.thecompany.net" />
<property name="port" value="25" />
<!-- addition: -->
<property name="javaMailProperties">
<props>
<prop key="hostname">thecompany.net</prop>
</props>
</property>
</bean>
Not quite sure what the correct key name is for hostname in the JavaMail API.
I think its an address resolution problem in your case. make sure the mail server is accessible using the given name from the machine you've deployed this code on. if not just add an entry to your the hosts file.
SMTPTransport mailFrom() sets the sender's address in the following order:
SMTPMessage.getEnvelopeFrom()
mail.smtp.from property
From: header in the message
System username using the InternetAddress.getLocalAddress() method
You just need to add property from in javaMailProperies to your email address

Categories

Resources