Download the eligible file from SFTP server by using spring Integration using Spring boot project - spring

My current project is based on Spring Integration. I am developing this project by using spring Boot.
My goal is to use Spring Integration to complete the below task.
Connect to SFTP
check if directory is created in the local at a specific folder
check the eligible file extension of the file specific to (CSV & XLSX)
Down load all the content from SFTP remote directory to local directory & need to track the file transfer start time and file transfer end time.
Read the file from local directory line by line and extract the certain column info.
Can you give me some suggestions ?
And how can I get the transfer start time ?
Note : This requirement i have to develop as an rest api. Please provide some guidance how i can achieve this by using spring integration?
Thanks. :)
public class SftpConfig {
#Value("${sftp.host}")
private String sftpHost;
#Value("${sftp.port:22}")
private int sftpPort;
#Value("${sftp.user}")
private String sftpUser;
#Value("${sftp.password:#{null}}")
private String sftpPasword;
#Value("${sftp.remote.directory:/}")
private String sftpRemoteDirectory;
#Value("${sftp.privateKey:#{null}}")
private Resource sftpPrivateKey;
#Value("${sftp.privateKeyPassPhrase:}")
private String privateKeyPassPhrase;
#Value("${sftp.remote.directory.download.filter:*.*}")
private String sftpRemoteDirectoryDownloadFilter;
#Value("${sftp.remote.directory.download:/}")
private String sftpRemoteDirectoryDownload;
#Value("${sftp.local.directory.download:${java.io.tmpdir}/localDownload}")
private String sftpLocalDirectoryDownload;
/*
* The SftpSessionFactory creates the sftp sessions. This is where you define
* the host , user and key information for your sftp server.
*/
// Creating session for Remote Destination SFTP server Folder
#Bean
public SessionFactory<ChannelSftp.LsEntry> sftpSessionFactory() {
DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory(true);
factory.setHost(sftpHost);
factory.setPort(sftpPort);
factory.setUser(sftpUser);
if (sftpPrivateKey != null) {
factory.setPrivateKey(sftpPrivateKey);
factory.setPrivateKeyPassphrase(privateKeyPassPhrase);
} else {
factory.setPassword("sftpPassword");
}
factory.setAllowUnknownKeys(true);
return new CachingSessionFactory<ChannelSftp.LsEntry>(factory);
}
/*
* The SftpInboundFileSynchronizer uses the session factory that we defined above.
* Here we set information about the remote directory to fetch files from.
* We could also set filters here to control which files get downloaded
*/
#Bean
public SftpInboundFileSynchronizer SftpInboundFileSynchronizer () {
SftpInboundFileSynchronizer synchronizer = new SftpInboundFileSynchronizer();
return null;
}

If you take a look into the SftpStreamingMessageSource instead: https://docs.spring.io/spring-integration/docs/current/reference/html/sftp.html#sftp-streaming, plus use a FileSplitter to read that file line by line (which supports a "first like as header", too), you won't need to worry about transferring the file to local dir. You will do everything with the remote content on demand.
On the other hand, since you talk about a REST API, you probably are going to have some #RestController or Spring Integration HTTP Inbound Gateway: https://docs.spring.io/spring-integration/docs/current/reference/html/http.html#http-inbound, then you need to think about using an SftpOutboundGateway with an MGET command: https://docs.spring.io/spring-integration/docs/current/reference/html/sftp.html#sftp-outbound-gateway.
If you still need to track a download time for every single file, you need to consider to use that gateway twice: with a LIST command and NAME_ONLY, and the second time for GET command. At this point you can add ChannelInterceptor for input and output channels of the second gateway, so you will have a file name info to correlate and catch start and stop time before and after this gateway.

Related

Spring Batch is looking for source files during deployment

I am developing Spring batch application on Spring Boot. Batch process is based on fire and forget, which means migration process is triggered by Http GET method and doesn't wait for result. There are 6 (six) csv files which containes migration data and these data must be processed and transferred to DB. Source files path is provided by application.yml file. Batch config (template) looks like as follows:
#Configuration
public class BeginCureBtachConfig {
#Value("$batch-files.begincure-source")
private String dataSourceFilePath;
#Autowired
IteamReader<BeginCure> itemReader;
#Autowired
ItemProcessor<BeginCure, BeginCure> itemProcessor;
#Bean Job beginCureJob(JobBuilderFactory jbFactory, StepBuilderDactory stp, ItemWriter<BeginCure> begnCure {
//// code goes here
}
#Bean
public FlatFileItemReader<BeginCure> beginCureItemReader() {
FlatFileItemReader<BeginCure> ffReader = new FlatFileItemReader<>();
ffReader.setResource(new FileSystemResource(dataSourceFilePath));
ffReader.setLineMapper(lineMapper());
return ffReader;
}
#Bean
public LineMapper<BeginCure> lineMapper () {
//..
}
}
The problem is when project is deployed on openshift, batch looks for files even migratin is not triggered.
In the code, I think this part of code giving some error ffReader.setResource(new FileSystemResource(dataSourceFilePath));
Is there any workaround or solution? Did anybody have this kind of problem?
Thank you in advance^^

