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.io.IOException;
021 import java.lang.reflect.Method;
022 import java.util.Properties;
023
024 import org.apache.camel.Consumer;
025 import org.apache.camel.ExchangePattern;
026 import org.apache.camel.Message;
027 import org.apache.camel.Processor;
028 import org.apache.camel.Producer;
029 import org.apache.camel.component.file.strategy.FileProcessStrategySupport;
030 import org.apache.camel.impl.ScheduledPollEndpoint;
031 import org.apache.camel.util.FactoryFinder;
032 import org.apache.camel.util.ObjectHelper;
033 import org.apache.camel.util.UuidGenerator;
034 import org.apache.commons.logging.Log;
035 import org.apache.commons.logging.LogFactory;
036
037 /**
038 * A <a href="http://activemq.apache.org/camel/file.html">File Endpoint</a> for
039 * working with file systems
040 *
041 * @version $Revision: 664624 $
042 */
043 public class FileEndpoint extends ScheduledPollEndpoint<FileExchange> {
044 private static final transient Log LOG = LogFactory.getLog(FileEndpoint.class);
045 private static final String DEFAULT_STRATEGYFACTORY_CLASS =
046 "org.apache.camel.component.file.strategy.FileProcessStrategyFactory";
047
048 private File file;
049 private FileProcessStrategy fileProcessStrategy;
050 private boolean autoCreate = true;
051 private boolean lock = true;
052 private boolean delete;
053 private boolean noop;
054 private boolean append = true;
055 private String moveNamePrefix;
056 private String moveNamePostfix;
057 private String[] excludedNamePrefixes = {"."};
058 private String[] excludedNamePostfixes = {FileProcessStrategySupport.DEFAULT_LOCK_FILE_POSTFIX};
059 private int bufferSize = 128 * 1024;
060 private boolean ignoreFileNameHeader;
061
062 protected FileEndpoint(File file, String endpointUri, FileComponent component) {
063 super(endpointUri, component);
064 this.file = file;
065 }
066
067 public FileEndpoint(String endpointUri, File file) {
068 super(endpointUri);
069 this.file = file;
070 }
071
072 public FileEndpoint(File file) {
073 this.file = file;
074 }
075
076 public FileEndpoint() {
077 }
078
079 public Producer<FileExchange> createProducer() throws Exception {
080 Producer<FileExchange> result = new FileProducer(this);
081 return result;
082 }
083
084 public Consumer<FileExchange> createConsumer(Processor processor) throws Exception {
085 Consumer<FileExchange> result = new FileConsumer(this, processor);
086 configureConsumer(result);
087 return result;
088 }
089
090 /**
091 * Create a new exchange for communicating with this endpoint
092 *
093 * @param file the file
094 * @return the created exchange
095 */
096 public FileExchange createExchange(File file) {
097 return new FileExchange(getCamelContext(), getExchangePattern(), file);
098 }
099
100 @Override
101 public FileExchange createExchange() {
102 return createExchange(getFile());
103 }
104
105 @Override
106 public FileExchange createExchange(ExchangePattern pattern) {
107 return new FileExchange(getCamelContext(), pattern, file);
108 }
109
110
111 /**
112 * Return the file name that will be auto-generated for the given message if none is provided
113 */
114 public String getGeneratedFileName(Message message) {
115 return getFileFriendlyMessageId(message.getMessageId());
116 }
117
118 /**
119 * Configures the given message with the file which sets the body to the file object
120 * and sets the {@link FileComponent#HEADER_FILE_NAME} header.
121 */
122 public void configureMessage(File file, Message message) {
123 message.setBody(file);
124 String relativePath = file.getPath().substring(getFile().getPath().length());
125 if (relativePath.startsWith(File.separator) || relativePath.startsWith("/")) {
126 relativePath = relativePath.substring(1);
127 }
128 message.setHeader(FileComponent.HEADER_FILE_NAME, relativePath);
129 }
130
131 public File getFile() {
132 ObjectHelper.notNull(file, "file");
133 if (autoCreate && !file.exists()) {
134 file.mkdirs();
135 }
136 return file;
137 }
138
139 public void setFile(File file) {
140 this.file = file;
141 }
142
143 public boolean isSingleton() {
144 return true;
145 }
146
147 public boolean isAutoCreate() {
148 return this.autoCreate;
149 }
150
151 public void setAutoCreate(boolean autoCreate) {
152 this.autoCreate = autoCreate;
153 }
154
155 public FileProcessStrategy getFileStrategy() {
156 if (fileProcessStrategy == null) {
157 fileProcessStrategy = createFileStrategy();
158 LOG.debug("Using file process strategy: " + fileProcessStrategy);
159 }
160 return fileProcessStrategy;
161 }
162
163 /**
164 * Sets the strategy to be used when the file has been processed such as
165 * deleting or renaming it etc.
166 *
167 * @param fileProcessStrategy the new strategy to use
168 */
169 public void setFileStrategy(FileProcessStrategy fileProcessStrategy) {
170 this.fileProcessStrategy = fileProcessStrategy;
171 }
172
173 public boolean isDelete() {
174 return delete;
175 }
176
177 public void setDelete(boolean delete) {
178 this.delete = delete;
179 }
180
181 public boolean isLock() {
182 return lock;
183 }
184
185 public void setLock(boolean lock) {
186 this.lock = lock;
187 }
188
189 public String getMoveNamePostfix() {
190 return moveNamePostfix;
191 }
192
193 /**
194 * Sets the name postfix appended to moved files. For example to rename all
195 * the files from <tt>*</tt> to <tt>*.done</tt> set this value to <tt>.done</tt>
196 */
197 public void setMoveNamePostfix(String moveNamePostfix) {
198 this.moveNamePostfix = moveNamePostfix;
199 }
200
201 public String getMoveNamePrefix() {
202 return moveNamePrefix;
203 }
204
205 /**
206 * Sets the name prefix appended to moved files. For example to move
207 * processed files into a hidden directory called <tt>.camel</tt> set this value to
208 * <tt>.camel/</tt>
209 */
210 public void setMoveNamePrefix(String moveNamePrefix) {
211 this.moveNamePrefix = moveNamePrefix;
212 }
213
214 public String[] getExcludedNamePrefixes() {
215 return excludedNamePrefixes;
216 }
217
218 /**
219 * Sets the excluded file name prefixes, such as <tt>"."</tt> for hidden files which
220 * are excluded by default
221 */
222 public void setExcludedNamePrefixes(String[] excludedNamePrefixes) {
223 this.excludedNamePrefixes = excludedNamePrefixes;
224 }
225
226 public String[] getExcludedNamePostfixes() {
227 return excludedNamePostfixes;
228 }
229
230 /**
231 * Sets the excluded file name postfixes, such as {@link FileProcessStrategySupport#DEFAULT_LOCK_FILE_POSTFIX}
232 * to ignore lock files by default.
233 */
234 public void setExcludedNamePostfixes(String[] excludedNamePostfixes) {
235 this.excludedNamePostfixes = excludedNamePostfixes;
236 }
237
238 public boolean isNoop() {
239 return noop;
240 }
241
242 /**
243 * If set to true then the default {@link FileProcessStrategy} will be to use the
244 * {@link org.apache.camel.component.file.strategy.NoOpFileProcessStrategy NoOpFileProcessStrategy}
245 * to not move or copy processed files
246 */
247 public void setNoop(boolean noop) {
248 this.noop = noop;
249 }
250
251 public boolean isAppend() {
252 return append;
253 }
254
255 /**
256 * When writing do we append to the end of the file, or replace it?
257 * The default is to append
258 */
259 public void setAppend(boolean append) {
260 this.append = append;
261 }
262
263 public int getBufferSize() {
264 return bufferSize;
265 }
266
267 /**
268 * Sets the buffer size used to read/write files
269 */
270 public void setBufferSize(int bufferSize) {
271 this.bufferSize = bufferSize;
272 }
273
274 public boolean isIgnoreFileNameHeader() {
275 return ignoreFileNameHeader;
276 }
277
278 /**
279 * If this flag is enabled then producers will ignore the {@link FileComponent#HEADER_FILE_NAME}
280 * header and generate a new dynamic file
281 */
282 public void setIgnoreFileNameHeader(boolean ignoreFileNameHeader) {
283 this.ignoreFileNameHeader = ignoreFileNameHeader;
284 }
285
286 /**
287 * A strategy method to lazily create the file strategy
288 */
289 protected FileProcessStrategy createFileStrategy() {
290 Class<?> factory = null;
291 try {
292 FactoryFinder finder = new FactoryFinder("META-INF/services/org/apache/camel/component/");
293 factory = finder.findClass("file", "strategy.factory.");
294 } catch (ClassNotFoundException e) {
295 LOG.debug("'strategy.factory.class' not found", e);
296 } catch (IOException e) {
297 LOG.debug("No strategy factory defined in 'META-INF/services/org/apache/camel/component/file'", e);
298 }
299
300 if (factory == null) {
301 // use default
302 factory = ObjectHelper.loadClass(DEFAULT_STRATEGYFACTORY_CLASS);
303 if (factory == null) {
304 throw new TypeNotPresentException("FileProcessStrategyFactory class not found", null);
305 }
306 }
307
308 try {
309 Method factoryMethod = factory.getMethod("createFileProcessStrategy", Properties.class);
310 return (FileProcessStrategy) ObjectHelper.invokeMethod(factoryMethod, null, getParamsAsProperties());
311 } catch (NoSuchMethodException e) {
312 throw new TypeNotPresentException(factory.getSimpleName()
313 + ".createFileProcessStrategy(Properties params) method not found", e);
314 }
315 }
316
317 protected Properties getParamsAsProperties() {
318 Properties params = new Properties();
319 if (isNoop()) {
320 params.setProperty("noop", Boolean.toString(true));
321 }
322 if (isDelete()) {
323 params.setProperty("delete", Boolean.toString(true));
324 }
325 if (isAppend()) {
326 params.setProperty("append", Boolean.toString(true));
327 }
328 if (isLock()) {
329 params.setProperty("lock", Boolean.toString(true));
330 }
331 if (moveNamePrefix != null) {
332 params.setProperty("moveNamePrefix", moveNamePrefix);
333 }
334 if (moveNamePostfix != null) {
335 params.setProperty("moveNamePostfix", moveNamePostfix);
336 }
337 return params;
338 }
339
340 @Override
341 protected String createEndpointUri() {
342 return "file://" + getFile().getAbsolutePath();
343 }
344
345 protected String getFileFriendlyMessageId(String id) {
346 return UuidGenerator.generateSanitizedId(id);
347 }
348 }