Spring Integration + Inbound channel adapter + Recursive directory scanner - file

Inbound channel adapter is created with a poller to poll files present in root directory and its sub directories
e.g.
RootDir
|_abc.txt
|_subdirectory1
|_subdirfile1.doc
The problem is inbound channel adapter is reading the directory also as message
#Bean
#InboundChannelAdapter(autoStartup = "false", value = "incomingchannel", poller = #Poller("custompoller"))
public MessageSource<File> fileReadingMessageSource(DirectoryScanner directoryScanner) {
FileReadingMessageSource sourceReader = new FileReadingMessageSource();
sourceReader.setScanner(directoryScanner);
}
#Bean
public DirectoryScanner directoryScanner() {
DirectoryScanner scanner = new RecursiveDirectoryScanner();
CompositeFileListFilter filter = new CompositeFileListFilter<>(
Arrays.asList(new AcceptOnceFileListFilter<>(), new RegexPatternFileListFilter(regex)));
scanner.setFilter(filter);
return scanner;
}
#Trasnformer(inputChannel="incomingchannel",....
torequest(Mesage<File> message) {
message.getPayload()
}
Here message.getpayLoad is printing subdirectory1 i.e. directory is also read as a file message
I can handle explicitly as file is directory or not in trasnformer and ignore, but wanted to know is there any way it can be filtered in Recursive Directory scanner attached to Inbound Channel adapter ?

This problem is probably related to this SO thread: Spring Integration + file reading message source _ Inbound Channel Adapter + Outbound Gateway.
You need to think twice if you are OK loosing file tree. It sounded for me that you would like to restore a tree in the FileWritingMessageHandler. So, it is probably better to #Filter messages with directory payload before sending to that transformer.
If you still want to skip dirs from the producing, consider to use a ChainFileListFilter instead of CompositeFileListFilter and configure a RegexPatternFileListFilter first.
This way a filtered directory from the RegexPatternFileListFilter (it is skipped by default see AbstractDirectoryAwareFileListFilter) won't go to the AcceptOnceFileListFilter at all. In your current configuration the AcceptOnceFileListFilter being first accepts a directory and really ignores the next filter in the composition.
UPDATE
What I mean should be like this:
#Bean
public DirectoryScanner directoryScanner() {
DirectoryScanner scanner = new RecursiveDirectoryScanner();
ChainFileListFilter filter = new ChainFileListFilter<>(
Arrays.asList(new RegexPatternFileListFilter(regex), new AcceptOnceFileListFilter<>()));
scanner.setFilter(filter);
return scanner;
}
Nothing more. As long as your regex is just for files, any sub-directory would be skipped and not allowed to go downstream.

Related

Spring Integration + file reading message source _ Inbound Channel Adapter + Outbound Gateway

For File reading message source Inbound Adapter and transformer
with annotations is configured as below
#Bean
#InboundChannelAdapter(autoStartup = "false", value = "incomingchannel", poller = #Poller("custompoller"))
public MessageSource<File> fileReadingMessageSource() {
}
#Transformer(inputChannel = "incomingchannel", outputChannel = "jobLaunchChannel")
public JobLaunchRequest toRequest(Message<File> message) throws Exception {
}
Now I want to change the Transformer to refer to a reply channel of outbound gateway i.e. which moves the files from one directory to another directory i.e. move the file from incomingchannel directory to a different directory and the process or transform he file or perform some validations
<file:outbound-gateway id="mover" request-channel="incomingchannel" reply-channel="newdirectory" directory="<<path to new directory file to be moved" delete-source-files="true"/>
Anyone has converted above XML configuration to annotation configurations or any ideas?
After annotation configurations I will have to change the transformer input channel to refer to newdirectory channel i.e. which is a reply channel of messaging gateway...
Thanks in advance for any help ot suggestions regarding this
--- Update 1 after trying out the snippet provided in link by Artem
#Bean
#ServiceActivator(inputChannel = "incomingchannel")
public MessageHandler fileWritingMessageHandler() {
FileWritingMessageHandler handler = new FileWritingMessageHandler(new File(newdirectorypath));
handler.setFileExistsMode(FileExistsMode.APPEND);
handler.setDeleteSourceFiles(true);
return handler;
}
#MessagingGateway(defaultRequestChannel = "incomingchannel", defaultReplyChannel = "newdirectorychannel")
public interface MyGateway {
void writeToFile(#Header(FileHeaders.FILENAME) String fileName, #Header(FileHeaders.FILENAME) File directory,
String data);
}
But there are two problems encountered
Inbound Adapter is trying to poll the directory also as file (Recursive Directory scanner is used) - How to ensure that directory is not polled as a file
nested exception is org.springframework.messaging.core.DestinationResolutionException: no output-channel or replyChannel header available, failedMessage=GenericMessage [payload=C
Ok. Since it looks like you would like to place the FileWritingMessageHandler after #InboundChannelAdapter and before #Transformer, so this should like:
#Bean
#InboundChannelAdapter(autoStartup = "false", value = "incomingchannel", poller = #Poller("custompoller"))
public MessageSource<File> fileReadingMessageSource() {
}
#Bean
#ServiceActivator(inputChannel = "incomingchannel")
public MessageHandler fileWritingMessageHandler() {
FileWritingMessageHandler handler = new FileWritingMessageHandler(new File(newdirectorypath));
handler.setFileExistsMode(FileExistsMode.APPEND);
handler.setDeleteSourceFiles(true);
handler.setOutputChannelName("jobLaunchTransfromerCannel");
return handler;
}
#Transformer(inputChannel = "jobLaunchTransfromerCannel", outputChannel = "jobLaunchChannel")
public JobLaunchRequest toRequest(Message<File> message) throws Exception {
}
This way an #InboundChannelAdapter sends a File into a FileWritingMessageHandler for its logic, which produces a result file for the next in flow #Transformer to convert a result file into a JobLaunchRequest. And only after that a message is going to be sent to the jobLaunchChannel to file a Spring Batch Job.

Create Event-Driven Consumer on File Endpoint without RouteBuilder in Camel 2.24

I want to run a processor upon file appearance in a directory. My file url is like this:
file:{{file.root}}in?include=.*\.csv&charset=windows-1251&move=../out/done
A procedure that associates an url with a processor is like this:
MessageProcessor getOrCreateConsumer(CamelContext context, String uri) {
Endpoint endpoint = context.getEndpoint(uri);
endpoint.setCamelContext(context); // added this out of desperation, doesn't help
processor = new MessageProcessor();
try {
Consumer consumer = endpoint.createConsumer(processor);
endpoint.start(); // do we need this at all? works the same without it
consumer.start();
} catch (Exception e) {
throw new RuntimeException(e);
}
return processor;
}
}
MessageProcessor is a processor that does some things to an exchange.
Everything seems to work except the file doesn't get moved to the ../out/done directory. While debugging I can't get when the endpoint is configured to provide the file message exchange with this post operation.
I think I am missing some magic call that is normally invoked by a RouteBuilder and that will fully configure the file endpoint. Can you please help me out?

