Spring Batch: Configuring service-activator - spring-integration

I'm trying to add a service-activator in my Spring Batch .XML configuration file which sets requires-reply=false in my transformer method: FileMessageToJobRequestTransformer.
Here's the XML:
<int:transformer id="fileMessageToJobRequestTransformer"
input-channel="inboundFileChannel"
output-channel="outboundJobRequestChannel"
method="transform" >
<bean class="com.distributedfinance.mbi.bai.transformer.FileMessageToJobRequestTransformer">
<property name="job" ref="baiParseJob"/>
<property name="fileParameterName" value="input.file.url"/>
</bean>
<int:poller fixed-rate="1000"/>
</int:transformer>
<int:service-activator requires-reply="false" input-channel="inboundFileChannel" output-channel="outboundJobRequestChannel" ref="fileMessageToJobRequestTransformer"> </int:service-activator>
Here's the stack trace:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.integration.config.ConsumerEndpointFactoryBean#3': Cannot resolve reference to bean 'org.springframework.integration.config.ServiceActivatorFactoryBean#1' while setting bean property 'handler'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.integration.config.ServiceActivatorFactoryBean#1': FactoryBean threw exception on object creation; nested exception is java.lang.IllegalArgumentException: Found ambiguous parameter type [class java.lang.Long] for method match: [public void org.springframework.integration.endpoint.AbstractPollingEndpoint.setTrigger(org.springframework.scheduling.Trigger), public void org.springframework.integration.endpoint.AbstractPollingEndpoint.setErrorHandler(org.springframework.util.ErrorHandler), public void org.springframework.integration.context.IntegrationObjectSupport.setComponentName(java.lang.String), public void org.springframework.integration.context.IntegrationObjectSupport.setApplicationContext(org.springframework.context.ApplicationContext) throws org.springframework.beans.BeansException, public void org.springframework.integration.endpoint.AbstractEndpoint.setAutoStartup(boolean), public void org.springframework.integration.endpoint.AbstractPollingEndpoint.setMaxMessagesPerPoll(long), public void org.springframework.integration.endpoint.AbstractEndpoint.setTaskScheduler(org.springframework.scheduling.TaskScheduler), public void org.springframework.integration.endpoint.AbstractEndpoint.setPhase(int), public void org.springframework.integration.context.IntegrationObjectSupport.getComponentType(), public void org.springframework.integration.endpoint.AbstractPollingEndpoint.setTransactionSynchronizationFactory(org.springframework.integration.transaction.TransactionSynchronizationFactory), public final void org.springframework.integration.context.IntegrationObjectSupport.setBeanFactory(org.springframework.beans.factory.BeanFactory)]
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:175)
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:103)
at org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance(AbstractBeanFactory.java:1584)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:253)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:196)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:351)
... 23 more
Caused by: java.lang.IllegalArgumentException: Found ambiguous parameter type [class java.lang.Long] for method match: [public void org.springframework.integration.endpoint.AbstractPollingEndpoint.setTrigger(org.springframework.scheduling.Trigger), public void org.springframework.integration.endpoint.AbstractPollingEndpoint.setErrorHandler(org.springframework.util.ErrorHandler), public void org.springframework.integration.context.IntegrationObjectSupport.setComponentName(java.lang.String), public void org.springframework.integration.context.IntegrationObjectSupport.setApplicationContext(org.springframework.context.ApplicationContext) throws org.springframework.beans.BeansException, public void org.springframework.integration.endpoint.AbstractEndpoint.setAutoStartup(boolean), public void org.springframework.integration.endpoint.AbstractPollingEndpoint.setMaxMessagesPerPoll(long), public void org.springframework.integration.endpoint.AbstractEndpoint.setTaskScheduler(org.springframework.scheduling.TaskScheduler), public void org.springframework.integration.endpoint.AbstractEndpoint.setPhase(int), public void org.springframework.integration.endpoint.AbstractPollingEndpoint.setBeanClassLoader(java.lang.ClassLoader), public void org.springframework.integration.endpoint.AbstractPollingEndpoint.setAdviceChain(java.util.List), public void org.springframework.integration.context.IntegrationObjectSupport.setChannelResolver(org.springframework.messaging.core.DestinationResolver), public final void org.springframework.integration.endpoint.AbstractEndpoint.stop(java.lang.Runnable), public void org.springframework.integration.endpoint.AbstractPollingEndpoint.setTaskExecutor(java.util.concurrent.Executor), public void org.springframework.integration.context.IntegrationObjectSupport.setMessageBuilderFactory(org.springframework.integration.support.MessageBuilderFactory), public java.lang.String org.springframework.integration.context.IntegrationObjectSupport.getComponentType(), public void org.springframework.integration.endpoint.AbstractPollingEndpoint.setTransactionSynchronizationFactory(org.springframework.integration.transaction.TransactionSynchronizationFactory), public final void org.springframework.integration.context.IntegrationObjectSupport.setBeanFactory(org.springframework.beans.factory.BeanFactory)]
at org.springframework.util.Assert.isNull(Assert.java:92)
at org.springframework.integration.util.MessagingMethodInvokerHelper.findHandlerMethodsForTarget(MessagingMethodInvokerHelper.java:446)
at org.springframework.integration.util.MessagingMethodInvokerHelper.<init>(MessagingMethodInvokerHelper.java:192)
at org.springframework.integration.util.MessagingMethodInvokerHelper.<init>(MessagingMethodInvokerHelper.java:136)
at org.springframework.integration.util.MessagingMethodInvokerHelper.<init>(MessagingMethodInvokerHelper.java:131)
at org.springframework.integration.handler.MethodInvokingMessageProcessor.<init>(MethodInvokingMessageProcessor.java:57)
at org.springframework.integration.handler.ServiceActivatingHandler.<init>(ServiceActivatingHandler.java:37)
at org.springframework.integration.config.ServiceActivatorFactoryBean.createMethodInvokingHandler(ServiceActivatorFactoryBean.java:57)
at org.springframework.integration.config.AbstractStandardMessageHandlerFactoryBean.createHandler(AbstractStandardMessageHandlerFactoryBean.java:97)
at org.springframework.integration.config.AbstractSimpleMessageHandlerFactoryBean.createHandlerInternal(AbstractSimpleMessageHandlerFactoryBean.java:116)
at org.springframework.integration.config.AbstractSimpleMessageHandlerFactoryBean.getObject(AbstractSimpleMessageHandlerFactoryBean.java:104)
at org.springframework.integration.config.AbstractSimpleMessageHandlerFactoryBean.getObject(AbstractSimpleMessageHandlerFactoryBean.java:46)
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:168)
... 28 more
Process finished with exit code 1
UPDATE:
#Transformer
public JobLaunchRequest transform(Message<File> message) {
LOGGER.debug("File message: {}", message);
String jobName = message.getPayload().getAbsolutePath().toString();
if (jobName.contains(".gitignore"))
return null;
JobParametersBuilder jobParametersBuilder = new JobParametersBuilder();
jobParametersBuilder.addString(fileParameterName,
"file://" + message.getPayload().getAbsolutePath());
LOGGER.debug("Job params: {}", jobParametersBuilder.toJobParameters());
Integer pathDelimiterOffset = jobName.lastIndexOf('/');
if (pathDelimiterOffset != -1) {
String fileName = jobName.substring(pathDelimiterOffset+1);
String archiveDirectoryName = "bai/archive/";
String archivePathName = archiveDirectoryName.concat(fileName);
File f = new File(archivePathName);
// If the file exists in the bai/archive directory - assume it has already been processed - SKIP IT! This is not a as reliable as querying Spring Batch tablle batch_execution_params... which will be done next.
if (f.exists() && !f.isDirectory())
return null;
}
try {
return new JobLaunchRequest(job, jobParametersBuilder.toJobParameters());
}
catch (Exception e) {
if (e instanceof JobInstanceAlreadyCompleteException) {
LOGGER.debug("Exception Handler: JobInstanceAlreadyCompleteException - Job params: {}", jobParametersBuilder.toJobParameters());
}
}
return null;
}
SftpBaiParserJobBridge-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:batch-int="http://www.springframework.org/schema/batch-integration"
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/batch-integration
http://www.springframework.org/schema/batch-integration/spring-batch-integration.xsd">
<!-- The bridge between SFTP spring integration to BAI parse spring batch job -->
<int:channel id="outboundJobRequestChannel"/>
<int:channel id="jobLaunchReplyChannel">
<int:queue/>
<int:interceptors>
<int:wire-tap channel="logger"/>
</int:interceptors>
</int:channel>
<!-- When getting a new BAI file, transform into spring batch job request -->
<int:transformer id="fileMessageToJobRequestTransformer"
input-channel="inboundFileChannel"
output-channel="outboundJobRequestChannel"
method="transform" >
<bean class="com.distributedfinance.mbi.bai.transformer.FileMessageToJobRequestTransformer">
<property name="job" ref="baiParseJob"/>
<property name="fileParameterName" value="input.file.url"/>
</bean>
<int:poller fixed-rate="60000" max-messages-per-poll="100"/>
</int:transformer>
<!-- Gateway to launch the BAI parse spring batch job -->
<batch-int:job-launching-gateway request-channel="outboundJobRequestChannel"
reply-channel="jobLaunchReplyChannel">
</batch-int:job-launching-gateway>
<int:logging-channel-adapter id="logger" level="WARN"
logger-name="com.distributedfinance.mbi.bai.job"/>
</beans>
SftpInboundReceive-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-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/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">
<!-- Create an inbound channel adapter to receive the BAI files via SFTP -->
<import resource="SftpSession-context.xml"/>
<int:channel id="inboundFileChannel">
<int:queue/>
</int:channel>
<int-sftp:inbound-channel-adapter id="sftpInboundAdapter"
auto-startup="true"
channel="inboundFileChannel"
session-factory="sftpSessionFactory"
local-directory="${bai.sftp.local-dir}"
remote-directory="${bai.sftp.remote-dir}"
auto-create-local-directory="true"
preserve-timestamp="true"
delete-remote-files="${bai.sftp.delete}">
<int:poller cron="${bai.sftp.cron}" max-messages-per-poll="10"/>
</int-sftp:inbound-channel-adapter>
</beans>
BaiParserJob.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:batch="http://www.springframework.org/schema/batch"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/batch
http://www.springframework.org/schema/batch/spring-batch.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/spring-util.xsd">
<bean name="jobParametersIncrementer" class="org.springframework.batch.core.launch.support.RunIdIncrementer"/>
<!-- BATCH-2351 workaround -->
<bean id="stepScope" class="org.springframework.batch.core.scope.StepScope">
<property name="autoProxy" value="true"/>
</bean>
<batch:job id="baiParseJob" incrementer="jobParametersIncrementer">
<batch:step id="baiParseStep" next="baiArchive">
<batch:tasklet transaction-manager="transactionManager">
<batch:chunk reader="baiItemReader"
processor="baiItemProcessor"
writer="baiItemWriter"
commit-interval="1"/>
</batch:tasklet>
</batch:step>
<batch:step id="baiArchive">
<batch:tasklet ref="fileArchivingTasklet"/>
</batch:step>
</batch:job>
<bean id="baiItemReader" class="com.distributedfinance.mbi.bai.reader.MultiLineBaiItemReader"
scope="step">
<property name="delegate" ref="flatFileItemReader"/>
<property name="baiFileFieldSetMapper">
<bean class="com.distributedfinance.mbi.bai.mapper.BaiFileFieldSetMapper"/>
</property>
<property name="baiGroupFieldSetMapper">
<bean class="com.distributedfinance.mbi.bai.mapper.BaiGroupFieldSetMapper"/>
</property>
<property name="baiAccountFieldSetMapper">
<bean class="com.distributedfinance.mbi.bai.mapper.BaiAccountFieldSetMapper">
<property name="parser">
<bean class="com.distributedfinance.mbi.bai.mapper.BaiTypeParser"/>
</property>
</bean>
</property>
<property name="baiTransactionFieldSetMapper">
<bean class="com.distributedfinance.mbi.bai.mapper.BaiTransactionFieldSetMapper"/>
</property>
</bean>
<bean id="flatFileItemReader" class="org.springframework.batch.item.file.FlatFileItemReader" scope="step">
<property name="resource" value="#{jobParameters['input.file.url']}"/>
<property name="lineMapper">
<bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
<property name="lineTokenizer">
<bean class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer"/>
</property>
<property name="fieldSetMapper">
<bean class="org.springframework.batch.item.file.mapping.PassThroughFieldSetMapper"/>
</property>
</bean>
</property>
</bean>
<bean id="baiItemProcessor" class="com.distributedfinance.mbi.bai.processor.BaiItemProcessor">
<constructor-arg index="0" ref="accountLookup"/>
<constructor-arg index="1">
<bean class="java.text.SimpleDateFormat">
<constructor-arg value="yyMMddHHmmss"/>
</bean>
</constructor-arg>
</bean>
<bean id="baiItemWriter" class="com.distributedfinance.mbi.bai.writer.BaiItemWriter"/>
<bean id="accountLookup" class="com.distributedfinance.mbi.bai.lookup.AccountLookup"/>
<bean id="fileArchivingTasklet" class="com.distributedfinance.mbi.bai.tasklet.FileArchivingTasklet">
<property name="downloadFileKey" value="input.file.url"/>
<property name="archiveDirectory" value="${bai.sftp.archive-dir}"/>
<property name="purgeDays" value="${bai.sftp.purge-days}"/>
</bean>
</beans>

