Misunderstanding with JobLaunchRequest - spring-integration

this is my previous question: Spring Integration + Spring Batch: the job doesn`t stop.
Project works good with annotation configuration, but I want the same on xml config :)
xml configuration:
<int:service-activator input-channel="fileInputChannel"
method="fileWritingMessageHandler"
output-channel="jobLaunchRequestChannel">
<bean class="service.impl.IntegrationServiceImpl"/>
</int:service-activator>
<int:service-activator input-channel="jobLaunchRequestChannel"
method="jobLaunchRequest"
output-channel="jobLaunchingGatewayChannel">
<bean class="service.impl.IntegrationServiceImpl"/>
</int:service-activator>
<batch-int:job-launching-gateway request-channel="jobLaunchingGatewayChannel"
reply-channel="finish"/>
<int:service-activator input-channel="finish"
ref="integrationServiceImpl"
method="finishJob">
</int:service-activator>
IntegrationConfiguration.java:
#Bean
public FtpInboundFileSynchronizer ftpInboundFileSynchronizer() {
FtpInboundFileSynchronizer fileSynchronizer = new FtpInboundFileSynchronizer(defaultFtpSessionFactory);
fileSynchronizer.setRemoteDirectory(remoteDirectory);
fileSynchronizer.setDeleteRemoteFiles(false);
return fileSynchronizer;
}
#Bean
#InboundChannelAdapter(channel = "fileInputChannel", poller = #Poller(cron = "*/5 * * * * ?"))
public FtpInboundFileSynchronizingMessageSource ftpInboundFileSynchronizingMessageSource(FtpInboundFileSynchronizer fileSynchronizer) throws Exception {
FtpInboundFileSynchronizingMessageSource messageSource = new FtpInboundFileSynchronizingMessageSource(fileSynchronizer);
messageSource.setAutoCreateLocalDirectory(true);
messageSource.setLocalDirectory(new File(localDirectory));
messageSource.setLocalFilter(new AcceptOnceFileListFilter<>());
return messageSource;
}
IntegrationServiceImpl:
#Override
public FileWritingMessageHandler fileWritingMessageHandler() {
FileWritingMessageHandler messageHandler = new FileWritingMessageHandler(new File(storageDirectory));
messageHandler.setDeleteSourceFiles(true);
messageHandler.setFileNameGenerator(message -> {
Long timestamp = new Date().getTime();
log.info(timestamp);
return "test_" + timestamp;
});
return messageHandler;
}
#Override
public JobLaunchRequest jobLaunchRequest(File file) throws IOException {
// public JobLaunchRequest jobLaunchRequest(FileWritingMessageHandler fileWritingMessageHandler) throws IOException {
String[] content = FileUtils.readFileToString(file, "UTF-8").split("\\s+");
JobParameters jobParameters = new JobParametersBuilder()
.addString("filename", file.getAbsolutePath())
.addString("id", content[0])
.addString("salary", content[1])
.toJobParameters();
log.info(jobParameters);
return new JobLaunchRequest(increaseSalaryJob, jobParameters);
}
#Override
public void finishJob() {
log.info("Job finished");
}
As you can see this xml config like previous post annotation config, BUT i have an error:
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1004E: Method call: Method jobLaunchRequest(org.springframework.integration.file.FileWritingMessageHandler) cannot be found on type service.impl.IntegrationServiceImpl
Why i can't use jobLaunchRequest(File)? And if i need to use jobLaunchRequest(FileWritingMessageHandler) how can I operate with file?

The jobLaunchRequest() method definitely has to be with a File argument because that is indeed what produced as a payload in the reply message from the FileWritingMessageHandler.
Your <int:service-activator input-channel="fileInputChannel"
method="fileWritingMessageHandler"> definition is wrong.
Since you would like to use a FileWritingMessageHandler as a service, you need to consider to use an <int-file:outbound-gateway> instead.
The service-activator is for calling POJO methods. Since FileWritingMessageHandler is a MessageHandler implementation it has to be used in the <service-activator> directly from the ref attribute without any method attribute usage.

Related

rename a file in source directory after successfully sending to jms queue , in spring integration. using ExpressionEvaluatingRequestHandlerAdvice

getting following exception
AdviceMessage [payload=org.springframework.expression.spel.SpelEvaluationException: EL1008E: Property or field 'absolutePath' cannot be found on object of type 'java.lang.String' - maybe not public or not valid?
see the source code..
#Bean
#InboundChannelAdapter(value = "fileInputChannel", poller = #Poller(fixedDelay = "1000"))
public MessageSource<File> fileReadingMessageSource(#Value("${file.poller.path}") final String path,
#Value("${file.poller.fileName-pattern}") final String fileExt) {
CompositeFileListFilter<File> filters = new CompositeFileListFilter<>();
filters.addFilter(new SimplePatternFileListFilter(fileExt));
// filters.addFilter(new AcceptOnceFileListFilter<File>());
FileReadingMessageSource source = new FileReadingMessageSource();
source.setAutoCreateDirectory(false);
source.setDirectory(new File(path));
source.setFilter(filters);
source.setUseWatchService(true);
source.setWatchEvents(WatchEventType.CREATE);
System.out.println(path);
return source;
}
#Bean
public IntegrationFlow processFile() {
return IntegrationFlows
.from("fileInputChannel")
.transform(fileToStringTransformer())
.handle("fileProcessor", "process")//added some data manupilation code and returns Message<String>//
.log(LoggingHandler.Level.INFO, "process file", m -> m.getHeaders().get("Message_Type"))
.channel(this.jmsOutboundChannel())
.get();
}
#Bean
public IntegrationFlow sendToJmsQueue(JmsTemplate wlsJmsTemplate) {
return IntegrationFlows.from(this.jmsOutboundChannel())
.log(LoggingHandler.Level.INFO, "sending to queue", m ->
m.getHeaders().get("Message_Type"))
.handle(Jms.outboundAdapter(wlsJmsTemplate).destination(inboundDataQueue),
e -> e.advice(expressionAdvice()))
}
#Bean
public Advice expressionAdvice() {
ExpressionEvaluatingRequestHandlerAdvice advice = new ExpressionEvaluatingRequestHandlerAdvice();
advice.setSuccessChannelName("success.input");
advice.setOnSuccessExpressionString("payload.delete()");
//advice.setOnSuccessExpressionString("payload.renameTo(new java.io.File(payload.absolutePath + '.Done'))");
advice.setFailureChannelName("failure.input");
advice.setOnFailureExpressionString("payload.renameTo(new java.io.File(payload.absolutePath + '.FailedToSend'))");
advice.setTrapException(true);
return advice;
}
When using a FileReadingMessageSource, as well as putting the File in the payload, the file is also added as a header FileHeaders.ORIGINAL_FILE so that it is available later if a transformation occurs.
So you expressions need to use
headers['file_originalFile'].renameTo(new java.io.File(headers['file_originalFile']).absolutePath + '.failed')
and
headers['file_originalFile'].delete()

Recursively read files with Spring Integration SFTP DSL

I am trying to make Spring Integration SFTP read files (.txt) from a remote server recursively from all subfolders. The remote folder is something like "/tmp/remoteFolder" and all subfolders are date folders like "/tmp/remoteFolder/20180830", "/tmp/remoteFolder/20170902".
This is the code that I have until now
#Bean
#InboundChannelAdapter(value = "sftpMgetInputChannel",
poller = #Poller(fixedDelay = "5000"))
public IntegrationFlow sftpMGetFlow() {
return IntegrationFlows.from("sftpMgetInputChannel")
.handleWithAdapter(h -> h.sftpGateway(this.sftpSessionFactory,
Command.MGET, "'/tmp/remoteDirectory/*'")
.options(Option.RECURSIVE)
.regexFileNameFilter("((\\d{8})|*\\.txt)")
.localDirectoryExpression("sftp-inbound" + "/" + "#remoteDirectory"))
.channel(remoteFileOutputChannel())
.get();
}
#Bean
public MessageChannel sftpMgetInboundChannel(){
return new DirectChannel();
}
#Bean
public PollableChannel remoteFileOutputChannel() {
return new QueueChannel();
}
How do I specify the root remote directory for sftp mget to be /tmp/remoteFolder? Why isn't this working? Why do I need to specifiy the output channel?
Update: Instead of calling channel(remoteFileOutputChannel()) I call a handler like this
#Bean
public MessageHandler messageHandler(){
return new MessageHandler() { ... }
}
Code updated:
#InboundChannelAdapter(value = "sftpMgetInputChannel",
poller = #Poller(fixedDelay = "5000"))
public String filesForMGET(){
return "'/tmp/input/remoteDirectory/*'";
}
#Bean
public IntegrationFlow sftpMGetFlow() {
return IntegrationFlows.from("sftpMgetInputChannel")
.handleWithAdapter(h -> h.sftpGateway(this.sftpSessionFactory,
Command.MGET, "payload")
.options(Option.RECURSIVE)
.regexFileNameFilter("((\\d{8})|*\\.txt)")
.localDirectoryExpression("'sftp-inbound/'" + "#remoteDirectory"))
.handler(messageHandler())
.get();
}
#Bean
public MessageChannel sftpMgetInboundChannel(){
return new DirectChannel();
}
#Bean
public MessageHandler messageHandler(){
return new MessageHandler() { ... }
}
With this updated code, I get the following error:
rg.springframework.core.NestedIOException: failed to read file; nested exception is 2: No such file
at org.springframework.integration.sftp.session.SftpSession.read(SftpSession.java:100)
at org.springframework.integration.file.remote.session.CachingSessionFactory$CachedSession.read(CachingSessionFactory.java:137)
at org.springframework.integration.file.remote.synchronizer.AbstractInboundFileSynchronizer.copyFileToLocalDirectory(AbstractInboundFileSynchronizer.java:176)
at org.springframework.integration.file.remote.synchronizer.AbstractInboundFileSynchronizer.synchronizeToLocalDirectory(AbstractInboundFileSynchronizer.java:138)
at org.springframework.integration.file.remote.synchronizer.AbstractInboundFileSynchronizingMessageSource.receive(AbstractInboundFileSynchronizingMessageSource.java:144)
at org.springframework.integration.endpoint.SourcePollingChannelAdapter.doPoll(SourcePollingChannelAdapter.java:89)
at org.springframework.integration.endpoint.AbstractPollingEndpoint$1.call(AbstractPollingEndpoint.java:146)
at org.springframework.integration.endpoint.AbstractPollingEndpoint$1.call(AbstractPollingEndpoint.java:144)
at org.springframework.integration.endpoint.AbstractPollingEndpoint$Poller$1.run(AbstractPollingEndpoint.java:207)
at org.springframework.integration.util.ErrorHandlingTaskExecutor$1.run(ErrorHandlingTaskExecutor.java:52)
at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:48)
at org.springframework.integration.util.ErrorHandlingTaskExecutor.execute(ErrorHandlingTaskExecutor.java:49)
at org.springframework.integration.endpoint.AbstractPollingEndpoint$Poller.run(AbstractPollingEndpoint.java:202)
at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:51)
at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:81)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:441)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
at java.util.concurrent.FutureTask.run(FutureTask.java:138)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:98)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:206)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:680)
Caused by: 2: No such file
at com.jcraft.jsch.ChannelSftp.throwStatusError(ChannelSftp.java:2289)
at com.jcraft.jsch.ChannelSftp._stat(ChannelSftp.java:1741)
at com.jcraft.jsch.ChannelSftp.get(ChannelSftp.java:1011)
at com.jcraft.jsch.ChannelSftp.get(ChannelSftp.java:986)
at org.springframework.integration.sftp.session.SftpSession.read(SftpSession.java:96)
... 22 more
With the expression set to payload (as was the case in your question before the edit), the message payload sent to the gateway should be /tmp/remoteFolder/* which internally is split into remote directory and remote filename (*).
Why do I need to specifiy the output channel?
The result of the MGET (list of retrieved files) needs to go somewhere.
EDIT
You misunderstond; you can't add the #InboundChannelAdapter annotation to the flow; you need something like this...
#InboundChannelAdapter(value = "sftpMgetInputChannel",
poller = #Poller(fixedDelay = "5000"))
public String filesForMGET() {
return "/tmp/remoteDirectory/";
}
#Bean
public IntegrationFlow sftpMGetFlow() {
return IntegrationFlows.from("sftpMgetInputChannel")
.handleWithAdapter(h -> h.sftpGateway(this.sftpSessionFactory,
Command.MGET, "payload")
.options(Option.RECURSIVE)
.regexFileNameFilter("((\\d{8})|*\\.txt)")
.localDirectoryExpression("sftp-inbound" + "/" + "#remoteDirectory"))
.channel(remoteFileOutputChannel())
.get();
}

Spring Integration Web Service - Jaxb2Marshaller - How I can use SAX Parser?

I am using Jaxb2Marshaller with Spring Integration. I have an inbound gateway web service, when someone call it, it will auto parse into JAXB generated classes.
But when I debug into the source, I see Jaxb2Marshaller using DOM. I thought it would use SAX as for binding XML data into Java object, SAX is faster. Why Jaxb2Marshaller use DOM by default ? How can I configure it to use SAX ?
As I checked the document
The
unmarshaller requires an instance of Source. If the message payload is not an instance of Source,
conversion will be attempted. Currently String, File and org.w3c.dom.Document payloads are
supported. Custom conversion to a Source is also supported by injecting an implementation of a
SourceFactory.
Note
If a SourceFactory is not set explicitly, the property on the UnmarshallingTransformer will
by default be set to a DomSourceFactory
About SourceFactory
http://docs.spring.io/spring-integration/api/org/springframework/integration/xml/source/SourceFactory.html
We can see that currently, it only has DomSourceFactory and StringSourceFactory. There is no SaxSourceFactory.
So we can't use SAX with Jaxb2Marshaller, right ?
Will it have SaxSourceFactory in the future ? or never ?
The weird thing is when I check Jaxb2Marshaller , I see the code already handle SAX
XMLReader xmlReader = null;
InputSource inputSource = null;
if (source instanceof SAXSource) {
SAXSource saxSource = (SAXSource) source;
xmlReader = saxSource.getXMLReader();
inputSource = saxSource.getInputSource();
}
else if (source instanceof StreamSource) {
StreamSource streamSource = (StreamSource) source;
if (streamSource.getInputStream() != null) {
inputSource = new InputSource(streamSource.getInputStream());
}
else if (streamSource.getReader() != null) {
inputSource = new InputSource(streamSource.getReader());
}
else {
inputSource = new InputSource(streamSource.getSystemId());
}
}
So, the final question is CAN I configure use Spring Integration Web Service with JAXB with SAX ? Am I missed something?
Here is my configurations:
<ws:inbound-gateway id="inbound-gateway" request-channel="RequestChannel" reply-channel="ResponseChannel"
marshaller="marshaller" unmarshaller="marshaller" />
<int:channel id="RequestChannel" />
<int:channel id="ResponseChannel" />
<bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="contextPath" value="com.example.webservice.api"/>
</bean>
Thank you and best regards,
Nha Nguyen
I'm using WebServiceGatewaySupport classes and tried adding a AxiomSoapMessageFactory as Bean to the application context, until I found out that WebServiceTemplate does not load the WebServiceMessageFactory from the application context. So I ended up with adding a constructor:
public class SomeServiceImpl extends WebServiceGatewaySupport implements SomeService {
public SomeServiceImpl(WebServiceMessageFactory messageFactory) {
super(messageFactory);
}
}
and building the service myself with a #Configuration class:
#Configuration
public class WebServicesConfiguration {
private WebServiceMessageFactory webServiceMessageFactory = new AxiomSoapMessageFactory();
#Bean
public SomeService someService() {
return new SomeServiceImpl(webServiceMessageFactory);
}
}
Try to configure AxiomSoapMessageFactory bean with the name MessageDispatcherServlet.DEFAULT_MESSAGE_FACTORY_BEAN_NAME.
By default it is SaajSoapMessageFactory which does exactly this before unmarshalling:
public Source getPayloadSource() {
SOAPElement bodyElement = SaajUtils.getFirstBodyElement(getSaajBody());
return bodyElement != null ? new DOMSource(bodyElement) : null;
}
where Axiom is based on STaX:
XMLStreamReader streamReader = getStreamReader(payloadElement);
return StaxUtils.createCustomStaxSource(streamReader);
And class StaxSource extends SAXSource {

spring batch integration file poller

I am trying to build a spring batch application that starts a job only after a file comes into a directory. For that I need a file poller and something like the snippet found in Spring reference manual:
public class FileMessageToJobRequest {
private Job job;
private String fileParameterName;
public void setFileParameterName(String fileParameterName) {
this.fileParameterName = fileParameterName;
}
public void setJob(Job job) {
this.job = job;
}
#Transformer
public JobLaunchRequest toRequest(Message<File> message) {
JobParametersBuilder jobParametersBuilder =
new JobParametersBuilder();
jobParametersBuilder.addString(fileParameterName,
message.getPayload().getAbsolutePath());
return new JobLaunchRequest(job, jobParametersBuilder.toJobParameters());
}
}
I would like to manage everything with configuration classes, but I can't really figure out how to make it work.
Your question isn't clear. Would be better to have something that works, then some your own PoC or attempt to reach the task.
But anyway that looks like you would like to avoid XML configuration and be only with Java & Annotation Configuration.
For this purpose I suggest you to take a look into Reference Manual and find this sample in the File Support chapter, too:
#Bean
#InboundChannelAdapter(value = "fileInputChannel", poller = #Poller(fixedDelay = "1000"))
public MessageSource<File> fileReadingMessageSource() {
FileReadingMessageSource source = new FileReadingMessageSource();
source.setDirectory(new File(INBOUND_PATH));
source.setFilter(new SimplePatternFileListFilter("*.txt"));
return source;
}

How could I transfer message dynamically in Sftp Inbound adapter through Spring DSL in java 1.7

I have a Sftp inbound flow and I got the session information from DefaultSftpSessionFactory. But I need to implement mulitple session information dynamically which I will get from database table. That means I have multiple number of Sftp server details that I need to implement in my integration flow. Now I have done with file transfer from single source to single destination but I need to implement multiple source to multiple destination. So can any one provide some pointer on this.
This is my session Factory...Here I have a single Sftp server information but how to configure multiple server details.
#Autowired
private DefaultSftpSessionFactory sftpSessionFactory;
#Bean
public DefaultSftpSessionFactory sftpSessionFactory() {
DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory(
true);
factory.setHost("111.11.12.143");
factory.setPort(22);
factory.setUser("sftp");
factory.setPassword("*******");
return factory;
}
and this is my Sftp Inbound flow..
#Bean
public IntegrationFlow sftpInboundFlow() {
System.out.println("enter sftpInboundFlow....."
+ sftpSessionFactory.getSession());
return IntegrationFlows
.from(Sftp.inboundAdapter(this.sftpSessionFactory)
.preserveTimestamp(true).remoteDirectory(remDir)
.regexFilter(".*\\.txt$")
.localFilenameExpression("#this.toUpperCase()")
.localDirectory(new File(localDir))
.remoteFileSeparator("/"),
new Consumer<SourcePollingChannelAdapterSpec>() {
#Override
public void accept(SourcePollingChannelAdapterSpec e) {
e.id("sftpInboundAdapter")
.autoStartup(true)
.poller(Pollers.fixedRate(1000)
.maxMessagesPerPoll(1));
}
})
//.channel(MessageChannels.queue("sftpInboundResultChannel"))
.channel(sftpInboundResultChannel())
.get();
}
As suggested by Gary I am editing my post....
Hi Gary,
I am taking the reference from Github dynamic FTP example.
Through the ChannelResolver class I need to call my above DSL class. and set the dynamic value in context property without using XML.
In my ChannelResolver class I want some thing like this
StandardEnvironment env = new StandardEnvironment();
Properties props = new Properties();
props.setProperty("inbound.host", host); //I am getting the value of 'host' from a DB table.
PropertiesPropertySource pps = new PropertiesPropertySource("sftpprop", props);
env.getPropertySources().addLast(pps);
context.setEnvironment(env);
And my DSL class I need to use like this.
#Value("${inbound.host}")
private String host;
So in this way can I set dynamic value for String 'host' ?
I am editing my original post...........
In my Outbound dynamic resolver class I am doing like this
StandardEnvironment env = new StandardEnvironment();
Properties props = new Properties();
props.setProperty("outbound.host", host);
props.setProperty("outbound.port", String.valueOf(port));
props.setProperty("outbound.user", user);
props.setProperty("outbound.password", password);
props.setProperty("outbound.remote.directory", remoteDir);
props.setProperty("outbound.local.directory", localDir);
PropertiesPropertySource pps = new PropertiesPropertySource("ftpprops", props);
env.getPropertySources().addLast(pps);
ctx.setEnvironment(env);
And this is my dsl class....
#Autowired
private DefaultSftpSessionFactory sftpSessionFactory;
#Bean
public DefaultSftpSessionFactory sftpSessionFactory(#Value("${outbound.host}") String host, #Value("${outbound.port}") int port,
#Value("${outbound.user}") String user, #Value("${outbound.password}") String password
) {
DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory(true);
factory.setHost(host);
factory.setPort(port);
factory.setUser(user);
factory.setPassword(password);
return factory;
}
#Bean
public IntegrationFlow fileInboundFlow(#Value("${outbound.local.directory}") String localDir)
{
return IntegrationFlows
.from(Files.inboundAdapter(new File(localDir)),
new Consumer<SourcePollingChannelAdapterSpec>() {
#Override
public void accept(SourcePollingChannelAdapterSpec e) {
e.autoStartup(true).poller(
Pollers.fixedDelay(5000)
.maxMessagesPerPoll(1));
}
})
.channel(sftpSendChannel())
.get();
}
#Bean
public IntegrationFlow sftpOutboundFlow(#Value("${outbound.remote.directory}") String remDir) {
return IntegrationFlows
.from(sftpSendChannel())
.handle(Sftp.outboundAdapter(this.sftpSessionFactory)
.useTemporaryFileName(false)
.remoteDirectory(remDir))
.get();
}
#Bean
public MessageChannel sftpSendChannel() {
return new DirectChannel();
}
#Bean
public static PropertySourcesPlaceholderConfigurer configurer1() {
return new PropertySourcesPlaceholderConfigurer();
}
And this the error log from console...
Aug 03, 2015 7:50:25 PM org.apache.catalina.core.StandardContext listenerStart
SEVERE: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sftpOutBoundDsl': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private org.springframework.integration.sftp.session.DefaultSftpSessionFactory com.tcs.iux.ieg.sftp.dynamic.SftpOutBoundDsl.sftpSessionFactory; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'outbound.host' in string value "${outbound.host}"
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1204)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:538)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:229)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:725)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:757)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:480)
at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:403)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:306)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:106)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4973)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5467)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549)
at java.util.concurrent.FutureTask.run(FutureTask.java:262)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:744)
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private org.springframework.integration.sftp.session.DefaultSftpSessionFactory com.tcs.iux.ieg.sftp.dynamic.SftpOutBoundDsl.sftpSessionFactory; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'outbound.host' in string value "${outbound.host}"
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:555)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331)
... 22 more
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'outbound.host' in string value "${outbound.host}"
at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:174)
at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:126)
at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:204)
at org.springframework.core.env.AbstractPropertyResolver.resolveRequiredPlaceholders(AbstractPropertyResolver.java:178)
at org.springframework.context.support.PropertySourcesPlaceholderConfigurer$2.resolveStringValue(PropertySourcesPlaceholderConfigurer.java:175)
at org.springframework.beans.factory.support.AbstractBeanFactory.resolveEmbeddedValue(AbstractBeanFactory.java:800)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:917)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:904)
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:815)
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:743)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:466)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1113)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1008)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:505)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:229)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1088)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1006)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:904)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:527)
... 24 more
It is currently not supported.
We have an open JIRA to add support for dynamic server selection but it's unlikely to be done in time for the upcoming 4.2 release.
You could work around it by writing your own custom delegating session factory that uses some criteria (e.g. a ThreadLocal) to determine which delegate factory to use.
EDIT:
As with the XML, you need a PropertySourcesPlaceholderConfigurer bean.
You should also use factory-method injection because the #Configuration class is created too early to have the #Value injected...
#Configuration
public class FooConfig {
#Bean
public DefaultSftpSessionFactory factory(
#Value("${inbound.host}") String host,
#Value("${inbound.port}") int port) {
DefaultSftpSessionFactory sf = new DefaultSftpSessionFactory();
sf.setHost(host);
sf.setPort(port);
return sf;
}
#Bean
public PropertySourcesPlaceholderConfigurer configurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
.
public class Testing {
#Test
public void test() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(FooConfig.class);
StandardEnvironment env = new StandardEnvironment();
Properties props = new Properties();
props.setProperty("inbound.host", "bar");
props.setProperty("inbound.port", "23");
PropertiesPropertySource pps = new PropertiesPropertySource("sftpprop", props);
env.getPropertySources().addLast(pps);
context.setEnvironment(env);
context.refresh();
DefaultSftpSessionFactory sessionFactory = context.getBean(DefaultSftpSessionFactory.class);
assertEquals("bar", TestUtils.getPropertyValue(sessionFactory, "host"));
context.close();
}
}
By the way, the delegating session factory will be in 4.2 after all.
EDIT2:
You can avoid the early instantiation of the config class and use global #Value injection, as long as you make the PSPC bean static...
#Configuration
public class FooConfig {
#Value("${foo}")
public String foo;
#Bean
public String earlyFoo() {
return this.foo;
}
#Bean
public String foo(#Value("${foo}") String foo) {
return foo;
}
#Bean
public static PropertySourcesPlaceholderConfigurer configurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
In this case, earlyFoo is populated as expected.

Resources