/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.samples.filesplit;
import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
import org.aopalliance.intercept.MethodInterceptor;
import org.apache.commons.net.ftp.FTPFile;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.mail.MailProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.integration.dsl.IntegrationFlows;
import org.springframework.integration.dsl.Pollers;
import org.springframework.integration.file.FileHeaders;
import org.springframework.integration.file.FileWritingMessageHandler;
import org.springframework.integration.file.dsl.FileWritingMessageHandlerSpec;
import org.springframework.integration.file.dsl.Files;
import org.springframework.integration.file.remote.session.SessionFactory;
import org.springframework.integration.file.splitter.FileSplitter;
import org.springframework.integration.file.support.FileExistsMode;
import org.springframework.integration.ftp.dsl.Ftp;
import org.springframework.integration.ftp.session.DefaultFtpSessionFactory;
import org.springframework.integration.http.config.EnableIntegrationGraphController;
import org.springframework.integration.mail.dsl.Mail;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.MessagingException;
@SpringBootApplication
@EnableIntegrationGraphController(allowedOrigins = "http://localhost:8082")
public class Application {
private static final String EMAIL_SUCCESS_SUFFIX = "emailSuccessSuffix";
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Autowired
private MailProperties mailProperties;
/**
* Poll for files, add an error channel, split into lines route the start/end markers
* to {@link #markers()} and the lines to {@link #lines}.
*
* @return the flow.
*/
@Bean
public IntegrationFlow fromFile() {
return IntegrationFlows.from(
Files.inboundAdapter(new File("/tmp/in"))
.preventDuplicates(false)
.patternFilter("*.txt"), e -> e.poller(Pollers.fixedDelay(5000)
.errorChannel("tfrErrors.input")))
.handle(Files.splitter(true, true))
.<Object, Class<?>>route(Object::getClass, m -> m
.channelMapping(FileSplitter.FileMarker.class, "markers.input")
.channelMapping(String.class, "lines.input"))
.get();
}
/**
* Process lines; append (no flush) to the appropriate file.
*
* @return the flow.
*/
@Bean
public IntegrationFlow lines(FileWritingMessageHandler fileOut) {
return f -> f.handle(fileOut);
}
@Bean
public FileWritingMessageHandlerSpec fileOut() {
return Files.outboundAdapter("'/tmp/out'")
.appendNewLine(true)
.fileNameExpression("payload.substring(1, 4) + '.txt'")
.fileExistsMode(FileExistsMode.APPEND_NO_FLUSH); // files remain open for efficiency
}
/**
* Process file markers; ignore START, when END, flush the files, ftp them and
* send an email.
*
* @return the flow.
*/
@Bean
public IntegrationFlow markers() {
return f -> f.<FileSplitter.FileMarker>filter(m -> m.getMark().equals(FileSplitter.FileMarker.Mark.END),
e -> e.id("markerFilter"))
.publishSubscribeChannel(s -> s
// first trigger file flushes
.subscribe(sf -> sf.transform("'/tmp/out/.*\\.txt'", e -> e.id("toTriggerPattern"))
.trigger("fileOut", e -> e.id("flusher")))
// send the first file
.subscribe(sf -> sf.<FileSplitter.FileMarker, File>transform(p -> new File("/tmp/out/002.txt"))
.enrichHeaders(h -> h.header(FileHeaders.FILENAME, "002.txt", true))
.handle(Ftp.outboundAdapter(ftp1()).remoteDirectory("foo"), e -> e.id("ftp002")))
// send the second file
.subscribe(sf -> sf.<FileSplitter.FileMarker, File>transform(p -> new File("/tmp/out/006.txt"))
.enrichHeaders(h -> h.header(FileHeaders.FILENAME, "006.txt", true))
.handle(Ftp.outboundAdapter(ftp2()).remoteDirectory("foo"), e -> e.id("ftp006")))
// send the third file
.subscribe(sf -> sf.<FileSplitter.FileMarker, File>transform(p -> new File("/tmp/out/009.txt"))
.enrichHeaders(h -> h.header(FileHeaders.FILENAME, "009.txt", true))
.handle(Ftp.outboundAdapter(ftp3()).remoteDirectory("foo"), e -> e.id("ftp009")))
// send an email
.subscribe(sf -> sf.transform(FileSplitter.FileMarker::getFilePath)
.enrichHeaders(Mail.headers()
.subject("File successfully split and transferred")
.from("foo@bar")
.toFunction(m -> new String[]{"bar@baz"}))
.enrichHeaders(h -> h.header(EMAIL_SUCCESS_SUFFIX, ".success"))
.channel("toMail.input")));
}
@Bean
public SessionFactory<FTPFile> ftp1() {
DefaultFtpSessionFactory ftp = new DefaultFtpSessionFactory();
ftp.setHost("host3");
ftp.setUsername("user");
ftp.setPassword("ftp");
return ftp;
}
@Bean
public SessionFactory<FTPFile> ftp2() {
DefaultFtpSessionFactory ftp = new DefaultFtpSessionFactory();
ftp.setHost("host3");
ftp.setUsername("user");
ftp.setPassword("ftp");
return ftp;
}
@Bean
public SessionFactory<FTPFile> ftp3() {
DefaultFtpSessionFactory ftp = new DefaultFtpSessionFactory();
ftp.setHost("host3");
ftp.setUsername("user");
ftp.setPassword("ftp");
return ftp;
}
/**
* Error flow - email failure
*
* @return the flow.
*/
@Bean
public IntegrationFlow tfrErrors() {
return f -> f
.enrichHeaders(Mail.headers()
.subject("File split and transfer failed")
.from("foo@bar")
.toFunction(m -> new String[]{"bar@baz"}))
.enrichHeaders(h -> h.header(EMAIL_SUCCESS_SUFFIX, ".failed")
.headerExpression(FileHeaders.ORIGINAL_FILE, "payload.failedMessage.headers['"
+ FileHeaders.ORIGINAL_FILE + "']"))
.<MessagingException, String>transform(p ->
p.getFailedMessage().getPayload().toString() + "\n" + getStackTraceAsString(p))
.channel("toMail.input");
}
@Bean
public IntegrationFlow toMail() {
return f -> f
.handle(Mail.outboundAdapter(this.mailProperties.getHost())
// .javaMailProperties(b -> b.put("mail.debug", "true"))
.port(this.mailProperties.getPort())
.credentials(this.mailProperties.getUsername(), this.mailProperties.getPassword()),
e -> e.id("mailOut").advice(afterMailAdvice()));
}
/**
* Rename the input file after success/failure.
*
* @return the flow.
*/
@Bean
public MethodInterceptor afterMailAdvice() {
return invocation -> {
Message<?> message = (Message<?>) invocation.getArguments()[0];
MessageHeaders headers = message.getHeaders();
File originalFile = headers.get(FileHeaders.ORIGINAL_FILE, File.class);
try {
invocation.proceed();
originalFile.renameTo(
new File(originalFile.getAbsolutePath() + headers.get(EMAIL_SUCCESS_SUFFIX)));
}
catch (Exception e) {
originalFile.renameTo(
new File(originalFile.getAbsolutePath() + headers.get(EMAIL_SUCCESS_SUFFIX) + ".email.failed"));
}
return null;
};
}
private String getStackTraceAsString(Throwable cause) {
StringWriter stringWriter = new StringWriter();
PrintWriter printWriter = new PrintWriter(stringWriter, true);
cause.printStackTrace(printWriter);
return stringWriter.getBuffer().toString();
}
}