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() && depth < endpoint.getMaxDepth() && isValidFile(gf, true, files)) {
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 (depth >= endpoint.minDepth && isValidFile(gf, false, files)) {
097                        log.trace("Adding valid file: {}", file);
098                        // matched file so add
099                        fileList.add(gf);
100                    }
101    
102                }
103            }
104    
105            return true;
106        }
107    
108        @Override
109        protected boolean isMatched(GenericFile<File> file, String doneFileName, List<File> files) {
110            String onlyName = FileUtil.stripPath(doneFileName);
111            // the done file name must be among the files
112            for (File f : files) {
113                if (f.getName().equals(onlyName)) {
114                    return true;
115                }
116            }
117            log.trace("Done file: {} does not exist", doneFileName);
118            return false;
119        }
120    
121        /**
122         * Creates a new GenericFile<File> based on the given file.
123         *
124         * @param endpointPath the starting directory the endpoint was configured with
125         * @param file the source file
126         * @return wrapped as a GenericFile
127         */
128        public static GenericFile<File> asGenericFile(String endpointPath, File file, String charset) {
129            GenericFile<File> answer = new GenericFile<File>();
130            // use file specific binding
131            answer.setBinding(new FileBinding());
132    
133            answer.setCharset(charset);
134            answer.setEndpointPath(endpointPath);
135            answer.setFile(file);
136            answer.setFileNameOnly(file.getName());
137            answer.setFileLength(file.length());
138            answer.setDirectory(file.isDirectory());
139            // must use FileUtil.isAbsolute to have consistent check for whether the file is
140            // absolute or not. As windows do not consider \ paths as absolute where as all
141            // other OS platforms will consider \ as absolute. The logic in Camel mandates
142            // that we align this for all OS. That is why we must use FileUtil.isAbsolute
143            // to return a consistent answer for all OS platforms.
144            answer.setAbsolute(FileUtil.isAbsolute(file));
145            answer.setAbsoluteFilePath(file.getAbsolutePath());
146            answer.setLastModified(file.lastModified());
147    
148            // compute the file path as relative to the starting directory
149            File path;
150            String endpointNormalized = FileUtil.normalizePath(endpointPath);
151            if (file.getPath().startsWith(endpointNormalized + File.separator)) {
152                // skip duplicate endpoint path
153                path = new File(ObjectHelper.after(file.getPath(), endpointNormalized + File.separator));
154            } else {
155                path = new File(file.getPath());
156            }
157    
158            if (path.getParent() != null) {
159                answer.setRelativeFilePath(path.getParent() + File.separator + file.getName());
160            } else {
161                answer.setRelativeFilePath(path.getName());
162            }
163    
164            // the file name should be the relative path
165            answer.setFileName(answer.getRelativeFilePath());
166    
167            // use file as body as we have converters if needed as stream
168            answer.setBody(file);
169            return answer;
170        }
171    
172        @Override
173        public FileEndpoint getEndpoint() {
174            return (FileEndpoint) super.getEndpoint();
175        }
176    }