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.Comparator;
023 import java.util.HashMap;
024 import java.util.Map;
025
026 import org.apache.camel.Component;
027 import org.apache.camel.Exchange;
028 import org.apache.camel.Expression;
029 import org.apache.camel.Message;
030 import org.apache.camel.Processor;
031 import org.apache.camel.impl.ScheduledPollEndpoint;
032 import org.apache.camel.language.simple.FileLanguage;
033 import org.apache.camel.spi.IdempotentRepository;
034 import org.apache.camel.util.FactoryFinder;
035 import org.apache.camel.util.FileUtil;
036 import org.apache.camel.util.ObjectHelper;
037 import org.apache.camel.util.UuidGenerator;
038 import org.apache.commons.logging.Log;
039 import org.apache.commons.logging.LogFactory;
040
041 /**
042 * Generic FileEndpoint
043 */
044 public abstract class GenericFileEndpoint<T> extends ScheduledPollEndpoint {
045
046 protected static final transient String DEFAULT_STRATEGYFACTORY_CLASS = "org.apache.camel.component.file.strategy.GenericFileProcessStrategyFactory";
047 protected static final transient int DEFAULT_IDEMPOTENT_CACHE_SIZE = 1000;
048
049 protected final transient Log log = LogFactory.getLog(getClass());
050
051 protected GenericFileProcessStrategy<T> processStrategy;
052 protected GenericFileOperations<T> operations;
053 protected GenericFileConfiguration configuration;
054
055 protected String localWorkDirectory;
056 protected boolean autoCreate = true;
057 protected int bufferSize = 128 * 1024;
058 protected boolean append = true;
059 protected boolean noop;
060 protected boolean recursive;
061 protected boolean delete;
062 protected boolean flattern;
063 protected String tempPrefix;
064 protected String include;
065 protected String exclude;
066 protected Expression fileName;
067 protected Expression move;
068 protected Expression preMove;
069 protected boolean idempotent;
070 protected IdempotentRepository idempotentRepository;
071 protected GenericFileFilter<T> filter;
072 protected Comparator<GenericFile<T>> sorter;
073 protected Comparator<GenericFileExchange> sortBy;
074 protected String readLock = "none";
075 protected long readLockTimeout;
076 protected GenericFileExclusiveReadLockStrategy exclusiveReadLockStrategy;
077
078 public GenericFileEndpoint() {
079 }
080
081 public GenericFileEndpoint(String endpointUri, Component component) {
082 super(endpointUri, component);
083 }
084
085 public boolean isSingleton() {
086 return true;
087 }
088
089 public abstract GenericFileConsumer<T> createConsumer(Processor processor) throws Exception;
090
091 public abstract GenericFileProducer<T> createProducer() throws Exception;
092
093 public abstract GenericFileExchange<T> createExchange(GenericFile<T> file);
094
095 public abstract String getScheme();
096
097 public abstract char getFileSeparator();
098
099 public abstract boolean isAbsolute(String name);
100
101 /**
102 * Return the file name that will be auto-generated for the given message if
103 * none is provided
104 */
105 public String getGeneratedFileName(Message message) {
106 return UuidGenerator.generateSanitizedId(message.getMessageId());
107 }
108
109 public GenericFileProcessStrategy<T> getGenericFileProcessStrategy() {
110 if (processStrategy == null) {
111 processStrategy = createGenericFileStrategy();
112 if (log.isDebugEnabled()) {
113 log.debug("Using Generic file process strategy: " + processStrategy);
114 }
115 }
116 return processStrategy;
117 }
118
119 /**
120 * A strategy method to lazily create the file strategy
121 */
122 @SuppressWarnings("unchecked")
123 protected GenericFileProcessStrategy<T> createGenericFileStrategy() {
124 Class<?> factory = null;
125 try {
126 FactoryFinder finder = getCamelContext().createFactoryFinder("META-INF/services/org/apache/camel/component/");
127 factory = finder.findClass(getScheme(), "strategy.factory.");
128 } catch (ClassNotFoundException e) {
129 log.debug("'strategy.factory.class' not found", e);
130 } catch (IOException e) {
131 log.debug("No strategy factory defined in 'META-INF/services/org/apache/camel/component/'", e);
132 }
133
134 if (factory == null) {
135 // use default
136 factory = this.getCamelContext().getClassResolver().resolveClass(DEFAULT_STRATEGYFACTORY_CLASS);
137 if (factory == null) {
138 throw new TypeNotPresentException(DEFAULT_STRATEGYFACTORY_CLASS + " class not found", null);
139 }
140 }
141
142 try {
143 Method factoryMethod = factory.getMethod("createGenericFileProcessStrategy", Map.class);
144 return (GenericFileProcessStrategy<T>) ObjectHelper.invokeMethod(factoryMethod, null, getParamsAsMap());
145 } catch (NoSuchMethodException e) {
146 throw new TypeNotPresentException(factory.getSimpleName() + ".createGenericFileProcessStrategy(GenericFileEndpoint endpoint) method not found", e);
147 }
148 }
149
150 public void setGenericFileProcessStrategy(GenericFileProcessStrategy<T> genericFileProcessStrategy) {
151 this.processStrategy = genericFileProcessStrategy;
152 }
153
154 public boolean isNoop() {
155 return noop;
156 }
157
158 public void setNoop(boolean noop) {
159 this.noop = noop;
160 }
161
162 public boolean isRecursive() {
163 return recursive;
164 }
165
166 public void setRecursive(boolean recursive) {
167 this.recursive = recursive;
168 }
169
170 public String getInclude() {
171 return include;
172 }
173
174 public void setInclude(String include) {
175 this.include = include;
176 }
177
178 public String getExclude() {
179 return exclude;
180 }
181
182 public void setExclude(String exclude) {
183 this.exclude = exclude;
184 }
185
186 public boolean isDelete() {
187 return delete;
188 }
189
190 public void setDelete(boolean delete) {
191 this.delete = delete;
192 }
193
194 public boolean isFlattern() {
195 return flattern;
196 }
197
198 public void setFlattern(boolean flattern) {
199 this.flattern = flattern;
200 }
201
202 public Expression getMove() {
203 return move;
204 }
205
206 public void setMove(Expression move) {
207 this.move = move;
208 }
209
210 /**
211 * Sets the move expression based on
212 * {@link org.apache.camel.language.simple.FileLanguage}
213 */
214 public void setMove(String fileLanguageExpression) {
215 String expression = configureMoveOrPreMoveExpression(fileLanguageExpression);
216 this.move = FileLanguage.file(expression);
217 }
218
219 public Expression getPreMove() {
220 return preMove;
221 }
222
223 public void setPreMove(Expression preMove) {
224 this.preMove = preMove;
225 }
226
227 /**
228 * Sets the pre move expression based on
229 * {@link org.apache.camel.language.simple.FileLanguage}
230 */
231 public void setPreMove(String fileLanguageExpression) {
232 String expression = configureMoveOrPreMoveExpression(fileLanguageExpression);
233 this.preMove = FileLanguage.file(expression);
234 }
235
236 public Expression getFileName() {
237 return fileName;
238 }
239
240 public void setFileName(Expression fileName) {
241 this.fileName = fileName;
242 }
243
244 /**
245 * Sets the file expression based on
246 * {@link org.apache.camel.language.simple.FileLanguage}
247 */
248 public void setFileName(String fileLanguageExpression) {
249 this.fileName = FileLanguage.file(fileLanguageExpression);
250 }
251
252 public boolean isIdempotent() {
253 return idempotent;
254 }
255
256 public void setIdempotent(boolean idempotent) {
257 this.idempotent = idempotent;
258 }
259
260 public IdempotentRepository getIdempotentRepository() {
261 return idempotentRepository;
262 }
263
264 public void setIdempotentRepository(IdempotentRepository idempotentRepository) {
265 this.idempotentRepository = idempotentRepository;
266 }
267
268 public GenericFileFilter<T> getFilter() {
269 return filter;
270 }
271
272 public void setFilter(GenericFileFilter<T> filter) {
273 this.filter = filter;
274 }
275
276 public Comparator<GenericFile<T>> getSorter() {
277 return sorter;
278 }
279
280 public void setSorter(Comparator<GenericFile<T>> sorter) {
281 this.sorter = sorter;
282 }
283
284 public Comparator<GenericFileExchange> getSortBy() {
285 return sortBy;
286 }
287
288 public void setSortBy(Comparator<GenericFileExchange> sortBy) {
289 this.sortBy = sortBy;
290 }
291
292 public void setSortBy(String expression) {
293 setSortBy(expression, false);
294 }
295
296 public void setSortBy(String expression, boolean reverse) {
297 setSortBy(GenericFileDefaultSorter.sortByFileLanguage(expression, reverse));
298 }
299
300 public String getTempPrefix() {
301 return tempPrefix;
302 }
303
304 /**
305 * Enables and uses temporary prefix when writing files, after write it will
306 * be renamed to the correct name.
307 */
308 public void setTempPrefix(String tempPrefix) {
309 this.tempPrefix = tempPrefix;
310 }
311
312 public GenericFileConfiguration getConfiguration() {
313 if (configuration == null) {
314 configuration = new GenericFileConfiguration();
315 }
316 return configuration;
317 }
318
319 public void setConfiguration(GenericFileConfiguration configuration) {
320 this.configuration = configuration;
321 }
322
323 public GenericFileExclusiveReadLockStrategy getExclusiveReadLockStrategy() {
324 return exclusiveReadLockStrategy;
325 }
326
327 public void setExclusiveReadLockStrategy(GenericFileExclusiveReadLockStrategy exclusiveReadLockStrategy) {
328 this.exclusiveReadLockStrategy = exclusiveReadLockStrategy;
329 }
330
331 public String getReadLock() {
332 return readLock;
333 }
334
335 public void setReadLock(String readLock) {
336 this.readLock = readLock;
337 }
338
339 public long getReadLockTimeout() {
340 return readLockTimeout;
341 }
342
343 public void setReadLockTimeout(long readLockTimeout) {
344 this.readLockTimeout = readLockTimeout;
345 }
346
347 public int getBufferSize() {
348 return bufferSize;
349 }
350
351 public void setBufferSize(int bufferSize) {
352 this.bufferSize = bufferSize;
353 }
354
355 public boolean isAppend() {
356 return append;
357 }
358
359 public void setAppend(boolean append) {
360 this.append = append;
361 }
362
363 public boolean isAutoCreate() {
364 return autoCreate;
365 }
366
367 public void setAutoCreate(boolean autoCreate) {
368 this.autoCreate = autoCreate;
369 }
370
371 public GenericFileOperations<T> getOperations() {
372 return operations;
373 }
374
375 public void setOperations(GenericFileOperations<T> operations) {
376 this.operations = operations;
377 }
378
379 public GenericFileProcessStrategy<T> getProcessStrategy() {
380 return processStrategy;
381 }
382
383 public void setProcessStrategy(GenericFileProcessStrategy<T> processStrategy) {
384 this.processStrategy = processStrategy;
385 }
386
387 public String getLocalWorkDirectory() {
388 return localWorkDirectory;
389 }
390
391 public void setLocalWorkDirectory(String localWorkDirectory) {
392 this.localWorkDirectory = localWorkDirectory;
393 }
394
395 /**
396 * Configures the given message with the file which sets the body to the
397 * file object.
398 */
399 public void configureMessage(GenericFile<T> file, Message message) {
400 message.setBody(file);
401
402 if (flattern) {
403 // when flattern the file name should not contain any paths
404 message.setHeader(Exchange.FILE_NAME, file.getFileNameOnly());
405 } else {
406 // compute name to set on header that should be relative to starting directory
407 String name = file.isAbsolute() ? file.getAbsoluteFilePath() : file.getRelativeFilePath();
408
409 // skip leading endpoint configured directory
410 String endpointPath = getConfiguration().getDirectory();
411 if (ObjectHelper.isNotEmpty(endpointPath) && name.startsWith(endpointPath)) {
412 name = ObjectHelper.after(name, getConfiguration().getDirectory() + File.separator);
413 }
414
415 // adjust filename
416 message.setHeader(Exchange.FILE_NAME, name);
417 }
418 }
419
420 /**
421 * Strategy to configure the move or premove option based on a String input.
422 * <p/>
423 * @param expression the original string input
424 * @return configured string or the original if no modifications is needed
425 */
426 protected String configureMoveOrPreMoveExpression(String expression) {
427 // if the expression already have ${ } placeholders then pass it unmodified
428 if (expression.indexOf("${") != -1) {
429 return expression;
430 }
431
432 // remove trailing slash
433 expression = FileUtil.stripTrailingSeparator(expression);
434
435 StringBuilder sb = new StringBuilder();
436
437 // if relative then insert start with the parent folder
438 if (!isAbsolute(expression)) {
439 sb.append("${file:parent}");
440 sb.append(getFileSeparator());
441 }
442 // insert the directory the end user provided
443 sb.append(expression);
444 // append only the filename (file:name can contain a relative path, so we must use onlyname)
445 sb.append(getFileSeparator());
446 sb.append("${file:onlyname}");
447
448 return sb.toString();
449 }
450
451 protected Map<String, Object> getParamsAsMap() {
452 Map<String, Object> params = new HashMap<String, Object>();
453
454 if (isNoop()) {
455 params.put("noop", Boolean.toString(true));
456 }
457 if (isDelete()) {
458 params.put("delete", Boolean.toString(true));
459 }
460 if (move != null) {
461 params.put("move", move);
462 }
463 if (preMove != null) {
464 params.put("preMove", preMove);
465 }
466 if (exclusiveReadLockStrategy != null) {
467 params.put("exclusiveReadLockStrategy", exclusiveReadLockStrategy);
468 }
469 if (readLock != null) {
470 params.put("readLock", readLock);
471 }
472 if (readLockTimeout > 0) {
473 params.put("readLockTimeout", readLockTimeout);
474 }
475
476 return params;
477 }
478
479 }