Java Outbound Adaptor processing

I have created an application that adds inbound adaptors at run time for ftp servers and registers them for removing at a certain stage if needed, this app will pull a csv file from ftp server(s) and place it in my local in a folder having the name of the ftp server, so every server I add will have a separate local folder created and the csv file is saved in it, now this is accomplished smoothly, the second part is I want to change the format of that file and then send it back to the respective server, so basically I need to use outbound adaptor, in this case I would need to create outbound adaptors at run time at the same time when creating inbound adaptor or adding a server, this should be done through controller same as the inbound, I searched for possible solutions and tried a one that is below but did not work or did not perform any sending of files to destination, any solution on how I can accomplish this?
In the configuration class I added the below:
public IntegrationFlow ftpOutboundFlow(Branch myBranch){
return IntegrationFlows.from(OUTBOUND_CHANNEL)
.handle(Ftp.outboundAdapter(createNewFtpSessionFactory(myBranch), FileExistsMode.FAIL)
.useTemporaryFileName(true)
.remoteFileSeparator("/")
//.fileNameExpression("BEY/FEFOexportBEY.csv")
.remoteDirectory(myBranch.getFolderPath()))
.get();
}
#MessagingGateway
public interface MyGateway {
#Gateway(requestChannel = OUTBOUND_CHANNEL)
void sendToFtp(File file);
}
#Bean
public IntegrationFlow ftpOutboundChannel() {
return IntegrationFlows.from(OUTBOUND_CHANNEL)
.publishSubscribeChannel(p -> p
.subscribe(t -> t.handle(System.out::println)))
/*.transform(p -> {
LOG.info("Outbound intermediate Channel, message=rename file:" + p);
return p;
})*/
.channel(new NullChannel())
.get();
}
And in the Controller class
#RequestMapping("/branch/showbranch/{id}")
public String getBranch (#PathVariable String id, Model model){
model.addAttribute("branch", branchService.getById(Long.valueOf(id)));
addFlowftp(id);
addFlowftpOutbound(id);
return "/branch/showbranch";
}
private void addFlowFtp(String name) {
branch = branchService.getById(Long.valueOf(name));
System.out.println(branch.getBranchCode());
IntegrationFlow flow = ftIntegration.fileInboundFlowFromFTPServer(branch);
this.flowContext.registration(flow).id(name).register();
}
private void addFlowftpOutbound(String name) {
branch = branchService.getById(Long.valueOf(name));
System.out.println(branch.getBranchCode());
IntegrationFlow flow = ftIntegration.ftpOutboundFlow(branch);
// this.flowContext.registration(flow).id(name).register();
myGateway.sendToFtp(new File("BEY/FEFOexportBEY.csv"));
}
Here is what I get in the consol as error when I enable the register before sending the file:
java.lang.IllegalArgumentException: An IntegrationFlow 'IntegrationFlowRegistration{integrationFlow=StandardIntegrationFlow{integrationComponents={org.springframework.integration.ftp.inbound.FtpInboundFileSynchronizer#aadab28=98.org.springframework.integration.ftp.inbound.FtpInboundFileSynchronizer#0, org.springframework.integration.config.SourcePollingChannelAdapterFactoryBean#aa290b3=stockInboundPoller, org.springframework.integration.transformer.MethodInvokingTransformer#e5f85cd=98.org.springframework.integration.transformer.MethodInvokingTransformer#0, 98.channel#0=98.channel#0, org.springframework.integration.config.ConsumerEndpointFactoryBean#319dff2=98.org.springframework.integration.config.ConsumerEndpointFactoryBean#0}}, id='98', inputChannel=null}' with flowId '98' is already registered.
An existing IntegrationFlowRegistration must be destroyed before overriding.
After my second trial where I removed the registration from the first method and only tried with the second method but nothing was sent to the FTP:
GenericMessage [payload=BEY\FEFOexportBEY.csv, headers={id=43cfc2db-41e9-0866-8e4c-8e95968189ff, timestamp=1542702869007}]
2018-11-20 10:34:29.011 INFO 13716 --- [nio-8081-exec-6] f.s.s.configuration.FTIntegration : Outbound intermediate Channel, message=rename file:BEY\FEFOexportBEY.csv
You need to perform this.flowContext.registration(flow).id(name).register(); before sending the file via myGateway.sendToFtp(new File("BEY/FEFOexportBEY.csv"));.
That's first.
Other concern I see in your code that you have a ftpOutboundChannel bean for an IntegrationFlow which is subscribed to the same OUTBOUND_CHANNEL. If that one is not declared as a PublishSubscribeChannel, then you'll end up with the round-robin distribution. And I believe you would like to have file sent to the FTP and logged. So, indeed you need to declare that channel as a PublishSubscribeChannel.
You don't have any error because that OUTBOUND_CHANNEL has your ftpOutboundChannel IntegrationFlow as subscriber.