You can't reference the fileMessageToJobRequestTransformer bean from a service-activator; the framework doesn't know which method to call. The actual transformer bean is wrapped in a consumer bean.
It looks like you don't need the service activator at all - you already have the transformer subscribed to that channel input-channel="inboundFileChannel".
With your configuration you have two consumers on that channel - the transformer and the service activator.
EDIT
Transformers are always expected to return a reply; since your class might or might not return a reply, it's not really a transformer (in the strictest sense), it's a service.
Simply use
<int:service-activator id="fileMessageToJobRequestTransformer"
input-channel="inboundFileChannel"
output-channel="outboundJobRequestChannel"
requires-reply="false"
method="transform" >
<bean class="com.distributedfinance.mbi.bai.transformer.FileMessageToJobRequestTransformer">
<property name="job" ref="baiParseJob"/>
<property name="fileParameterName" value="input.file.url"/>
</bean>
<int:poller fixed-rate="1000"/>
</int:service-activator>

Related

Renaming the original file when it's handled successfully by ftp adapter

I have the following Spring Integration configuration:
#Bean
public IntegrationFlow myFlow() {
return IntegrationFlows.from(Files.inboundAdapter(new File(inputDir))
.patternFilter("*.csv"),
poller -> poller.poller(pm -> pm.cron(cronExpression)))
.transform(Transformers.fileToByteArray())
.handleWithAdapter(adapters -> adapters.ftp(ftpSessionFactory())
.remoteDirectory(remoteDir))
.get();
}
It's working properly but how can I rename the original file to mark it as processed when the ftp adapter has been successful?
This can be done using ExpressionEvaluatingRequestHandlerAdvice. In xml config it looks like:
<int-ftp:outbound-channel-adapter
channel="inputChannel"
session-factory="mockSessionFactory"
remote-directory="foo">
<int-ftp:request-handler-advice-chain>
<bean class="org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice">
<property name="onSuccessExpression" value="payload.delete()" />
<property name="successChannel" ref="afterSuccessDeleteChannel" />
<property name="onFailureExpression" value="payload.renameTo(new java.io.File(payload.absolutePath + '.failed.to.send'))" />
<property name="failureChannel" ref="afterFailRenameChannel" />
</bean>
</int-ftp:request-handler-advice-chain>
</int-ftp:outbound-channel-adapter>

