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 }