Discussion about spring integration sftp

I use spring integration sftp to download and upload files.In the document ,I found
Spring Integration supports sending and receiving files over SFTP by providing three client side endpoints: Inbound Channel Adapter, Outbound Channel Adapter, and Outbound Gateway
When I want to download files I must assign the local directory and when I want to upload files I must assign the remote directory.But if I can't assign the directory when I write the code such as my directory is association with date.How can I assign the directory at runtime?
Here is my code:
#Bean
public SessionFactory<LsEntry> sftpSessionFactory(){
DefaultSftpSessionFactory defaultSftpSessionFactory = new DefaultSftpSessionFactory();
defaultSftpSessionFactory.setHost(host);
defaultSftpSessionFactory.setPort(Integer.parseInt(port));
defaultSftpSessionFactory.setUser(username);
defaultSftpSessionFactory.setPassword(password);
defaultSftpSessionFactory.setAllowUnknownKeys(true);
return new CachingSessionFactory<LsEntry>(defaultSftpSessionFactory);
}
#Bean
public SftpRemoteFileTemplate sftpRemoteFileTemplate(){
SftpRemoteFileTemplate sftpRemoteFileTemplate = new SftpRemoteFileTemplate(sftpSessionFactory());
return sftpRemoteFileTemplate;
}
#Bean
#ServiceActivator(inputChannel = "sftpChannel")
public MessageHandler handlerGet() {
SftpOutboundGateway sftpOutboundGateway = new SftpOutboundGateway(sftpSessionFactory(), "mget", "payload");
sftpOutboundGateway.setLocalDirectory(new File(localDirectory));
sftpOutboundGateway.setFilter(new SftpSimplePatternFileListFilter("*.txt"));
sftpOutboundGateway.setSendTimeout(1000);
return sftpOutboundGateway;
}
In the messageHandler,I must assign the localDirectory in the outboundGateway. And when I want change my localDirectory by days.I must download the file to the localDirectory and move to the target directory. How can I assign the localDirectory at runtime .such as today I download to 20170606/ and tomorrow I download to 20170607 ?
edit
this is my option and test
public interface OutboundGatewayOption {
#Gateway(requestChannel = "sftpChannel")
public List<File> getFiles(String dir);
}
#Test
public void test2(){
outboundGatewayOption.getFiles("upload/20160920/");
}
sftpOutboundGateway.setLocalDirectoryExpression(
new SpelExpressionParser().parseExpression("headers['whereToPutTheFiles']");
or parseExpression("#someBean.getDirectoryName(payload)")
etc.
The expression must evaluate to a String representing the directory absolute path.
While evaluating the expression, the remote directory is available as a variable #remoteDirectory.

File Polling using Spring Integration

How to poll multiple files from a particular file directory using Spring Integration Api without xml configuration preferably using Java annotations based approach?
I want to get the polled files list and iterate through them and process further. This is the requirement. Any Sample
Code available to meet this requirement. Thanks in Advance. Below is the code fragment I used.
#Bean
#InboundChannelAdapter(value = "fileInputChannel", poller = #Poller(fixedDelay = "60000",maxMessagesPerPoll="5"))
public MessageSource<File> fileReadingMessageSource() {
txtSource = new FileReadingMessageSource();
txtSource.setDirectory(inputDir);
txtSource.setFilter(new SimplePatternFileListFilter("*.txt"));
txtSource.setScanEachPoll(true);
return txtSource;
}
#Bean
#Transformer(inputChannel = "fileInputChannel", outputChannel = "processFileChannel")
public FileToStringTransformer fileToStringTransformer() {
Message<File> message1 = txtSource.receive();
File file1 = message1.getPayload();
return new FileToStringTransformer();
}
But irrespective of no of files in the input dir the message source instance always fetch only one file. Not sure how to make it work for multiple files to be fetched.
You can try adding a task-executor to your implementation
Follow this post for polling multiple files concurrently, but it doesn't follow the annotation based approach. And this post for sequential polling of files. And this also involves the use of annotations.

How to copy files from remote machine(other machine) using apche Camel?

Below is code for copy files(any type of file) from remote machine or any other machine which is in our LAN. How to automatically copy files Using Apche Camel.
I want to copy images, so any method to copy only images.
I implement this in spring.
final String sourceFolder = "ftp:172.30.83.119\\D:MyCLone\\WSIMS\\images?username=shailesh.bhad&password=Password2";
final String destinationFolder = "file:D:\\outbox";
final CamelContext camelContext = new DefaultCamelContext();
camelContext.addRoutes(new RouteBuilder() {
#Override
public void configure() {
from(sourceFolder).to(destinationFolder);
}
});
camelContext.start();
Thread.sleep(30000);
camelContext.stop();
}
See the FTP example
http://camel.apache.org/ftp-example.html
And then insted of sending to another FTP server, you can change the url to use the file component so it can store the files in your LAN
And see also this FAQ how to keep a Java JVM running
http://camel.apache.org/running-camel-standalone-and-have-it-keep-running.html

Resources