Spring integration outbound channel adapters not closing the open sockets and leaving the file handles open - spring

We are using spring integration adapters for file ftp in our project, the problem we are facing is, the adapters are not closing the open socket connections.
As a result, other modules which are in the same managed server are failing with "Too many open files" socket connection exception. Is there a way to close the unused open socket connections from the channel adapters Or Can we get the underlying jsch connections and close the sockets from sftp channel adapters.
We have tried caching session factory and it did not close the open sockets. The file handles kept on piling up. Thanks in advance for the inputs.
We have two xmls one with outboundAdapter and the other with InboundAdapter. These two are in different xmls as they are different jobs that are run using spring batch. We are expected to send files to a location.
We are using spring batch 2.2.0 and spring integration 2.1.6 and spring integration 2.1.6.
Here is the configuration:
We have one session factory and it is wrapped by cachingSession factory:
<beans:bean id="sftpSessionFactory"
class="org.springframework.integration.sftp.session.DefaultSftpSessionFactory">
<beans:property name="host" value="hostname"/>
<beans:property name="privateKey" value="somepath"/>
<beans:property name="port" value="22"/>
</beans:bean>
<bean id="cachingSessionFactory"
class="org.springframework.integration.file.remote.session.CachingSessionFactory">
<constructor-arg ref="sftpSessionFactory"/>
<constructor-arg value="10"/>
<property name="sessionWaitTimeout" value="1000"/>
</bean>
**and then we have a channel**
<int:channel id="ftpChannel" />
**and then we have the following outbound Channel adapter**
<int-sftp:outbound-channel-adapter id="sftpOutboundAdapter"
session-factory="cachingSessionFactory"
channel="inputChannel"
charset="UTF-8"
use-temporary-filename="false"/>
**With the above configuration we are using the ftpChannel to send the files by constructing a payload like this:**
message = MessageBuilder.withPayLoad(f).build() // MessageBuilder is //org.springframework.integration.support.MessageBuilder and f is the file
ftpChannel.send(message)
**In another inbound job, the following is the configuration of adapters:
Session factory:**
<beans:bean id="sftpSessionFactory2"
class="org.springframework.integration.sftp.session.DefaultSftpSessionFactory">
<beans:property name="host" value="hostname"/>
<beans:property name="privateKey" value="somepath"/>
<beans:property name="port" value="22"/>
</beans:bean>
**Caching session factory:**
<bean id="cachingSessionFactory2"
class="org.springframework.integration.file.remote.session.CachingSessionFactory">
<constructor-arg ref="sftpSessionFactory2"/>
<constructor-arg value="10"/>
<property name="sessionWaitTimeout" value="1000"/>
</bean>
**and another channel:**
<int:channel id="ftpChannel2" />
**Now we have the following adapter in this xml:**
<int-sftp:outbound-channel-adapter id="sftpInboundAdapter"
session-factory="cachingSessionFactory2"
channel="inputChannel"
charset="UTF-8"
use-temporary-filename="false"/>
With this configuration in the above xml we are trying to get session from the cachingSessionFactory configured in the first xml, getting a session out of it, getting a list of files and then sending some files with ftpChannel2.send() and doing session.close() in finally block. When I do session.isOpen() in after session.close(), I see true being returned.
With these two jobs, I could see a lot of open file handles, which are socket connections and I am absolutely clueless as to how I can close those opened sockets.

The session will be closed when the operation is complete as long as you don't use the caching session factory - that is intended to keep the session open for the next use.
If you turn on DEBUG logging, you should get some insight into what it wrong.
EDIT
Just ran this with no problems:
#Test
public void test() throws Exception {
DefaultFtpSessionFactory sf = new DefaultFtpSessionFactory();
sf.setHost("10.0.0.3");
sf.setUsername("ftptest");
sf.setPassword("ftptest");
FtpSession session = sf.getSession();
Thread.sleep(10000);
session.close();
assertFalse(session.isOpen());
System.out.println("closed");
Thread.sleep(10000);
}
During the first sleep netstat -ntp shows the socket open; socket is gone after the close.
The session is the socket...
public void disconnect() throws IOException
{
closeQuietly(_socket_);
...
}
EDIT2
I had forgotten that with 2.1.x there was the cache-sessions attribute (2.1.x is very old).
I just tested with this (and 2.1.6) ...
<bean id="sftpSessionFactory"
class="org.springframework.integration.sftp.session.DefaultSftpSessionFactory">
<property name="host" value="10.0.0.3" />
<property name="privateKey" value="file:/somPathTo/.ssh/id_rsa" />
<property name="port" value="22" />
<property name="user" value="ftptest" />
</bean>
<int:channel id="inputChannel" />
<int-sftp:outbound-channel-adapter id="sftpOutboundAdapter"
session-factory="sftpSessionFactory"
channel="inputChannel"
charset="UTF-8"
cache-sessions="false"
use-temporary-file-name="false"
remote-directory="." />
public class Main {
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("context.xml");
File f = new File("foo.txt");
FileOutputStream fos = new FileOutputStream(f);
fos.write("bar".getBytes());
fos.close();
context.getBean("inputChannel", MessageChannel.class).send(MessageBuilder.withPayload(f).build());
System.out.println("Sleeping - check socket");
Thread.sleep(60000); // check socket
context.close();
System.exit(0);
}
}
With no problems (the socket is closed); if I set the cache-sessions to true, the socket remains open as expected.
I do notice you don't have a remote-directory attribute - that's illegal:
exactly one of 'remote-directory' or 'remote-directory-expression' is required on a remote file outbound adapter