Dynamic delay configuration to find file size or line count

I have spring xd aws-s3 source and I have configured to change to poller delay depending on my file size.Now in my trigger advice afterReceive method How can i get fileSize or lineCount inside my file from Message<?> result?Ideally line count can be good so i can tune delay according to lines or file size
stream create aws-s3|log
<?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:context="http://www.springframework.org/schema/context"
xmlns:int-aws="http://www.springframework.org/schema/integration/aws"
xmlns:int-file="http://www.springframework.org/schema/integration/file"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration/file
http://www.springframework.org/schema/integration/file/spring-integration-file.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.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">
<context:property-placeholder location="classpath*:dms-security-${region}.properties" />
<int:poller fixed-delay="${fixedDelay}" default="true" trigger="dynamicTrigger" ">
<int:advice-chain>
<ref bean="pollAdvise" />
<ref bean="smartPollAdvise" />
</int:advice-chain>
</int:poller>
<bean id="dynamicTrigger"
class="org.springframework.integration.util.DynamicPeriodicTrigger">
<constructor-arg name="period" value="5000" />
</bean>
<bean id="pollAdvise" class="org.springframework.integration.scheduling.PollSkipAdvice">
<constructor-arg ref="healthCheckStrategy"/>
</bean>
<bean id="smartPollAdvise" class="com.test.api.dms.main.TriggerAdvise">
<property name="trigger" ref="dynamicTrigger"/>
</bean>
<bean id="healthCheckStrategy" class="com.test.api.dms.main.ServiceHealthCheckPollSkipStrategy">
<property name="url" value="${url}"/>
<property name="doHealthCheck" value="${doHealthCheck}"/>
<property name="restTemplate" ref="restTemplate"/>
</bean>
<bean id="restTemplate"
class="org.springframework.web.client.RestTemplate">
<constructor-arg ref="requestFactory"/>
</bean>
<bean id="requestFactory"
class="com.test.api.dms.main.BatchClientHttpRequestFactory">
<constructor-arg ref="verifier"/>
</bean>
<bean id="verifier"
class="com.test.api.dms.main.NullHostnameVerifier">
</bean>
<bean id="encryptedDatum" class="com.test.api.dms.core.security.EncryptedSecuredDatum"/>
<!-- aws-endpoint="https://s3.amazonaws.com" proxyHost="proxy.kdc.test.com" proxyPort="8099"-->
<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="com.test.api.dms.main.CustomC1AmazonS3Operations">
<constructor-arg index="0" ref="clientConfiguration"/>
<property name="awsEndpoint" value="s3.amazonaws.com"/>
<property name="temporaryDirectory" value="${temporaryDirectory}"/>
<property name="awsSecurityKey" value="#{encryptedDatum.decryptBase64Encoded('${awsSecurityKey}')}"/>
</bean>
<bean id="credentials" class="org.springframework.integration.aws.core.BasicAWSCredentials">
</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="${fileNameWildcard}"
remote-directory="${prefix}"
channel="splitChannel"
local-directory="${localDirectory}"
accept-sub-folders="false"
delete-source-files="true"
archive-bucket="${archiveBucket}"
archive-directory="${archiveDirectory}">
</int-aws:s3-inbound-channel-adapter>
<int-file:splitter input-channel="splitChannel" output-channel="output" markers="false" charset="UTF-8">
<int-file:request-handler-advice-chain>
<bean class="org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice">
<property name="onSuccessExpression" value="payload.delete()"/>
</bean>
</int-file:request-handler-advice-chain>
</int-file:splitter>
<int:channel-interceptor pattern="*" order="3">
<bean class="org.springframework.integration.channel.interceptor.WireTap">
<constructor-arg ref="loggingChannel" />
</bean>
</int:channel-interceptor>
<int:logging-channel-adapter id="loggingChannel" log-full-message="true" level="INFO"/>
<int:channel id="output"/>
</beans>
public class TriggerAdvice extends AbstractMessageSourceAdvice {
private final DynamicPeriodicTrigger trigger;
private volatile long nextPollPeriod;
public TriggerAdvice(DynamicPeriodicTrigger trigger) {
this.trigger = trigger;
this.nextPollPeriod = trigger.getPeriod();
}
public long getNextPollPeriod() {
return nextPollPeriod;
}
public void setNextPollPeriod(long nextPollPeriod) {
this.nextPollPeriod = nextPollPeriod;
}
#Override
public boolean beforeReceive(MessageSource<?> source) {
return true;
}
#Override
public Message<?> afterReceive(Message<?> result, MessageSource<?> source) {
if (result == null) {
this.trigger.setPeriod(this.nextPollPeriod);
}
return null;
}
}
The aws-s3 source returns the Message<File>, so your afterReceive can just cast the getPayload() and use standard Java mechanism to count lines from the file:
LineNumberReader lnr = new LineNumberReader(new FileReader((File) result.getPayload());
lnr.skip(Long.MAX_VALUE);
System.out.println(lnr.getLineNumber() + 1); //Add 1 because line index starts at 0
// Finally, the LineNumberReader object should be closed to prevent resource leak
lnr.close();

Spring Integration DynamicFtpChannelResolver - Unable to autowire service bean

I have implemented the DynamicFtpChannelResolver found in the spring integration samples in order to allow dynamic FTP locations in my integration app.
As part of the outbound adapter I have added an advice chain. The FtpCompleteAdvice requires access to an existing service bean but at runtime this is not injected presumably because the context is dynamically created.
Is there a way for autowiring to work or another way to get access to this service bean?
Here is an extract of the xml:
<int:channel id="toFtpChannel" />
<bean id="ftpClientFactory" class="org.springframework.integration.sftp.session.DefaultSftpSessionFactory">
<property name="host" value="${host}" />
<property name="port" value="${port}" />
<property name="user" value="${user}" />
<property name="password" value="${password}" />
</bean>
<int-sftp:outbound-channel-adapter id="ftpOutbound" session-factory="ftpClientFactory" channel="toFtpChannel" remote-directory="${remote.directory}" remote-file-separator="/" remote-filename-generator-expression="headers.filename">
<int-sftp:request-handler-advice-chain>
<bean class="org.springframework.integration.handler.advice.RequestHandlerRetryAdvice">
<property name="retryTemplate" ref="retryTemplate" />
</bean>
<bean class="com.bstonetech.ptms.integration.util.FtpCompleteAdvice">
<property name="interfaceType" value="OUTBOUND" />
<property name="interfaceName" value="TEST" />
</bean>
</int-sftp:request-handler-advice-chain>
</int-sftp:outbound-channel-adapter>
<bean id="retryTemplate" class="org.springframework.retry.support.RetryTemplate">
<property name="retryPolicy">
<bean class="org.springframework.retry.policy.SimpleRetryPolicy">
<property name="maxAttempts" value="3" />
</bean>
</property>
<property name="backOffPolicy">
<bean class="org.springframework.retry.backoff.ExponentialBackOffPolicy">
<property name="maxInterval" value="600000" />
<property name="initialInterval" value="3000" />
<property name="multiplier" value="2" />
</bean>
</property>
</bean>
public class FtpCompleteAdvice extends AbstractRequestHandlerAdvice {
#Autowired
private IEmailUtilities emailUtilities;
#Autowired
private IFileService fileService;
private String interfaceType;
private String interfaceName;
public void setInterfaceType(String interfaceType) {
this.interfaceType = interfaceType;
}
public void setInterfaceName(String interfaceName) {
this.interfaceName = interfaceName;
}
#Override
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
Object result = callback.execute();
String filename = (String)message.getHeaders().get("filename");
//insert ftp row into file_ctl
fileService.insertFtpFile(filename, interfaceName, interfaceType, new Date());
//send email to confirm ftp
emailUtilities.afterFtp(filename, interfaceName);
return result;
}
}
You need to make the new context a child of the main context that has those beans. This is discussed in the forum posts referenced in the sample's README, when using a similar technique for inbound endpoints.
Then, any beans in the parent context are available for wiring.

