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 */
017package org.apache.camel.component.file;
018
019import java.io.File;
020import java.util.Arrays;
021import java.util.List;
022
023import org.apache.camel.Processor;
024import org.apache.camel.util.FileUtil;
025import org.apache.camel.util.ObjectHelper;
026
027/**
028 * File consumer.
029 */
030public 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}