Camel File Consumer when folder is not accessible

Hy all,
when using the file consumer on a folder, which the camel context does not have access to (e.g. using chmod a-rwx test/locked) the context starts as normal (not even warnings) but also doesn't pick up any files in the folder (well, how should it).
Is there a way to have the file component thrown an error on startup (or at least on polling the folder), when it can't access the folder?
Otherwise I will have to check it using manual java code before configuring the route, which I would like to avoid.
Thanks and greets
Chris
Otherwise I will have to check it using manual java code before configuring the route, which I would like to avoid.
You can use a PollingConsumerPollStrategy with the endpoint to check the whether the path has access issues, then raise error.
public class FileAccessPollingConsumerPollStrategy extends DefaultPollingConsumerPollStrategy{
public boolean begin(Consumer consumer, Endpoint endpoint) {
File pollingDirectory = ((FileEndpoint) endpoint).getFile();
//check your access with poillingDirectory and raise any error.
return true;
}
}
Then configure your camel endpoint - from("file://inbox/?pollStrategy=#fileAccessPollingConsumerPollStrategy")
I settled on executing the following code before the route is configured and thus making sure it can be accessed:
Path path = Paths.get(folderPath);
// The only case we do not allow is when the folder exists and is not readable
boolean canAccess = !Files.exists(path) || Files.isReadable(path);
if (!canAccess) {
throw new IllegalArgumentException("Could not initialize route, because folder \""
+ baseFolder + "\" can not be accessed. This might happen when it does not exist "
+ "or there are no read permission for the current user");
}
I also created an issue in the apache jira to add the feature in the future:
https://issues.apache.org/jira/browse/CAMEL-13483
Thanks everbody for the suggestions
Chris

Camel File component - skip file

I'm using the camel file component to poll files from a directory.
Before I can handle a file some conditions have to be satisfied,
if not camel should skip the file without deleting/moving it and
go to the next one.
To do this I use this:
public InputStream myMethod(#Body InputStream is, #Headers .....) {
if( !checkPrerequisites )
throw new MyRuntimeException("conditions not satisfied yet");
So I'm wondering if there is another way to archive the desired behaviour.
You could implement a GenericFileFilter. Create the filter, like so:
public class AnotherFileExistsFilter<T> implements GenericFileFilter<T> {
#Override
public boolean accept(GenericFile<T> firstFile) {
return Files.exists(Paths.get("/some/other/folder/" + firstFile.getFileName()));
}
}
Add it to your endpoint using filter=#anotherFileExistsBeanName.
If you want to keep checking the file, set idempotent=false, and I recommend setting a delay (delay=xxx in ms) in order to not poll the folder continuously.
More details are on the Apache Camel File2 page.

ADF: How to get path of file when using InputFile Component

I am using jdeveloper version 11.1.1.5.0. In my use case I have created Mail Client Send Mail program where I used ADF InputFile component to attach File on mail.
But problem is that InputFile Component only return path of file(only get file name). And in my mail program DataSource class use full path to access file name.
UploadedFile uploadfile=(UploadedFile) actionEvent.getNewValue();
String fname= uploadfile.getFilename();//this line only get file name.
So how can I get full path using adf InputFile component or any other way to fulfill my requirement.
You could save the uploaded file in a path at the server. Only take care about naming that file, because of concurrency of users you should follow a policy about it, for example, adding te time in milliseconds to the name of the file. Like this...
private String writeToFile(UploadedFile file) {
ServletContext servletCtx =
(ServletContext)FacesContext.getCurrentInstance().getExternalContext().getContext();
String fileDirPath = servletCtx.getRealPath("/files/tmp");
String fileName = getTimeInMilis()+file.getFilename();
try {
InputStream is = file.getInputStream();
OutputStream os =
new FileOutputStream(fileDirPath + "/"+fileName);
int readData;
while ((readData = is.read()) != -1) {
os.write(readData);
}
is.close();
os.close();
} catch (IOException ex) {
ex.printStackTrace();
}
return fileName;
}
This method also returns the new name of the uploaded file. You can replace getTimeInMilis() with any naming policy you like.
It would be a security issue if a web app is able to see anything other than the data stream for an uploaded file. The directory structure of the client would not be exposed to the webapp. As such, unless you plan to upload the file from the same host as the server, you will not have access to the file path on the client.
Note: Using answer instead of comment due to reputation threshold

Resources