Spring integration xml file errors out on reactor StringUtils - spring-integration

I have a spring integration sftp flow which I load as a child context within my overall application context. This is based on the dynamic ftp SI example. My integration flow has nothing about reactor or streams in it. Its a simple flow with one direct channel connected with a sftp-outbound-gateway to transfer files to a sftp server. I can even run units tests and the flow work fine (is able to transfer files) but when I run an integration test which loads the full parent application and then initializes the child context with this sftp flow loaded in it, it throws an error for not being able to find reactor/StringUtils class.
The reason for that seems to be that spring-integration-sftp loads reactor jars as transient deps but since my parent application already has a different version of reactor loaded in the classpath I excluded the reactor-core from spring integration dep. If I dont exclude the reactor-core from spring-integration then there are some version conflicts so I would like to exclude it.
reactorVersion = 2.0.0.M2
compile("io.projectreactor:reactor-core:$reactorVersion")
compile "io.projectreactor.spring:reactor-spring-context:$reactorVersion"
compile("org.springframework.integration:spring-integration-sftp") {
exclude module: "reactor-core"
}
Initializing the SI flow
context = new ClassPathXmlApplicationContext(new String[] { "classpath:adapters/"
+ sink.getConfigurationFile() }, false);
setEnvironment(context, sink);
context.setParent(parentContext);
context.refresh();
context.registerShutdownHook();
The error when i ran the integration test
org.springframework.beans.factory.BeanDefinitionStoreException: Unexpected exception parsing XML document from class path resource [adapters/sftp.xml]; nested exception is java.lang.NoClassDefFoundError: reactor/util/StringUtils
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:414)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:336)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:304)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:181)
Finally the SI flow
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:task="http://www.springframework.org/schema/task" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-sftp="http://www.springframework.org/schema/integration/sftp"
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://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/sftp http://www.springframework.org/schema/integration/sftp/spring-integration-sftp.xsd">
<import resource="common.xml" />
<bean id="sftpSessionFactory"
class="org.springframework.integration.file.remote.session.CachingSessionFactory">
<constructor-arg ref="defaultSftpSessionFactory" />
</bean>
<bean id="defaultSftpSessionFactory"
class="org.springframework.integration.sftp.session.DefaultSftpSessionFactory">
<property name="host" value="${sink.host}" />
<property name="port" value="22" />
<property name="privateKey" value="${sink.private.key}" />
<property name="privateKeyPassphrase" value="${sink.private.key.phrase}" />
<property name="user" value="${sink.user}" />
<property name="password" value="${sink.pass}" />
</bean>
<int:channel id="input" />
<int-sftp:outbound-channel-adapter
auto-startup="true" session-factory="sftpSessionFactory" channel="input"
remote-directory="${sink.path}" remote-filename-generator-expression="headers['remote_file_name']">
<int-sftp:request-handler-advice-chain>
<bean
class="org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice">
<property name="onSuccessExpression" value="payload" />
<property name="successChannel" ref="successChannel" />
<property name="onFailureExpression" value="payload" />
<property name="failureChannel" ref="failureChannel" />
<property name="trapException" value="true" />
</bean>
</int-sftp:request-handler-advice-chain>
</int-sftp:outbound-channel-adapter>
<int:channel id="successChannel" />
<int:service-activator input-channel="successChannel"
ref="completionHandler" method="handle" />
<int:channel id="failureChannel" />
<int:service-activator input-channel="failureChannel"
ref="failureHandler" method="handle" />
Updating to add my reactor configuration
#Configuration
#EnableReactor
public class ReactorConfiguration {
static {
Environment.initializeIfEmpty().assignErrorJournal();
}
#Bean
public EventBus eventBus() {
return EventBus.config().env(Environment.get()).dispatcher(Environment.SHARED).get();
}
#Bean
public IdGenerator randomUUIDGenerator() {
return new IdGenerator() {
#Override
public UUID generateId() {
return UUIDUtils.random();
}
};
}
}