Related

How to stop poller in spring integration while processing Files

<bean id="inFileHandler"
class="com.yahoo.FileProcessHandler" />
<bean id="executorService" class="java.util.concurrent.Executors" factory-method="newSingleThreadExecutor" destroy-method="shutdownNow" />
<int:channel id="inChannel" />
<int:channel id="outChannel">
<int:queue capacity="5" />
</int:channel>
<bean id="sftpFactory"
class="org.springframework.integration.sftp.session.DefaultSftpSessionFactory">
<property name="host" value="${SFTP_HOST}"></property>
<property name="port" value="${SFTPPORT}"></property>
<property name="user" value="${SFTPUSERNAME}"></property>
<property name="password" value="${SFTPPASSWORD}"></property>
</bean>
<sftp:inbound-channel-adapter id="ftpInBound" channel="inChannel"
session-factory="sftpFactory"
delete-remote-files="true" remote-directory="/Files"
local-directory="file:C:/Bhaji">
<int:poller id="poller" fixed-rate="10000"/>
</sftp:inbound-channel-adapter>
<int:service-activator input-channel="inChannel"
output-channel="nullChannel" ref="inFileHandler" method="handler" />
and the FileProcess handler is
#Autowired
private ExecutorService executorService;
private static Logger log = LoggerFactory.getLogger(FileProcessHandler.class);
public File handler(File input) {
**//Doing some time taking process**
return input;
}
here while doing the time taking process i don't want to poll the SFTP inbound-channel-adapter. after completion of time taken process the poller should start automatically.
Is there any way to do this ? is there any way to achieve it?
What are using the executor service for - let the flow run on the poller thread and the next poll won't happen until the current poll completes.
Looks like it would be enough for you to use fixed-delay instead of fixed-rate on the <poller> and do the task in a TaskScheduler Thread. In this case you will have only one process at a time. And the next one will start only after the finish of previous.

How to stop/start spring DefaultMessageListenerContainer?

I have developed project using Spring JMS to receive the message from Queue. and it is deployed Websphere application Server (WAS 7.5) clustered environment.
it is working fine once it is deployed in server.Later i have update my logger information and deployed in to server. it seems server not picking the latest code base. Even though i have stop/start the cluster.
Please refer below config xml.
<bean id="connectionFactory" class="com.ibm.mq.jms.MQQueueConnectionFactory">
<property name="hostName" value="${hostName}"/>
<property name="port" value="${port}"/>
<property name="queueManager" value="${queueManager}"/>
<property name="transportType" value="${transportType}"/>
<property name="channel" value="${channel}"/>
</bean>
<jms:listener-container container-type="default"
connection-factory="connectionFactory" acknowledge="auto" concurrency="5" >
<jms:listener destination="DEV.TESTQUEUE" ref="jmsMessageListener"
</jms:listener-container>
<bean id="jmsMessageListener" class="JmsMessageListener"/>
public class JmsMessageListener implements MessageListener {
public void onMessage(Message message) {
}
}
Could you please advise how to stop/start the container?
Here is my solution:
final Map<String, DefaultMessageListenerContainer> containers = ctx.getBeansOfType(DefaultMessageListenerContainer.class);
if (containers != null && !containers.isEmpty()) {
for (DefaultMessageListenerContainer container : containers.values()) {
container.stop();
}
}
At last i found answer.
Default executor of DMLC is SimpleAsyncTaskExecutor.
SimpleAsyncTaskExecutor: This implementation does not reuse any threads, rather it starts up a new thread for each invocation.
However, it does support a concurrency limit which will block any invocations that are over the limit until a slot has been freed up.
If you’re looking for true pooling, keep scrolling further down the page. Spring Framework Task Execution and Scheduling.
So thread keep on running in container. this root cause of my issue. then i have restarted my JVM(with the support WAS Admin) and implemented ThreadPoolExecutor.
<jms:listener-container container-type="default"
connection-factory="connectionFactory" acknowledge="auto" concurrency="5" task-executor="taskExecutor">
<jms:listener destination="topCli_Service" ref="jmsMessageListener"
</jms:listener-container>
<bean id="taskExecutor"
class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="5" />
<property name="maxPoolSize" value="10" />
<property name="WaitForTasksToCompleteOnShutdown" value="true" />
</bean>