Unable to implement SpringSecurity-Hibernate simplest example

I was going through following post Spring login form example and many other post of Spring Security to create a login example but I was not able to prepare any simple example.
I was trying to incorporate solution from this post Spring login form example
but problem is that I want Hibernate session factory to be injected in the UserDAO so that I can write query to get userName from table. #Autowire is not working
so I used
<context:annotation-config />
<context:component-scan
base-package="com.tcs.ceg" />
but now i am getting runtime exception as cannot autowire sessionFactory as no matching bean found.
But a bean with this name I have created in my applicationContext.xml .
Can anyone tell me how can I inject sessionFactory?
my application-security.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd
">
<context:annotation-config />
<context:component-scan
base-package="com.tcs.ceg" />
<global-method-security pre-post-annotations="enabled" />
<http pattern="/css/**" security="none"/>
<http pattern="/images/**" security="none"/>
<http pattern="/js/**" security="none"/>
<http pattern="/index.jsp" security="none"/>
<http pattern="/loggedout.jsp" security="none"/>
<http use-expressions="true">
<!--
Allow all other requests. In a real application you should
adopt a whitelisting approach where access is not allowed by default
-->
<intercept-url pattern="/**" access="isAuthenticated()" />
<form-login />
<logout logout-success-url="/loggedout.jsp" delete-cookies="JSESSIONID"/>
<remember-me />
</http>
<beans:bean id="myUserService" class="com.tcs.ceg.services.impl.UserServiceImpl" />
<authentication-manager>
<authentication-provider user-service-ref="myUserService" />
</authentication-manager>
</beans:beans>
and my applicationContext.xml file
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:lang="http://www.springframework.org/schema/lang"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd" >
<context:annotation-config />
<mvc:annotation-driven />
<context:component-scan
base-package="com.tcs.ceg" />
<bean id="viewResolver"
class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass">
<value>
org.springframework.web.servlet.view.tiles2.TilesView
</value>
</property>
</bean>
<bean id="tilesConfigurer"
class="org.springframework.web.servlet.view.tiles2.TilesConfigurer">
<property name="definitions">
<list>
<value>/WEB-INF/tiles.xml</value>
</list>
</property>
</bean>
<bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="classpath:messages" />
<property name="defaultEncoding" value="UTF-8"/>
</bean>
<bean id="localeChangeInterceptor"
class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
<property name="paramName" value="lang" />
</bean>
<bean id="localeResolver"
class="org.springframework.web.servlet.i18n.CookieLocaleResolver">
<property name="defaultLocale" value="en"/>
</bean>
<bean id="handlerMapping"
class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="interceptors">
<ref bean="localeChangeInterceptor" />
</property>
</bean>
<jee:jndi-lookup id="dataSource1" jndi-name="jdbc/PmdDS"/>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource1" />
<property name="configLocation">
<value>classpath:hibernate.cfg.xml</value>
</property>
<property name="configurationClass">
<value>org.hibernate.cfg.AnnotationConfiguration</value>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="current_session_context_class">thread</prop>
<prop key="cache.provider_class">org.hibernate.cache.NoCacheProvider</prop>
</props>
</property>
</bean>
<tx:annotation-driven />
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- one of the properties available; the maximum file size in bytes -->
<property name="maxUploadSize" value="1000000000000"/>
</bean>
</beans>
Code for UserServiceImpl
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.tcs.ceg.dao.intrface.UserDao;
#Service
public class UserServiceImpl implements UserDetailsService {
#Autowired
private transient UserDao userDao;//userDao is null
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
#Override
#Transactional
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
// TODO Auto-generated method stub
User user=null;
try{
user = userDao.getUser(username);
}catch(Exception err){
err.printStackTrace();
}
if (user != null) {
// convert roles
// initialize user
return user;
} else {
throw new UsernameNotFoundException("No user with username '" + username + "' found!");
}
}
}
Code for UserDaoImpl
import java.util.ArrayList;
import java.util.Collection;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Repository;
import com.tcs.ceg.dao.intrface.UserDao;
import com.tcs.ceg.util.lib.DbComparisonException;
#Repository
public class UserDaoimpl implements UserDao {
#Autowired
private SessionFactory sessionFactory;//sessionfactory is null
#Override
public User getUser(String username) throws DbComparisonException {
String password = "rajesh";
boolean enabled = true;
boolean accountNonExpired = true;
boolean credentialsNonExpired = true;
boolean accountNonLocked = true;
Collection<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new GrantedAuthorityImpl("admin"));
User user = new User(username, password, enabled,
accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
return user;
}
}
Please note i havent written following line in UserDaoimpl because sessionFactory is null
sessionFactory.getCurrentSession().createQuery("from User").list();//throws null pointer exception
I think that the sessionFactory attribute of your DAO must be of type LocalSessionFactoryBean.
EDIT :
In your applicationContext.xml file, you have declared this bean :
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource1" />
<property name="configLocation">
<value>classpath:hibernate.cfg.xml</value>
</property>
<property name="configurationClass">
<value>org.hibernate.cfg.AnnotationConfiguration</value>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="current_session_context_class">thread</prop>
<prop key="cache.provider_class">org.hibernate.cache.NoCacheProvider</prop>
</props>
</property>
</bean>
which is a org.springframework.orm.hibernate3.LocalSessionFactoryBean.
Then, in your DAO, sessionFactory is a org.hibernate.SessionFactory.
So I think that Spring is unable to autowire your bean in the DAO since it is not the same type.

Spring 3.0 Annonation-based AutoWiring

In the below xml configuartion, i have a sql query which needs to be injected to empDAO.
<bean id="propertyPlaceholderConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>/WEB-INF/conf/db.properties</value>
<value>/WEB-INF/conf/query.properties</value>
</list>
</property>
</bean>
<bean id="empDAO" class="com.dao.EmployeeDAO">
<!-- How to do Annotation-based autowire for the string-->
<property name="selectTradeQ" value="${select.emp}" />
</bean>
My question is How to use Annotation-autowire for the String? Some thing like below
//This is not possible ?? Then how to do this
<bean id="selectTradeQ" value="${select.emp}>
#Component
public class EmployeeDAO {
#Value("${select.emp}")
private String selectTradeQ;
}

Resources