Gradle currently doesn't do a great job excluding dependencies of transitive dependencies.
It's not spring-integration-sftp that pulls in reactor, it's spring-integration-core.
Try explicitly excluding via that.
We have removed the hard transitive reactor dependency in SI 4.2 (but it's not released yet).
The spring team has created a gradle plugin that might help.

Related

how to write unit test for spring integration xml code

Im new to spring integration,I want to write unit test cases for my application. I'm working on an application where integration interface will be called by other system with XML input we transform the input XML using XSLT and will make a call to different system and the response will be sent to the caller.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:int-jms="http://www.springframework.org/schema/integration/jms"
xmlns:int-http="http://www.springframework.org/schema/integration/http"
xmlns:int-xml="http://www.springframework.org/schema/integration/xml"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.2.xsd
http://www.springframework.org/schema/integration/xml http://www.springframework.org/schema/integration/xml/spring-integration-xml-4.2.xsd
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-4.2.xsd
http://www.springframework.org/schema/integration/jms http://www.springframework.org/schema/integration/jms/spring-integration-jms-4.2.xsd
http://www.springframework.org/schema/integration/http http://www.springframework.org/schema/integration/http/spring-integration-http-4.2.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-5.0.xsd">
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>application.properties</value>
<value>application-${spring.profiles.active}.properties</value>
</list>
</property>
</bean>
<bean id="MessagePrinter" class="com.oms.integration.messagelogger.MessagePrinter"></bean>
<int:channel id="logIncommingCaptureRequestChannel"></int:channel>
<int:channel id="transformedCaptureRequestToCardinalChannel"></int:channel>
<int:channel id="incommingCaptureRequestToCardinal"></int:channel>
<int:channel id="CaptureRequestToCardinalChannel"></int:channel>
<int:channel id="logCaptureResponseFromCardinal"></int:channel>
<int:channel id="transformCaptureResponseFromCardinal"></int:channel>
<int:channel id="logTransformResponseFromCardinal"></int:channel>
<int:channel id="ResponseFromCardinalToYantraChannel"></int:channel>
<int-http:inbound-gateway request-channel="logIncommingCaptureRequestChannel" supported-methods="POST" path="/fp075" reply-channel="ResponseFromCardinalToYantraChannel"/>
<int-http:outbound-gateway request-channel="CaptureRequestToCardinalChannel" url="${Paypal_Url}={data}" expected-response-type="java.lang.String" http-method="GET" reply-channel="logCaptureResponseFromCardinal">
<int-http:uri-variable name="data" expression="payload"/>
</int-http:outbound-gateway>
<int:service-activator ref="MessagePrinter" input-channel="logIncommingCaptureRequestChannel" method="printIncommingCaptureRequestFromYantra" output-channel="incommingCaptureRequestToCardinal"></int:service-activator>
<int:service-activator ref="MessagePrinter" input-channel="transformedCaptureRequestToCardinalChannel" method="printTransformedCaptureRequestFromYantraToCardinal" output-channel="CaptureRequestToCardinalChannel"></int:service-activator>
<int:service-activator ref="MessagePrinter" input-channel="logCaptureResponseFromCardinal" method="printCaptureResponseFromCardinal" output-channel="transformCaptureResponseFromCardinal"></int:service-activator>
<int:service-activator ref="MessagePrinter" method="printTransformedResponseFromCardinal" input-channel="logTransformResponseFromCardinal" output-channel="ResponseFromCardinalToYantraChannel"></int:service-activator>
<int-xml:xslt-transformer
input-channel="incommingCaptureRequestToCardinal"
xsl-resource="classpath:/templates/FP075/DSW_XSLT_YANTRA_To_Paypal_Capture_Request_FP075.xslt"
output-channel="transformedCaptureRequestToCardinalChannel"
id="TransformIncommingCaptureRequest">
<int-xml:xslt-param name="MsgType"
value="${PP_Cardinal_MsgType_FP075}" />
<int-xml:xslt-param name="Version"
value="${PP_Cardinal_Version}" />
<int-xml:xslt-param name="ProcessorId"
value="${PP_Cardinal_ProcessorId}" />
<int-xml:xslt-param name="MerchantId"
value="${PP_Cardinal_MerchantId}" />
<int-xml:xslt-param name="TransactionPwd"
value="${PP_Cardinal_TransactionPwd}" />
</int-xml:xslt-transformer>
<int-xml:xslt-transformer
id="transformCaptureResponse"
input-channel="transformCaptureResponseFromCardinal"
xsl-resource="classpath:/templates/FP075/DSW_XSLT_YANTRA_To_Paypal_Capture_Response_FP075.xslt"
output-channel="logTransformResponseFromCardinal">
</int-xml:xslt-transformer>
</beans>
Since you have there <int-http:inbound-gateway> and <int-http:outbound-gateway> I would suggest you to take a look into the Mock Spring MVC: https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#spring-mvc-test-framework
The server side might be like this:
#RunWith(SpringRunner.class)
#WebAppConfiguration
#DirtiesContext
public class MyTests {
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Before
public void setup() {
this.mockMvc =
MockMvcBuilders.webAppContextSetup(this.wac)
.build();
}
For the client side you need to use something like MockMvcClientHttpRequestFactory abd inject it into the HttpRequestExecutingMessageHandler for that mentioned <int-http:outbound-gateway>.
So, in the end your main configuration is the same and all the flow structure remains untouched. You send mock requests via this.mockMvc.perform() and expect response after your flow processing.

Spring Integration Webservices

I am very new at spring integration, I am trying to convert following code into spring integration code but I am not able to send the request properly.
I really don't know what I am doing wrong and what to do, actually the task is to split the XML which are sending to webservices then get the response back from it and then aggregate to make it one XML.
Normal code:
connection = (HttpURLConnection) validateServer.openConnection();
connection.setRequestMethod("POST");
connection.setDoInput(true);
connection.setDoOutput(true);
String parameters = "";
if (isMultipleVal) {
parameters = ADD_FUNCTION2
+ URLEncoder.encode(getxmldatalist, "UTF-8");
} else {
parameters = ADD_FUNCTION
+ URLEncoder.encode(getxmldatalist, "UTF-8");
}
LOG.info("parameters:" + parameters);
System.out.println("In client parameters "+parameters);
byte[] parameterAsBytes = parameters.getBytes();
System.out.println("Parameters as bytes "+parameterAsBytes);
connection.setRequestProperty(CONTENT_LENGTH,String.valueOf(parameterAsBytes.length));
OutputStream oStream = connection.getOutputStream();
oStream.write(parameterAsBytes, 0, parameterAsBytes.length);
oStream.flush();
oStream.close();
Spring Integration code:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-xml="http://www.springframework.org/schema/integration/xml"
xmlns:int-ws="http://www.springframework.org/schema/integration/ws"
xmlns:int-http="http://www.springframework.org/schema/integration/http"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:int-stream="http://www.springframework.org/schema/integration/stream"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/stream http://www.springframework.org/schema/integration/stream/spring-integration-stream.xsd
http://www.springframework.org/schema/integration/xml http://www.springframework.org/schema/integration/xml/spring-integration-xml.xsd
http://www.springframework.org/schema/integration/ws http://www.springframework.org/schema/integration/ws/spring-integration-ws.xsd
http://www.springframework.org/schema/integration/http http://www.springframework.org/schema/integration/http/spring-integration-http.xsd">
<int:channel id="requestGatewayChannel"/>
<int:gateway id="serviceGateway" service-interface="xxx.xxx.xxx" default-request-channel="requestGatewayChannel" default-reply-channel="replyGatewayChannel" />
<int:channel id="replyGatewayChannel"/>
<int:splitter ref="rootSplitter" input-channel="requestGatewayChannel" output-channel="splitterOutputChannel" method="splitRoot"/>
<int:channel id="splitterOutputChannel"/>
<bean id="rootSplitter" class="xxx.xxx.xxx.VehicleRootSpliter" />
<int-xml:marshalling-transformer id="marshaller" input-channel="splitterOutputChannel" output-channel="marshallerOutputChannel" marshaller="marshallerUnmarshaller" result-type="StringResult" />
<int:channel id="marshallerOutputChannel"></int:channel>
<int:object-to-string-transformer input-channel="marshallerOutputChannel" output-channel="stringOutputChannel"></int:object-to-string-transformer>
<int:channel id="stringOutputChannel"></int:channel>
<int:transformer id="dataTransformer" input-channel="stringOutputChannel" output-channel="dataTransformerOutputChannel" ref="xmlToEncode" method="dataTransformer"></int:transformer>
---To encode the string of xml using custom transformer---
<int:channel id="dataTransformerOutputChannel"></int:channel>
<bean id="xmlToEncode" class="xxx.xxx.xxx.DataTransformer"></bean>
<int:header-enricher default-overwrite="true" input-channel="dataTransformerOutputChannel" output-channel="enricherOutputChannel">
<int:header name="Content_length" ref="headerBean" method="valueLength"></int:header>
</int:header-enricher>
<int:channel id="enricherOutputChannel"></int:channel>
<bean id="headerBean" class="xxx.xxx.xxx.DataTransformer"></bean>
<int-http:outbound-gateway url="${toms.ws.url}" request-channel="enricherOutputChannel" reply-channel="httpOutputChannel"
header-mapper="headerMapper" http-method="POST"
expected-response-type="java.lang.String"/>
<bean id="headerMapper" class="org.springframework.integration.http.support.DefaultHttpHeaderMapper">
<property name="outboundHeaderNames" value="HTTP_REQUEST_HEADERS, Content_length"></property>
<property name="userDefinedHeaderPrefix" value=""></property>
</bean>
<int:channel id="httpOutputchannel"></int:channel>
<int:service-activator input-channel="httpOutputChannel" ref="webserviceActivator" method="checkerWeb"></int:service-activator>
<bean id="webserviceActivator" class="xxx.xxx.xxx"></bean>
<bean id="marshallerUnmarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller" >
<property name="classesToBeBound">
<list>
<value>com.xxx.xxx.xxx.VRoot</value>
</list>
</property>
</bean>
</beans>

issue in spring xd cluster when deploying my module

I have a module s3-puller which pulls file from was s3 .In the production i am facing some issue when i try to create a stream.But local single node it works fine and i tried to set up 3 node cluster and 1 admin node in local it works fine.
Below is my application context
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-aws="http://www.springframework.org/schema/integration/aws"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/aws http://www.springframework.org/schema/integration/aws/spring-integration-aws-1.0.xsd">
<int:poller fixed-delay="${fixed-delay}" default="true"/>
<bean id="credentials" class="org.springframework.integration.aws.core.BasicAWSCredentials">
<property name="accessKey" value="${accessKey}"/>
<property name="secretKey" value="${secretKey}"/>
</bean>
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>dms-aws-s3-nonprod.properties</value>
</property>
</bean>
<bean id="clientConfiguration" class="com.amazonaws.ClientConfiguration">
<property name="proxyHost" value="${proxyHost}"/>
<property name="proxyPort" value="${proxyPort}"/>
<property name="preemptiveBasicProxyAuth" value="false"/>
</bean>
<bean id="s3Operations" class="org.springframework.integration.aws.s3.core.CustomC1AmazonS3Operations">
<constructor-arg index="0" ref="credentials"/>
<constructor-arg index="1" ref="clientConfiguration"/>
<property name="awsEndpoint" value="s3.amazonaws.com"/>
<property name="temporaryDirectory" value="${temporaryDirectory}"/>
<property name="awsSecurityKey" value="${awsSecurityKey}"/>
</bean>
<!-- aws-endpoint="https://s3.amazonaws.com" -->
<int-aws:s3-inbound-channel-adapter aws-endpoint="s3.amazonaws.com"
bucket="${bucket}"
s3-operations="s3Operations"
credentials-ref="credentials"
file-name-wildcard="${file-name-wildcard}"
remote-directory="${remote-directory}"
channel="splitChannel"
local-directory="${local-directory}"
accept-sub-folders="false"
delete-source-files="true"
archive-bucket="${archive-bucket}"
archive-directory="${archive-directory}">
</int-aws:s3-inbound-channel-adapter>
<int:splitter input-channel="splitChannel" output-channel="output"
expression="T(org.apache.commons.io.FileUtils).lineIterator(payload)"/>
<int:channel id="output"/>
my Application.java
package com.capitalone.api.dms.main;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.annotation.ImportResource;
#SpringBootApplication
#ImportResource("classpath:config/applicationContext.xml")
public class Application {
public static void main(String[] args) throws Exception {
new SpringApplicationBuilder(Application.class)
.web(false)
.showBanner(false)
.properties("security.basic.enabled=false")
.run(args);
}
}
I am getting below exception when i try to create a basic stream
module upload --file aws.jar --name aws-s3-options --type source
stream create feedTest91 --definition "aws-s3-options | log" --deploy
I get below exception
DeploymentStatus{state=failed,error(s)=org.springframework.beans.factory.BeanDefinitionStoreException: Invalid bean definition with name 'objectNameProperties' defined in null: Could not resolve placeholder 'xd.module.sequence' in string value "${xd.module.sequence}"; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'xd.module.sequence' in string value "${xd.module.sequence}" at org.springframework.beans.factory.config.PlaceholderConfigurerSupport.doProcessProperties(PlaceholderConfigurerSupport.java:211) at org.springframework.beans.factory.config.PropertyPlaceholderConfigurer.processProperties(PropertyPlaceholderConfigurer.java:222) at org.springframework.beans.factory.config.PropertyResourceConfigurer.postProcessBeanFactory(PropertyResourceConfigurer.java:86) at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:265)
from the source code i see that its loaded by jmx mbean file of xd and loaded by below java file
https://github.com/spring-projects/spring-xd/blob/6923ee8705bd9c2c58ad73120724b8b87c5ba37d/spring-xd-dirt/src/main/resources/META-INF/spring-xd/plugins/jmx/mbean-exporters.xml
https://github.com/spring-projects/spring-xd/blob/e9ce8e897774722c1e61038817ebd55c5cf0befc/spring-xd-dirt/src/main/java/org/springframework/xd/dirt/plugins/MBeanExportingPlugin.java
Solution :
I am planning to inject them from my s3 module .Is it right way to do please let me know what should be the values?
<context:mbean-export />
<int-jmx:mbean-export object-naming-strategy="moduleObjectNamingStrategy" />
<util:properties id="objectNameProperties">
<prop key="group">${xd.group.name}</prop>
<prop key="label">${xd.module.label}</prop>
<prop key="type">${xd.module.type}</prop>
<prop key="sequence">${xd.module.sequence}</prop>
</util:properties>
<bean id="moduleObjectNamingStrategy"
class="org.springframework.xd.dirt.module.jmx.ModuleObjectNamingStrategy">
<constructor-arg value="xd.${xd.stream.name:${xd.job.name:}}" />
<constructor-arg ref="objectNameProperties" />
</bean>
That property should be automatically set up by the ModuleInfoPlugin.
This is the second time someone has said that property is missing somehow.
I have opened a JIRA Issue.

Dynamically getting sftp connection properties from database and get sftpConnectionFactory in spring integration

I have requirement that I should get SFTP connection properties from database and create sftpSessionFactory by using properties comes from the database dynamically.
Below ${sftp.host}, ${sftp.port}, ${sftp.user} and ${sftp.password} properties values should come from database.
<bean id="sftpSessionFactory" class="org.springframework.integration.sftp.session.DefaultSftpSessionFactory">
<property name="host" value="${sftp.host}"/>
<property name="port" value="${sftp.port}" />
<property name="user" value="${sftp.user}"/>
<property name="password" value="${sftp.password}"/>
</bean>
How to create SFTP connection by getting properties from database?
web.xml
<!-- Loads the Spring application contexts -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath*:context/application-context-*.xml
</param-value>
</context-param>
application-context-services.xml:
<?xml version="1.0" encoding="UTF-8"?>
<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:p="http://www.springframework.org/schema/p"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:task="http://www.springframework.org/schema/task"
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://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">
<context:annotation-config />
<context:component-scan base-package="com.abc.xyz.batch.services" />
<bean id="freemarkerConfiguration"
class="org.springframework.ui.freemarker.FreeMarkerConfigurationFactoryBean">
<property name="preferFileSystemAccess" value="false" />
<property name="templateLoaderPath" value="classpath:templates/" />
<property name="freemarkerSettings">
<props>
<prop key="default_encoding">UTF-8</prop>
<prop key="locale">ar_AR</prop>
</props>
</property>
</bean>
<bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="cacheSeconds" value="0" />
<property name="defaultEncoding" value="UTF-8" />
<property name="useCodeAsDefaultMessage" value="true" />
<property name="basenames">
<list>
<value>classpath:validation-messages</value>
</list>
</property>
</bean>
EnvironmentLookupService.java
#Service
public class EnvironmentLookupService {
private static final Logger logger = LoggerFactory.getLogger(EnvironmentLookupService.class);
#Autowired
#Qualifier("transactionManager")
private PlatformTransactionManager txManager;
#Autowired
private ConfigDao configDao;
private final Map<String, String> config = new HashMap<String, String>();
/**
* Loads all lookups used in xyz from database
*/
#PostConstruct
public void loadLookupData() {
logger.info("loading lookup cache");
new TransactionTemplate(txManager).execute(new TransactionCallbackWithoutResult() {
#Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
loadLookups();
}
});
// initialize();
}
/**
* Refreshes the lookup caches
*/
#Scheduled(cron = "${core.lookups.cache.refresh.cron}")
public void refreshCache() {
loadLookupData();
}
/**
* Loads all lookups from database
*/
protected void loadLookups() {
final List<IConfig> list = configDao.findAll();
for (final IConfig property : list) {
config.put(property.getKey(), property.getValue());
}
}
/**
* Will return configuration value for the key provided
*
* #param key
* #return String
*/
public String retriveConfigValue(EConfig key) {
return config.get(key.getValue());
}
}
DynamicSftpChannelResolver.java
#Service
public class DynamicSftpChannelResolver {
#Autowired
private EnvironmentLookupService lookupService;
ConfigurableApplicationContext confAppContext;
#PostConstruct
public void setEnvironmentForSftpConnection() {
confAppContext = new ClassPathXmlApplicationContext("classpath*:context/*integration-context.xml");
final StandardEnvironment stdEnvironment = new StandardEnvironment();
final Properties props = new Properties();
props.setProperty(EConfig.SFTP_HOST.getValue(), lookupService.retriveConfigValue(EConfig.SFTP_HOST));
props.setProperty(EConfig.SFTP_PORT.getValue(), lookupService.retriveConfigValue(EConfig.SFTP_PORT));
props.setProperty(EConfig.SFTP_USER.getValue(), lookupService.retriveConfigValue(EConfig.SFTP_USER));
props.setProperty(EConfig.SFTP_PASSWORD.getValue(), lookupService.retriveConfigValue(EConfig.SFTP_PASSWORD));
final PropertiesPropertySource propPropertySource = new PropertiesPropertySource("sftp", props);
stdEnvironment.getPropertySources().addLast(propPropertySource);
confAppContext.setEnvironment(stdEnvironment);
}
}
spring-integration-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-file="http://www.springframework.org/schema/integration/file"
xmlns:int-sftp="http://www.springframework.org/schema/integration/sftp"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:int-ws="http://www.springframework.org/schema/integration/ws"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/integration/ws http://www.springframework.org/schema/integration/ws/spring-integration-ws.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/file http://www.springframework.org/schema/integration/file/spring-integration-file.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration/sftp http://www.springframework.org/schema/integration/sftp/spring-integration-sftp.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">
<tx:annotation-driven transaction-manager="transactionManager" />
<context:property-placeholder />
<!-- <context:property-placeholder ignore-unresolvable="true" location="classpath:*.properties"/> -->
<bean id="sftpSessionFactory" class="org.springframework.integration.sftp.session.DefaultSftpSessionFactory">
<property name="host" value="${sftp.host}"/>
<property name="port" value="${sftp.port}" />
<property name="user" value="${sftp.user}"/>
<property name="password" value="${sftp.password}"/>
</bean>
<int:channel id="ch.file.download"></int:channel >
<int:channel id="ch.file.type.access"></int:channel >
<int:channel id="ch.file.type.excel"></int:channel >
<int:channel id="ch.file.type.csv"></int:channel >
<int:channel id="ch.file.validated"></int:channel>
<int-sftp:inbound-channel-adapter id="sftp.file.client.download"
session-factory="sftpSessionFactory" channel="ch.file.download"
auto-create-local-directory="true" remote-directory="${sftp.remote.directory}"
local-filename-generator-expression="#this"
temporary-file-suffix=".downloading"
local-directory="${sftp.local.directory}"
>
<int:poller fixed-rate="1000"/>
</int-sftp:inbound-channel-adapter>
After doing all the above sftpSessionFactory is not getting created. no exception also.
I guest *integration-conotext.xml is not loading.
Can you please suggest me ,what's going wrong ?.
Your web.xml has an option to load any XML config by pattern classpath*:context/application-context-*.xml. But the Spring Integration config name looks like spring-integration-context.xml.
From my perspective it doesn't matter and it would be better to use <context:import> on the matter.
From other side, pleas, take a look to the new DelegatingSessionFactory feature since Spring Integration 4.2 which allows to retrieve the target SessionFactory at runtime against the requestMessage.
And one more point. I use Commons Configuration to load Properties from DataBase and use the last one for the properties-placeholder:
<bean id="databaseConfiguration" class="org.apache.commons.configuration.DatabaseConfiguration">
<constructor-arg ref="dataSource"/>
<constructor-arg value="PROPERTIES"/>
<constructor-arg value="PROPERTY"/>
<constructor-arg value="PROPERTYVALUE"/>
</bean>
<bean id="propertiesFromDB" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"
p:staticMethod="org.apache.commons.configuration.ConfigurationConverter.getProperties"
p:arguments-ref="databaseConfiguration"/>
<context:property-placeholder properties-ref="propertiesFromDB"/>
If that would be enough for you to load them from DB only once at startup.

Spring integration FTP: Stop file inbound-channel-adapter automatically when all source folder files are deleted

Is there any configuration settings/expression to stop channel adapter based on a condition (i.e, stop channel adapter when the source folder is empty)?
I can do this programatically as below but this approach won't work in my case.
Config:
<file:inbound-channel-adapter prevent-duplicates="false" auto-startup="false" auto-create-directory="true" directory="${local.tmp.path}" id="fileInbound" channel="outputChannel" filename-pattern="*.xml">
<int:poller fixed-rate="${local.transfer.interval}" max-messages-per-poll="100" />
</file:inbound-channel-adapter>
<sftp:outbound-channel-adapter id="sftpFileOutboundAdapter" auto-create-directory="true" session-factory="sftpSessionFactory" channel="outputChannel" charset="UTF-8" remote-directory="${ftp.path}">
<sftp:request-handler-advice-chain>
<bean class="org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice">
<property name="onSuccessExpression" value="payload.delete()" />
<property name="trapException" value="true" />
</bean>
</sftp:request-handler-advice-chain>
</sftp:outbound-channel-adapter>
Code:
fileInbound = context.getBean("fileInbound", SourcePollingChannelAdapter.class);
if (fileInbound.isRunning() && isSourcefolderEmpty()) {
fileInbound.stop();
}
Something like this should work.
In your onSuccessExpression use payload and add a successChannel.
Make the successChannel a <publish-subsribe-channel/>.
First subscriber (order="1") is a <service-activator/> with the payload.delete().
Second subscriber (order="2") is a <chain/> that starts with a <filter/> that checks if the directory is empty...
.
<chain ...>
<filter expression="new java.io.File('/tmp/foo').list().length == 0" />
<transformer expression="#fileInbound.stop()"/>
<control-bus/>
</chain>

Resources