Camel JMS with CLIENT_ACKNOWLEDGE mode not working

I am unable to acknowledge JMS message with CLIENT_ACKNOWLEDGE mode in camel. After digging a into the stack trace I found that message.acknowledge() in AbstractMessageListenerContainer
is always being executed which causes the auto-ack behavior. Am I configured anything wrong?
org.springframework.jms.listener.AbstractMessageListenerContainer.commitIfNecessary(Session, Message)
protected void commitIfNecessary(Session session, Message message) throws JMSException {
// Commit session or acknowledge message.
if (session.getTransacted()) {
// Commit necessary - but avoid commit call within a JTA transaction.
if (isSessionLocallyTransacted(session)) {
// Transacted session created by this container -> commit.
JmsUtils.commitIfNecessary(session);
}
}
else if (message != null && isClientAcknowledge(session)) {
message.acknowledge();
}
}
Spring Configuration
<bean id="jms" class="org.apache.camel.component.jms.JmsComponent">
<property name="connectionFactory" ref="cachedConnectionFactory" />
<property name="asyncConsumer" value="true" />
<property name="acknowledgementModeName" value="CLIENT_ACKNOWLEDGE" />
</bean>
<bean id="cachedConnectionFactory"
class="org.springframework.jms.connection.CachingConnectionFactory">
<property name="targetConnectionFactory" ref="connectionFactory" />
<property name="sessionCacheSize" value="30" />
</bean>
Camel Route
from(jmsTerminalRequest).routeId("generic-jms-inbound").setExchangePattern(ExchangePattern.InOnly).threads(5, 20, "generic-jms-inbound").bean(clientAckProcessor).to("...")
What you can do is specifying acknowledgement mode within route consuming endpoint definition:
from("...?maxConcurrentConsumers=20&acknowledgementModeName=CLIENT_ACKNOWLEDGE")
.bean(clientAckProcessor)
.to("...")

ActiveMQ, Camel, Spring, simple route not working

