001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.camel.component.file;
018    
019    import java.io.File;
020    import java.util.Arrays;
021    import java.util.List;
022    
023    import org.apache.camel.Processor;
024    import org.apache.camel.util.FileUtil;
025    import org.apache.camel.util.ObjectHelper;
026    
027    /**
028     * File consumer.
029     */
030    public class FileConsumer extends GenericFileConsumer<File> {
031    
032        private String endpointPath;
033    
034        public FileConsumer(GenericFileEndpoint<File> endpoint, Processor processor, GenericFileOperations<File> operations) {
035            super(endpoint, processor, operations);
036            this.endpointPath = endpoint.getConfiguration().getDirectory();
037        }
038    
039        @Override
040        protected boolean pollDirectory(String fileName, List<GenericFile<File>> fileList, int depth) {
041            log.trace("pollDirectory from fileName: {}", fileName);
042    
043            depth++;
044    
045            File directory = new File(fileName);
046            if (!directory.exists() || !directory.isDirectory()) {
047                log.debug("Cannot poll as directory does not exists or its not a directory: {}", directory);
048                if (getEndpoint().isDirectoryMustExist()) {
049                    throw new GenericFileOperationFailedException("Directory does not exist: " + directory);
050                }
051                return true;
052            }
053    
054            log.trace("Polling directory: {}", directory.getPath());
055            File[] dirFiles = directory.listFiles();
056            if (dirFiles == null || dirFiles.length == 0) {
057                // no files in this directory to poll
058                if (log.isTraceEnabled()) {
059                    log.trace("No files found in directory: {}", directory.getPath());
060                }
061                return true;
062            } else {
063                // we found some files
064                if (log.isTraceEnabled()) {
065                    log.trace("Found {} in directory: {}", dirFiles.length, directory.getPath());
066                }
067            }
068            List<File> files = Arrays.asList(dirFiles);
069    
070            for (File file : files) {
071                // check if we can continue polling in files
072                if (!canPollMoreFiles(fileList)) {
073                    return false;
074                }
075    
076                // trace log as Windows/Unix can have different views what the file is?
077                if (log.isTraceEnabled()) {
078                    log.trace("Found file: {} [isAbsolute: {}, isDirectory: {}, isFile: {}, isHidden: {}]",
079                            new Object[]{file, file.isAbsolute(), file.isDirectory(), file.isFile(), file.isHidden()});
080                }
081    
082                // creates a generic file
083                GenericFile<File> gf = asGenericFile(endpointPath, file, getEndpoint().getCharset());
084    
085                if (file.isDirectory()) {
086                    if (endpoint.isRecursive() && isValidFile(gf, true, files) && depth < endpoint.getMaxDepth()) {
087                        // recursive scan and add the sub files and folders
088                        String subDirectory = fileName + File.separator + file.getName();
089                        boolean canPollMore = pollDirectory(subDirectory, fileList, depth);
090                        if (!canPollMore) {
091                            return false;
092                        }
093                    }
094                } else {
095                    // Windows can report false to a file on a share so regard it always as a file (if its not a directory)
096                    if (isValidFile(gf, false, files) && depth >= endpoint.minDepth) {
097                        if (isInProgress(gf)) {
098                            if (log.isTraceEnabled()) {
099                                log.trace("Skipping as file is already in progress: {}", gf.getFileName());
100                            }
101                        } else {
102                            log.trace("Adding valid file: {}", file);
103                            // matched file so add
104                            fileList.add(gf);
105                        }
106                    }
107    
108                }
109            }
110    
111            return true;
112        }
113    
114        @Override
115        protected boolean isMatched(GenericFile<File> file, String doneFileName, List<File> files) {
116            String onlyName = FileUtil.stripPath(doneFileName);
117            // the done file name must be among the files
118            for (File f : files) {
119                if (f.getName().equals(onlyName)) {
120                    return true;
121                }
122            }
123            log.trace("Done file: {} does not exist", doneFileName);
124            return false;
125        }
126    
127        /**
128         * Creates a new GenericFile<File> based on the given file.
129         *
130         * @param endpointPath the starting directory the endpoint was configured with
131         * @param file the source file
132         * @return wrapped as a GenericFile
133         */
134        public static GenericFile<File> asGenericFile(String endpointPath, File file, String charset) {
135            GenericFile<File> answer = new GenericFile<File>();
136            // use file specific binding
137            answer.setBinding(new FileBinding());
138    
139            answer.setCharset(charset);
140            answer.setEndpointPath(endpointPath);
141            answer.setFile(file);
142            answer.setFileNameOnly(file.getName());
143            answer.setFileLength(file.length());
144            answer.setDirectory(file.isDirectory());
145            // must use FileUtil.isAbsolute to have consistent check for whether the file is
146            // absolute or not. As windows do not consider \ paths as absolute where as all
147            // other OS platforms will consider \ as absolute. The logic in Camel mandates
148            // that we align this for all OS. That is why we must use FileUtil.isAbsolute
149            // to return a consistent answer for all OS platforms.
150            answer.setAbsolute(FileUtil.isAbsolute(file));
151            answer.setAbsoluteFilePath(file.getAbsolutePath());
152            answer.setLastModified(file.lastModified());
153    
154            // compute the file path as relative to the starting directory
155            File path;
156            String endpointNormalized = FileUtil.normalizePath(endpointPath);
157            if (file.getPath().startsWith(endpointNormalized + File.separator)) {
158                // skip duplicate endpoint path
159                path = new File(ObjectHelper.after(file.getPath(), endpointNormalized + File.separator));
160            } else {
161                path = new File(file.getPath());
162            }
163    
164            if (path.getParent() != null) {
165                answer.setRelativeFilePath(path.getParent() + File.separator + file.getName());
166            } else {
167                answer.setRelativeFilePath(path.getName());
168            }
169    
170            // the file name should be the relative path
171            answer.setFileName(answer.getRelativeFilePath());
172    
173            // use file as body as we have converters if needed as stream
174            answer.setBody(file);
175            return answer;
176        }
177    
178        @Override
179        public FileEndpoint getEndpoint() {
180            return (FileEndpoint) super.getEndpoint();
181        }
182    }