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.IOException; 020import java.lang.reflect.Method; 021import java.util.ArrayList; 022import java.util.Comparator; 023import java.util.HashMap; 024import java.util.List; 025import java.util.Map; 026 027import org.apache.camel.CamelContext; 028import org.apache.camel.Component; 029import org.apache.camel.Exchange; 030import org.apache.camel.Expression; 031import org.apache.camel.ExpressionIllegalSyntaxException; 032import org.apache.camel.LoggingLevel; 033import org.apache.camel.Message; 034import org.apache.camel.Processor; 035import org.apache.camel.impl.ScheduledPollEndpoint; 036import org.apache.camel.processor.idempotent.MemoryIdempotentRepository; 037import org.apache.camel.spi.BrowsableEndpoint; 038import org.apache.camel.spi.FactoryFinder; 039import org.apache.camel.spi.IdempotentRepository; 040import org.apache.camel.spi.Language; 041import org.apache.camel.spi.UriParam; 042import org.apache.camel.util.FileUtil; 043import org.apache.camel.util.IOHelper; 044import org.apache.camel.util.ObjectHelper; 045import org.apache.camel.util.ServiceHelper; 046import org.apache.camel.util.StringHelper; 047import org.slf4j.Logger; 048import org.slf4j.LoggerFactory; 049 050/** 051 * Base class for file endpoints 052 */ 053public abstract class GenericFileEndpoint<T> extends ScheduledPollEndpoint implements BrowsableEndpoint { 054 055 protected static final String DEFAULT_STRATEGYFACTORY_CLASS = "org.apache.camel.component.file.strategy.GenericFileProcessStrategyFactory"; 056 protected static final int DEFAULT_IDEMPOTENT_CACHE_SIZE = 1000; 057 058 protected final Logger log = LoggerFactory.getLogger(getClass()); 059 060 protected GenericFileConfiguration configuration; 061 062 @UriParam 063 protected GenericFileProcessStrategy<T> processStrategy; 064 @UriParam 065 protected IdempotentRepository<String> inProgressRepository = new MemoryIdempotentRepository(); 066 @UriParam 067 protected String localWorkDirectory; 068 @UriParam 069 protected boolean autoCreate = true; 070 @UriParam 071 protected boolean startingDirectoryMustExist; 072 @UriParam 073 protected boolean directoryMustExist; 074 @UriParam 075 protected int bufferSize = FileUtil.BUFFER_SIZE; 076 @UriParam 077 protected GenericFileExist fileExist = GenericFileExist.Override; 078 @UriParam 079 protected boolean noop; 080 @UriParam 081 protected boolean recursive; 082 @UriParam 083 protected boolean delete; 084 @UriParam 085 protected boolean flatten; 086 @UriParam 087 protected int maxMessagesPerPoll; 088 @UriParam 089 protected boolean eagerMaxMessagesPerPoll = true; 090 @UriParam 091 protected int maxDepth = Integer.MAX_VALUE; 092 @UriParam 093 protected int minDepth; 094 @UriParam 095 protected String tempPrefix; 096 @UriParam 097 protected Expression tempFileName; 098 @UriParam 099 protected boolean eagerDeleteTargetFile = true; 100 @UriParam 101 protected String include; 102 @UriParam 103 protected String exclude; 104 @UriParam 105 protected String charset; 106 @UriParam 107 protected Expression fileName; 108 @UriParam 109 protected Expression move; 110 @UriParam 111 protected Expression moveFailed; 112 @UriParam 113 protected Expression preMove; 114 @UriParam 115 protected Expression moveExisting; 116 @UriParam 117 protected Boolean idempotent; 118 @UriParam 119 protected Expression idempotentKey; 120 @UriParam 121 protected IdempotentRepository<String> idempotentRepository; 122 @UriParam 123 protected GenericFileFilter<T> filter; 124 @UriParam 125 protected AntPathMatcherGenericFileFilter<T> antFilter; 126 @UriParam 127 protected Comparator<GenericFile<T>> sorter; 128 @UriParam 129 protected Comparator<Exchange> sortBy; 130 @UriParam 131 protected String readLock = "none"; 132 @UriParam 133 protected long readLockCheckInterval = 1000; 134 @UriParam 135 protected long readLockTimeout = 10000; 136 @UriParam 137 protected boolean readLockMarkerFile = true; 138 @UriParam 139 protected LoggingLevel readLockLoggingLevel = LoggingLevel.WARN; 140 @UriParam 141 protected long readLockMinLength = 1; 142 @UriParam 143 protected GenericFileExclusiveReadLockStrategy<T> exclusiveReadLockStrategy; 144 @UriParam 145 protected boolean keepLastModified; 146 @UriParam 147 protected String doneFileName; 148 @UriParam 149 protected boolean allowNullBody; 150 151 public GenericFileEndpoint() { 152 } 153 154 public GenericFileEndpoint(String endpointUri, Component component) { 155 super(endpointUri, component); 156 } 157 158 public boolean isSingleton() { 159 return true; 160 } 161 162 public abstract GenericFileConsumer<T> createConsumer(Processor processor) throws Exception; 163 164 public abstract GenericFileProducer<T> createProducer() throws Exception; 165 166 public abstract Exchange createExchange(GenericFile<T> file); 167 168 public abstract String getScheme(); 169 170 public abstract char getFileSeparator(); 171 172 public abstract boolean isAbsolute(String name); 173 174 /** 175 * Return the file name that will be auto-generated for the given message if 176 * none is provided 177 */ 178 public String getGeneratedFileName(Message message) { 179 return StringHelper.sanitize(message.getMessageId()); 180 } 181 182 public GenericFileProcessStrategy<T> getGenericFileProcessStrategy() { 183 if (processStrategy == null) { 184 processStrategy = createGenericFileStrategy(); 185 log.debug("Using Generic file process strategy: {}", processStrategy); 186 } 187 return processStrategy; 188 } 189 190 /** 191 * This implementation will <b>not</b> load the file content. 192 * Any file locking is neither in use by this implementation.. 193 */ 194 @Override 195 public List<Exchange> getExchanges() { 196 final List<Exchange> answer = new ArrayList<Exchange>(); 197 198 GenericFileConsumer<?> consumer = null; 199 try { 200 // create a new consumer which can poll the exchanges we want to browse 201 // do not provide a processor as we do some custom processing 202 consumer = createConsumer(null); 203 consumer.setCustomProcessor(new Processor() { 204 @Override 205 public void process(Exchange exchange) throws Exception { 206 answer.add(exchange); 207 } 208 }); 209 // do not start scheduler, as we invoke the poll manually 210 consumer.setStartScheduler(false); 211 // start consumer 212 ServiceHelper.startService(consumer); 213 // invoke poll which performs the custom processing, so we can browse the exchanges 214 consumer.poll(); 215 } catch (Exception e) { 216 throw ObjectHelper.wrapRuntimeCamelException(e); 217 } finally { 218 try { 219 ServiceHelper.stopService(consumer); 220 } catch (Exception e) { 221 log.debug("Error stopping consumer used for browsing exchanges. This exception will be ignored", e); 222 } 223 } 224 225 return answer; 226 } 227 228 /** 229 * A strategy method to lazily create the file strategy 230 */ 231 @SuppressWarnings("unchecked") 232 protected GenericFileProcessStrategy<T> createGenericFileStrategy() { 233 Class<?> factory = null; 234 try { 235 FactoryFinder finder = getCamelContext().getFactoryFinder("META-INF/services/org/apache/camel/component/"); 236 log.trace("Using FactoryFinder: {}", finder); 237 factory = finder.findClass(getScheme(), "strategy.factory.", CamelContext.class); 238 } catch (ClassNotFoundException e) { 239 log.trace("'strategy.factory.class' not found", e); 240 } catch (IOException e) { 241 log.trace("No strategy factory defined in 'META-INF/services/org/apache/camel/component/'", e); 242 } 243 244 if (factory == null) { 245 // use default 246 try { 247 log.trace("Using ClassResolver to resolve class: {}", DEFAULT_STRATEGYFACTORY_CLASS); 248 factory = this.getCamelContext().getClassResolver().resolveClass(DEFAULT_STRATEGYFACTORY_CLASS); 249 } catch (Exception e) { 250 log.trace("Cannot load class: {}", DEFAULT_STRATEGYFACTORY_CLASS, e); 251 } 252 // fallback and us this class loader 253 try { 254 if (log.isTraceEnabled()) { 255 log.trace("Using classloader: {} to resolve class: {}", this.getClass().getClassLoader(), DEFAULT_STRATEGYFACTORY_CLASS); 256 } 257 factory = this.getCamelContext().getClassResolver().resolveClass(DEFAULT_STRATEGYFACTORY_CLASS, this.getClass().getClassLoader()); 258 } catch (Exception e) { 259 if (log.isTraceEnabled()) { 260 log.trace("Cannot load class: {} using classloader: " + this.getClass().getClassLoader(), DEFAULT_STRATEGYFACTORY_CLASS, e); 261 } 262 } 263 264 if (factory == null) { 265 throw new TypeNotPresentException(DEFAULT_STRATEGYFACTORY_CLASS + " class not found", null); 266 } 267 } 268 269 try { 270 Method factoryMethod = factory.getMethod("createGenericFileProcessStrategy", CamelContext.class, Map.class); 271 Map<String, Object> params = getParamsAsMap(); 272 log.debug("Parameters for Generic file process strategy {}", params); 273 return (GenericFileProcessStrategy<T>) ObjectHelper.invokeMethod(factoryMethod, null, getCamelContext(), params); 274 } catch (NoSuchMethodException e) { 275 throw new TypeNotPresentException(factory.getSimpleName() + ".createGenericFileProcessStrategy method not found", e); 276 } 277 } 278 279 public boolean isNoop() { 280 return noop; 281 } 282 283 public void setNoop(boolean noop) { 284 this.noop = noop; 285 } 286 287 public boolean isRecursive() { 288 return recursive; 289 } 290 291 public void setRecursive(boolean recursive) { 292 this.recursive = recursive; 293 } 294 295 public String getInclude() { 296 return include; 297 } 298 299 public void setInclude(String include) { 300 this.include = include; 301 } 302 303 public String getExclude() { 304 return exclude; 305 } 306 307 public void setExclude(String exclude) { 308 this.exclude = exclude; 309 } 310 311 public void setAntInclude(String antInclude) { 312 if (this.antFilter == null) { 313 this.antFilter = new AntPathMatcherGenericFileFilter<T>(); 314 } 315 this.antFilter.setIncludes(antInclude); 316 } 317 318 public void setAntExclude(String antExclude) { 319 if (this.antFilter == null) { 320 this.antFilter = new AntPathMatcherGenericFileFilter<T>(); 321 } 322 this.antFilter.setExcludes(antExclude); 323 } 324 325 /** 326 * Sets case sensitive flag on {@link org.apache.camel.component.file.AntPathMatcherFileFilter} 327 * <p/> 328 * Is by default turned on <tt>true</tt>. 329 */ 330 public void setAntFilterCaseSensitive(boolean antFilterCaseSensitive) { 331 if (this.antFilter == null) { 332 this.antFilter = new AntPathMatcherGenericFileFilter<T>(); 333 } 334 this.antFilter.setCaseSensitive(antFilterCaseSensitive); 335 } 336 337 public GenericFileFilter<T> getAntFilter() { 338 return antFilter; 339 } 340 341 public boolean isDelete() { 342 return delete; 343 } 344 345 public void setDelete(boolean delete) { 346 this.delete = delete; 347 } 348 349 public boolean isFlatten() { 350 return flatten; 351 } 352 353 public void setFlatten(boolean flatten) { 354 this.flatten = flatten; 355 } 356 357 public Expression getMove() { 358 return move; 359 } 360 361 public void setMove(Expression move) { 362 this.move = move; 363 } 364 365 /** 366 * Sets the move failure expression based on 367 * {@link org.apache.camel.language.simple.SimpleLanguage} 368 */ 369 public void setMoveFailed(String fileLanguageExpression) { 370 String expression = configureMoveOrPreMoveExpression(fileLanguageExpression); 371 this.moveFailed = createFileLanguageExpression(expression); 372 } 373 374 public Expression getMoveFailed() { 375 return moveFailed; 376 } 377 378 public void setMoveFailed(Expression moveFailed) { 379 this.moveFailed = moveFailed; 380 } 381 382 /** 383 * Sets the move expression based on 384 * {@link org.apache.camel.language.simple.SimpleLanguage} 385 */ 386 public void setMove(String fileLanguageExpression) { 387 String expression = configureMoveOrPreMoveExpression(fileLanguageExpression); 388 this.move = createFileLanguageExpression(expression); 389 } 390 391 public Expression getPreMove() { 392 return preMove; 393 } 394 395 public void setPreMove(Expression preMove) { 396 this.preMove = preMove; 397 } 398 399 /** 400 * Sets the pre move expression based on 401 * {@link org.apache.camel.language.simple.SimpleLanguage} 402 */ 403 public void setPreMove(String fileLanguageExpression) { 404 String expression = configureMoveOrPreMoveExpression(fileLanguageExpression); 405 this.preMove = createFileLanguageExpression(expression); 406 } 407 408 public Expression getMoveExisting() { 409 return moveExisting; 410 } 411 412 public void setMoveExisting(Expression moveExisting) { 413 this.moveExisting = moveExisting; 414 } 415 416 /** 417 * Sets the move existing expression based on 418 * {@link org.apache.camel.language.simple.SimpleLanguage} 419 */ 420 public void setMoveExisting(String fileLanguageExpression) { 421 String expression = configureMoveOrPreMoveExpression(fileLanguageExpression); 422 this.moveExisting = createFileLanguageExpression(expression); 423 } 424 425 public Expression getFileName() { 426 return fileName; 427 } 428 429 public void setFileName(Expression fileName) { 430 this.fileName = fileName; 431 } 432 433 /** 434 * Sets the file expression based on 435 * {@link org.apache.camel.language.simple.SimpleLanguage} 436 */ 437 public void setFileName(String fileLanguageExpression) { 438 this.fileName = createFileLanguageExpression(fileLanguageExpression); 439 } 440 441 public String getDoneFileName() { 442 return doneFileName; 443 } 444 445 /** 446 * Sets the done file name. 447 * <p/> 448 * Only ${file.name} and ${file.name.noext} is supported as dynamic placeholders. 449 */ 450 public void setDoneFileName(String doneFileName) { 451 this.doneFileName = doneFileName; 452 } 453 454 public Boolean isIdempotent() { 455 return idempotent != null ? idempotent : false; 456 } 457 458 public String getCharset() { 459 return charset; 460 } 461 462 public void setCharset(String charset) { 463 IOHelper.validateCharset(charset); 464 this.charset = charset; 465 } 466 467 protected boolean isIdempotentSet() { 468 return idempotent != null; 469 } 470 471 public void setIdempotent(Boolean idempotent) { 472 this.idempotent = idempotent; 473 } 474 475 public Expression getIdempotentKey() { 476 return idempotentKey; 477 } 478 479 public void setIdempotentKey(Expression idempotentKey) { 480 this.idempotentKey = idempotentKey; 481 } 482 483 public void setIdempotentKey(String expression) { 484 this.idempotentKey = createFileLanguageExpression(expression); 485 } 486 487 public IdempotentRepository<String> getIdempotentRepository() { 488 return idempotentRepository; 489 } 490 491 public void setIdempotentRepository(IdempotentRepository<String> idempotentRepository) { 492 this.idempotentRepository = idempotentRepository; 493 } 494 495 public GenericFileFilter<T> getFilter() { 496 return filter; 497 } 498 499 public void setFilter(GenericFileFilter<T> filter) { 500 this.filter = filter; 501 } 502 503 public Comparator<GenericFile<T>> getSorter() { 504 return sorter; 505 } 506 507 public void setSorter(Comparator<GenericFile<T>> sorter) { 508 this.sorter = sorter; 509 } 510 511 public Comparator<Exchange> getSortBy() { 512 return sortBy; 513 } 514 515 public void setSortBy(Comparator<Exchange> sortBy) { 516 this.sortBy = sortBy; 517 } 518 519 public void setSortBy(String expression) { 520 setSortBy(expression, false); 521 } 522 523 public void setSortBy(String expression, boolean reverse) { 524 setSortBy(GenericFileDefaultSorter.sortByFileLanguage(getCamelContext(), expression, reverse)); 525 } 526 527 public String getTempPrefix() { 528 return tempPrefix; 529 } 530 531 /** 532 * Enables and uses temporary prefix when writing files, after write it will 533 * be renamed to the correct name. 534 */ 535 public void setTempPrefix(String tempPrefix) { 536 this.tempPrefix = tempPrefix; 537 // use only name as we set a prefix in from on the name 538 setTempFileName(tempPrefix + "${file:onlyname}"); 539 } 540 541 public Expression getTempFileName() { 542 return tempFileName; 543 } 544 545 public void setTempFileName(Expression tempFileName) { 546 this.tempFileName = tempFileName; 547 } 548 549 public void setTempFileName(String tempFileNameExpression) { 550 this.tempFileName = createFileLanguageExpression(tempFileNameExpression); 551 } 552 553 public boolean isEagerDeleteTargetFile() { 554 return eagerDeleteTargetFile; 555 } 556 557 public void setEagerDeleteTargetFile(boolean eagerDeleteTargetFile) { 558 this.eagerDeleteTargetFile = eagerDeleteTargetFile; 559 } 560 561 public GenericFileConfiguration getConfiguration() { 562 if (configuration == null) { 563 configuration = new GenericFileConfiguration(); 564 } 565 return configuration; 566 } 567 568 public void setConfiguration(GenericFileConfiguration configuration) { 569 this.configuration = configuration; 570 } 571 572 public GenericFileExclusiveReadLockStrategy<T> getExclusiveReadLockStrategy() { 573 return exclusiveReadLockStrategy; 574 } 575 576 public void setExclusiveReadLockStrategy(GenericFileExclusiveReadLockStrategy<T> exclusiveReadLockStrategy) { 577 this.exclusiveReadLockStrategy = exclusiveReadLockStrategy; 578 } 579 580 public String getReadLock() { 581 return readLock; 582 } 583 584 public void setReadLock(String readLock) { 585 this.readLock = readLock; 586 } 587 588 public long getReadLockCheckInterval() { 589 return readLockCheckInterval; 590 } 591 592 public void setReadLockCheckInterval(long readLockCheckInterval) { 593 this.readLockCheckInterval = readLockCheckInterval; 594 } 595 596 public long getReadLockTimeout() { 597 return readLockTimeout; 598 } 599 600 public void setReadLockTimeout(long readLockTimeout) { 601 this.readLockTimeout = readLockTimeout; 602 } 603 604 public boolean isReadLockMarkerFile() { 605 return readLockMarkerFile; 606 } 607 608 public void setReadLockMarkerFile(boolean readLockMarkerFile) { 609 this.readLockMarkerFile = readLockMarkerFile; 610 } 611 612 public LoggingLevel getReadLockLoggingLevel() { 613 return readLockLoggingLevel; 614 } 615 616 public void setReadLockLoggingLevel(LoggingLevel readLockLoggingLevel) { 617 this.readLockLoggingLevel = readLockLoggingLevel; 618 } 619 620 public long getReadLockMinLength() { 621 return readLockMinLength; 622 } 623 624 public void setReadLockMinLength(long readLockMinLength) { 625 this.readLockMinLength = readLockMinLength; 626 } 627 628 public int getBufferSize() { 629 return bufferSize; 630 } 631 632 public void setBufferSize(int bufferSize) { 633 if (bufferSize <= 0) { 634 throw new IllegalArgumentException("BufferSize must be a positive value, was " + bufferSize); 635 } 636 this.bufferSize = bufferSize; 637 } 638 639 public GenericFileExist getFileExist() { 640 return fileExist; 641 } 642 643 public void setFileExist(GenericFileExist fileExist) { 644 this.fileExist = fileExist; 645 } 646 647 public boolean isAutoCreate() { 648 return autoCreate; 649 } 650 651 public void setAutoCreate(boolean autoCreate) { 652 this.autoCreate = autoCreate; 653 } 654 655 public boolean isStartingDirectoryMustExist() { 656 return startingDirectoryMustExist; 657 } 658 659 public void setStartingDirectoryMustExist(boolean startingDirectoryMustExist) { 660 this.startingDirectoryMustExist = startingDirectoryMustExist; 661 } 662 663 public boolean isDirectoryMustExist() { 664 return directoryMustExist; 665 } 666 667 public void setDirectoryMustExist(boolean directoryMustExist) { 668 this.directoryMustExist = directoryMustExist; 669 } 670 671 public GenericFileProcessStrategy<T> getProcessStrategy() { 672 return processStrategy; 673 } 674 675 public void setProcessStrategy(GenericFileProcessStrategy<T> processStrategy) { 676 this.processStrategy = processStrategy; 677 } 678 679 public String getLocalWorkDirectory() { 680 return localWorkDirectory; 681 } 682 683 public void setLocalWorkDirectory(String localWorkDirectory) { 684 this.localWorkDirectory = localWorkDirectory; 685 } 686 687 public int getMaxMessagesPerPoll() { 688 return maxMessagesPerPoll; 689 } 690 691 public void setMaxMessagesPerPoll(int maxMessagesPerPoll) { 692 this.maxMessagesPerPoll = maxMessagesPerPoll; 693 } 694 695 public boolean isEagerMaxMessagesPerPoll() { 696 return eagerMaxMessagesPerPoll; 697 } 698 699 public void setEagerMaxMessagesPerPoll(boolean eagerMaxMessagesPerPoll) { 700 this.eagerMaxMessagesPerPoll = eagerMaxMessagesPerPoll; 701 } 702 703 public int getMaxDepth() { 704 return maxDepth; 705 } 706 707 public void setMaxDepth(int maxDepth) { 708 this.maxDepth = maxDepth; 709 } 710 711 public int getMinDepth() { 712 return minDepth; 713 } 714 715 public void setMinDepth(int minDepth) { 716 this.minDepth = minDepth; 717 } 718 719 public IdempotentRepository<String> getInProgressRepository() { 720 return inProgressRepository; 721 } 722 723 public void setInProgressRepository(IdempotentRepository<String> inProgressRepository) { 724 this.inProgressRepository = inProgressRepository; 725 } 726 727 public boolean isKeepLastModified() { 728 return keepLastModified; 729 } 730 731 public void setKeepLastModified(boolean keepLastModified) { 732 this.keepLastModified = keepLastModified; 733 } 734 735 public boolean isAllowNullBody() { 736 return allowNullBody; 737 } 738 739 public void setAllowNullBody(boolean allowNullBody) { 740 this.allowNullBody = allowNullBody; 741 } 742 743 /** 744 * Configures the given message with the file which sets the body to the 745 * file object. 746 */ 747 public void configureMessage(GenericFile<T> file, Message message) { 748 message.setBody(file); 749 750 if (flatten) { 751 // when flatten the file name should not contain any paths 752 message.setHeader(Exchange.FILE_NAME, file.getFileNameOnly()); 753 } else { 754 // compute name to set on header that should be relative to starting directory 755 String name = file.isAbsolute() ? file.getAbsoluteFilePath() : file.getRelativeFilePath(); 756 757 // skip leading endpoint configured directory 758 String endpointPath = getConfiguration().getDirectory() + getFileSeparator(); 759 760 // need to normalize paths to ensure we can match using startsWith 761 endpointPath = FileUtil.normalizePath(endpointPath); 762 String copyOfName = FileUtil.normalizePath(name); 763 if (ObjectHelper.isNotEmpty(endpointPath) && copyOfName.startsWith(endpointPath)) { 764 name = name.substring(endpointPath.length()); 765 } 766 767 // adjust filename 768 message.setHeader(Exchange.FILE_NAME, name); 769 } 770 } 771 772 /** 773 * Set up the exchange properties with the options of the file endpoint 774 */ 775 public void configureExchange(Exchange exchange) { 776 // Now we just set the charset property here 777 if (getCharset() != null) { 778 exchange.setProperty(Exchange.CHARSET_NAME, getCharset()); 779 } 780 } 781 782 /** 783 * Strategy to configure the move, preMove, or moveExisting option based on a String input. 784 * 785 * @param expression the original string input 786 * @return configured string or the original if no modifications is needed 787 */ 788 protected String configureMoveOrPreMoveExpression(String expression) { 789 // if the expression already have ${ } placeholders then pass it unmodified 790 if (StringHelper.hasStartToken(expression, "simple")) { 791 return expression; 792 } 793 794 // remove trailing slash 795 expression = FileUtil.stripTrailingSeparator(expression); 796 797 StringBuilder sb = new StringBuilder(); 798 799 // if relative then insert start with the parent folder 800 if (!isAbsolute(expression)) { 801 sb.append("${file:parent}"); 802 sb.append(getFileSeparator()); 803 } 804 // insert the directory the end user provided 805 sb.append(expression); 806 // append only the filename (file:name can contain a relative path, so we must use onlyname) 807 sb.append(getFileSeparator()); 808 sb.append("${file:onlyname}"); 809 810 return sb.toString(); 811 } 812 813 protected Map<String, Object> getParamsAsMap() { 814 Map<String, Object> params = new HashMap<String, Object>(); 815 816 if (isNoop()) { 817 params.put("noop", Boolean.toString(true)); 818 } 819 if (isDelete()) { 820 params.put("delete", Boolean.toString(true)); 821 } 822 if (move != null) { 823 params.put("move", move); 824 } 825 if (moveFailed != null) { 826 params.put("moveFailed", moveFailed); 827 } 828 if (preMove != null) { 829 params.put("preMove", preMove); 830 } 831 if (exclusiveReadLockStrategy != null) { 832 params.put("exclusiveReadLockStrategy", exclusiveReadLockStrategy); 833 } 834 if (readLock != null) { 835 params.put("readLock", readLock); 836 } 837 if (readLockCheckInterval > 0) { 838 params.put("readLockCheckInterval", readLockCheckInterval); 839 } 840 if (readLockTimeout > 0) { 841 params.put("readLockTimeout", readLockTimeout); 842 } 843 params.put("readLockMarkerFile", readLockMarkerFile); 844 params.put("readLockMinLength", readLockMinLength); 845 params.put("readLockLoggingLevel", readLockLoggingLevel); 846 847 return params; 848 } 849 850 private Expression createFileLanguageExpression(String expression) { 851 Language language; 852 // only use file language if the name is complex (eg. using $) 853 if (expression.contains("$")) { 854 language = getCamelContext().resolveLanguage("file"); 855 } else { 856 language = getCamelContext().resolveLanguage("constant"); 857 } 858 return language.createExpression(expression); 859 } 860 861 /** 862 * Creates the associated name of the done file based on the given file name. 863 * <p/> 864 * This method should only be invoked if a done filename property has been set on this endpoint. 865 * 866 * @param fileName the file name 867 * @return name of the associated done file name 868 */ 869 protected String createDoneFileName(String fileName) { 870 String pattern = getDoneFileName(); 871 ObjectHelper.notEmpty(pattern, "doneFileName", pattern); 872 873 // we only support ${file:name} or ${file:name.noext} as dynamic placeholders for done files 874 String path = FileUtil.onlyPath(fileName); 875 String onlyName = FileUtil.stripPath(fileName); 876 877 pattern = pattern.replaceFirst("\\$\\{file:name\\}", onlyName); 878 pattern = pattern.replaceFirst("\\$simple\\{file:name\\}", onlyName); 879 pattern = pattern.replaceFirst("\\$\\{file:name.noext\\}", FileUtil.stripExt(onlyName)); 880 pattern = pattern.replaceFirst("\\$simple\\{file:name.noext\\}", FileUtil.stripExt(onlyName)); 881 882 // must be able to resolve all placeholders supported 883 if (StringHelper.hasStartToken(pattern, "simple")) { 884 throw new ExpressionIllegalSyntaxException(fileName + ". Cannot resolve reminder: " + pattern); 885 } 886 887 String answer = pattern; 888 if (ObjectHelper.isNotEmpty(path) && ObjectHelper.isNotEmpty(pattern)) { 889 // done file must always be in same directory as the real file name 890 answer = path + getFileSeparator() + pattern; 891 } 892 893 if (getConfiguration().needToNormalize()) { 894 // must normalize path to cater for Windows and other OS 895 answer = FileUtil.normalizePath(answer); 896 } 897 898 return answer; 899 } 900 901 /** 902 * Is the given file a done file? 903 * <p/> 904 * This method should only be invoked if a done filename property has been set on this endpoint. 905 * 906 * @param fileName the file name 907 * @return <tt>true</tt> if its a done file, <tt>false</tt> otherwise 908 */ 909 protected boolean isDoneFile(String fileName) { 910 String pattern = getDoneFileName(); 911 ObjectHelper.notEmpty(pattern, "doneFileName", pattern); 912 913 if (!StringHelper.hasStartToken(pattern, "simple")) { 914 // no tokens, so just match names directly 915 return pattern.equals(fileName); 916 } 917 918 // the static part of the pattern, is that a prefix or suffix? 919 // its a prefix if ${ start token is not at the start of the pattern 920 boolean prefix = pattern.indexOf("${") > 0; 921 922 // remove dynamic parts of the pattern so we only got the static part left 923 pattern = pattern.replaceFirst("\\$\\{file:name\\}", ""); 924 pattern = pattern.replaceFirst("\\$simple\\{file:name\\}", ""); 925 pattern = pattern.replaceFirst("\\$\\{file:name.noext\\}", ""); 926 pattern = pattern.replaceFirst("\\$simple\\{file:name.noext\\}", ""); 927 928 // must be able to resolve all placeholders supported 929 if (StringHelper.hasStartToken(pattern, "simple")) { 930 throw new ExpressionIllegalSyntaxException(fileName + ". Cannot resolve reminder: " + pattern); 931 } 932 933 if (prefix) { 934 return fileName.startsWith(pattern); 935 } else { 936 return fileName.endsWith(pattern); 937 } 938 } 939 940 @Override 941 protected void doStart() throws Exception { 942 ServiceHelper.startServices(inProgressRepository, idempotentRepository); 943 super.doStart(); 944 } 945 946 @Override 947 protected void doStop() throws Exception { 948 super.doStop(); 949 ServiceHelper.stopServices(inProgressRepository, idempotentRepository); 950 } 951}