I am having some trouble with a simple camel route that should be grabbing a message from an ActiveMQ topic that I have and then printing out the contents of the messages to the console through the use of log.
Right now all that it is is the camel-context.xml, and a java class that is producing the topic in ActiveMQ and adding a simple string message to the queue. I am using the ActiveMQ interface to check to see if the topic is being created, and it is, but my message is not being added to the topic nor is it being routed through the camel route. Running main I can get the output of my sys out to the console, and I see that 1 message is "enqueued" and 1 message is "dequeued" in the activemq interface. I just do not get any output to the console from the "log message" in my route.
Any help or tips would be greatly appreciated since I am new to all 3 of these technologies, and I just want to get this simple "Hello World" working.
Thank you! The two files are found below:
After further testing I think that it just has something to do with the way that I am trying to log the contents of the message, because I know that it is picking up my camel route because I added a second topic and told the camel route to route the messages to it like the following:
to uri="activemq:topic:My.SecondTestTopic"
and I am able to see if being redirected to that queue in the activeMQ interface.
TestMessageProducer.java
package com.backend;
import javax.jms.*;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
public class TestMessageProducer {
private static String url = ActiveMQConnection.DEFAULT_BROKER_URL;
public static void main(String[] args) throws JMSException {
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url);
Connection connection = connectionFactory.createConnection();
connection.start();
Session session = connection.createSession(false,
Session.AUTO_ACKNOWLEDGE);
Topic topic = session.createTopic("My.TestTopic");
MessageProducer producer = session.createProducer(topic);
TextMessage message = session.createTextMessage();
message.setText("THIS IS A TEST TEXT MESSAGE BEING SENT TO THE TOPIC AND HOPEFULLY BEING PICKED UP BY THE" +
"CAMEL ROUTE");
producer.send(message);
System.out.println("Sent message '" + message.getText() + "'");
connection.close();
}
}
Camel-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<spring:beans xmlns:spring="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://camel.apache.org/schema/spring"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:alch="http://service.alchemy.kobie.com/"
xsi:schemaLocation="http://www.springframework.org/schema/beans classpath:META-INF/spring/spring-beans.xsd
http://camel.apache.org/schema/spring classpath:META-INF/spring/camel-spring.xsd">
<!-- load properties -->
<spring:bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<spring:property name="locations" value="file:backend.properties" />
</spring:bean>
<spring:bean id="properties"
class="org.apache.camel.component.properties.PropertiesComponent">
<spring:property name="location" value="file:backend.properties" />
</spring:bean>
<spring:bean id="jmsConnectionFactory"
class="org.apache.activemq.ActiveMQConnectionFactory">
<spring:property name="brokerURL" value="tcp://0.0.0.0:61616?useLocalHost=true" />
</spring:bean>
<spring:bean id="pooledConnectionFactory"
class="org.apache.activemq.pool.PooledConnectionFactory">
<spring:property name="maxConnections" value="8" />
<spring:property name="maximumActive" value="500" />
<spring:property name="connectionFactory" ref="jmsConnectionFactory" />
</spring:bean>
<spring:bean id="jmsConfig"
class="org.apache.camel.component.jms.JmsConfiguration">
<spring:property name="connectionFactory" ref="pooledConnectionFactory"/>
<spring:property name="transacted" value="false"/>
<spring:property name="concurrentConsumers" value="1"/>
</spring:bean>
<spring:bean id="activemq"
class="org.apache.activemq.camel.component.ActiveMQComponent">
<spring:property name="configuration" ref="jmsConfig"/>
</spring:bean>
<!-- camel configuration -->
<camelContext xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="activemq:topic:My.TestTopic"/>
<log message="Output of message from Queue: ${in.body}"/>
<to uri="activemq:topic:My.SecondTestTopic" />
</route>
And you have started the Camel application first. Eg as you send non persistent messages to a topic. And if there is no active subscribers when sending, then the messages is not received by anybody. You may want to use persistent durable topic instead.
I suspect you are creating two seperate instances of an ActiveMQ broker. Can you update your TestMessageProducer to use the URL tcp://localhost:61616 ? Also, can you use jconsole to check the topic activity in the activemq instance on both VMs ?
=== UPDATE ===
I missed the bit about you verifying that the 2nd topic did receive the message, so your route is working.... Must be the logger. If you have the camel source code in your IDE, you could turn the debugger on and place a break point on
org.apache.camel.component.log.LogProducer
.process(Exchange exchange, AsyncCallback callback)
to see what happens and if it is called. Which logging package do you have configured ?

Spring-JMS(Websphere MQ)

I have the below configurations in Spring , it is working fine but performance is too low (it takes 1 min for 20 messages). Can you please suggest me changes to increase the performance.
<bean id="jmsConnectionFactory" class="com.ibm.mq.jms.MQQueueConnectionFactory">
<property name="transportType"><value>1</value></property>
<property name="queueManager"><value></value></property>
<property name="hostName"><value></value></property>
<property name="port"><value></value></property>
<property name="channel"><value></value></property>
<property name="clientId"><value></value></property>
</bean>
<bean id="SenderJMSTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory"><ref bean="jmsConnectionFactory" /> </property>
<property name="pubSubDomain"><value>false</value></property>
<property name="defaultDestination"><ref bean="senderQueue" /></property>
</bean>
<bean id="senderQueue" class="com.ibm.mq.jms.MQQueue">
<constructor-arg value="TEST" />
<property name="baseQueueManagerName"><value>tree.queue.manager</value></property>
<property name="baseQueueName"><value>ORANGE.QUEUE</value></property>
</bean>
<bean id="jmsSender" class="org.tree.jms.spring.JMSSender">
<property name="jmsTemplate"><ref bean="SenderJMSTemplate"/></property>
</bean>
I am calling from spring as
JMSSender obj = (JMSSender) context.getBean("jmsSender");
And My Sender program is :
#Cacheable("message")
public void sendMesage() {
jmsTemplate.send(new MessageCreator() {
public Message createMessage(Session session)throws JMSException {
message = (Message) session.createTextMessage(stringBuffer.toString());
return message;
}
});
}
}
A common problem when using JMSTemplate to send messages out of JavaEE containers is the it's extremly slow since it acquires a new connection for each message (and then closes it). You would probably need a pooled/cached connection to gain speed here.
Read this article, it's written for ActiveMQ, but applies in a similar way to WebSphere MQ: http://activemq.apache.org/jmstemplate-gotchas.html
You can setup a cached connection factory in spring using something like this:
<bean id="cachedConnectionFactory"
class="org.springframework.jms.connection.CachingConnectionFactory"
p:targetConnectionFactory-ref="jmsConnectionFactory"
p:sessionCacheSize="10" />
Then use it instead of the original one for JMS connections.

Resources