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.io.FileNotFoundException;
021
022import org.apache.camel.Component;
023import org.apache.camel.Exchange;
024import org.apache.camel.Processor;
025import org.apache.camel.processor.idempotent.MemoryIdempotentRepository;
026import org.apache.camel.spi.UriEndpoint;
027import org.apache.camel.spi.UriParam;
028import org.apache.camel.spi.UriPath;
029import org.apache.camel.util.FileUtil;
030import org.apache.camel.util.ObjectHelper;
031
032/**
033 * File endpoint.
034 */
035@UriEndpoint(scheme = "file", consumerClass = FileConsumer.class)
036public class FileEndpoint extends GenericFileEndpoint<File> {
037
038    private final FileOperations operations = new FileOperations(this);
039    @UriPath
040    private File file;
041    @UriParam
042    private boolean copyAndDeleteOnRenameFail = true;
043    @UriParam
044    private boolean renameUsingCopy;
045    @UriParam
046    private boolean forceWrites = true;
047
048    public FileEndpoint() {
049        // use marker file as default exclusive read locks
050        this.readLock = "markerFile";
051    }
052
053    public FileEndpoint(String endpointUri, Component component) {
054        super(endpointUri, component);
055        // use marker file as default exclusive read locks
056        this.readLock = "markerFile";
057    }
058
059    public FileConsumer createConsumer(Processor processor) throws Exception {
060        ObjectHelper.notNull(operations, "operations");
061        ObjectHelper.notNull(file, "file");
062
063        // auto create starting directory if needed
064        if (!file.exists() && !file.isDirectory()) {
065            if (isAutoCreate()) {
066                log.debug("Creating non existing starting directory: {}", file);
067                boolean absolute = FileUtil.isAbsolute(file);
068                boolean created = operations.buildDirectory(file.getPath(), absolute);
069                if (!created) {
070                    log.warn("Cannot auto create starting directory: {}", file);
071                }
072            } else if (isStartingDirectoryMustExist()) {
073                throw new FileNotFoundException("Starting directory does not exist: " + file);
074            }
075        }
076
077        FileConsumer result = newFileConsumer(processor, operations);
078
079        if (isDelete() && getMove() != null) {
080            throw new IllegalArgumentException("You cannot set both delete=true and move options");
081        }
082
083        // if noop=true then idempotent should also be configured
084        if (isNoop() && !isIdempotentSet()) {
085            log.info("Endpoint is configured with noop=true so forcing endpoint to be idempotent as well");
086            setIdempotent(true);
087        }
088
089        // if idempotent and no repository set then create a default one
090        if (isIdempotentSet() && isIdempotent() && idempotentRepository == null) {
091            log.info("Using default memory based idempotent repository with cache max size: " + DEFAULT_IDEMPOTENT_CACHE_SIZE);
092            idempotentRepository = MemoryIdempotentRepository.memoryIdempotentRepository(DEFAULT_IDEMPOTENT_CACHE_SIZE);
093        }
094
095        // set max messages per poll
096        result.setMaxMessagesPerPoll(getMaxMessagesPerPoll());
097        result.setEagerLimitMaxMessagesPerPoll(isEagerMaxMessagesPerPoll());
098
099        configureConsumer(result);
100        return result;
101    }
102
103    public GenericFileProducer<File> createProducer() throws Exception {
104        ObjectHelper.notNull(operations, "operations");
105
106        // you cannot use temp file and file exists append
107        if (getFileExist() == GenericFileExist.Append && ((getTempPrefix() != null) || (getTempFileName() != null))) {
108            throw new IllegalArgumentException("You cannot set both fileExist=Append and tempPrefix/tempFileName options");
109        }
110
111        // ensure fileExist and moveExisting is configured correctly if in use
112        if (getFileExist() == GenericFileExist.Move && getMoveExisting() == null) {
113            throw new IllegalArgumentException("You must configure moveExisting option when fileExist=Move");
114        } else if (getMoveExisting() != null && getFileExist() != GenericFileExist.Move) {
115            throw new IllegalArgumentException("You must configure fileExist=Move when moveExisting has been set");
116        }
117
118        return new GenericFileProducer<File>(this, operations);
119    }
120
121    public Exchange createExchange(GenericFile<File> file) {
122        Exchange exchange = createExchange();
123        if (file != null) {
124            file.bindToExchange(exchange);
125        }
126        return exchange;
127    }
128
129    /**
130     * Strategy to create a new {@link FileConsumer}
131     *
132     * @param processor  the given processor
133     * @param operations file operations
134     * @return the created consumer
135     */
136    protected FileConsumer newFileConsumer(Processor processor, GenericFileOperations<File> operations) {
137        return new FileConsumer(this, processor, operations);
138    }
139
140    public File getFile() {
141        return file;
142    }
143
144    public void setFile(File file) {
145        this.file = file;
146        // update configuration as well
147        getConfiguration().setDirectory(FileUtil.isAbsolute(file) ? file.getAbsolutePath() : file.getPath());
148    }
149
150    @Override
151    public String getScheme() {
152        return "file";
153    }
154
155    @Override
156    protected String createEndpointUri() {
157        return getFile().toURI().toString();
158    }
159
160    @Override
161    public char getFileSeparator() {       
162        return File.separatorChar;
163    }
164
165    @Override
166    public boolean isAbsolute(String name) {
167        // relative or absolute path?
168        return FileUtil.isAbsolute(new File(name));
169    }
170
171    public boolean isCopyAndDeleteOnRenameFail() {
172        return copyAndDeleteOnRenameFail;
173    }
174
175    public void setCopyAndDeleteOnRenameFail(boolean copyAndDeleteOnRenameFail) {
176        this.copyAndDeleteOnRenameFail = copyAndDeleteOnRenameFail;
177    }
178
179    public boolean isRenameUsingCopy() {
180        return renameUsingCopy;
181    }
182
183    public void setRenameUsingCopy(boolean renameUsingCopy) {
184        this.renameUsingCopy = renameUsingCopy;
185    }
186
187    public boolean isForceWrites() {
188        return forceWrites;
189    }
190
191    public void setForceWrites(boolean forceWrites) {
192        this.forceWrites = forceWrites;
193    }
194}