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.util.ArrayList; 020import java.util.Collections; 021import java.util.Deque; 022import java.util.LinkedList; 023import java.util.List; 024import java.util.Queue; 025import java.util.regex.Pattern; 026 027import org.apache.camel.AsyncCallback; 028import org.apache.camel.Exchange; 029import org.apache.camel.Message; 030import org.apache.camel.Processor; 031import org.apache.camel.ShutdownRunningTask; 032import org.apache.camel.impl.ScheduledBatchPollingConsumer; 033import org.apache.camel.util.CastUtils; 034import org.apache.camel.util.ObjectHelper; 035import org.apache.camel.util.StopWatch; 036import org.apache.camel.util.StringHelper; 037import org.apache.camel.util.TimeUtils; 038import org.slf4j.Logger; 039import org.slf4j.LoggerFactory; 040 041/** 042 * Base class for file consumers. 043 */ 044public abstract class GenericFileConsumer<T> extends ScheduledBatchPollingConsumer { 045 protected final Logger log = LoggerFactory.getLogger(getClass()); 046 protected GenericFileEndpoint<T> endpoint; 047 protected GenericFileOperations<T> operations; 048 protected String fileExpressionResult; 049 protected volatile ShutdownRunningTask shutdownRunningTask; 050 protected volatile int pendingExchanges; 051 protected Processor customProcessor; 052 protected boolean eagerLimitMaxMessagesPerPoll = true; 053 protected volatile boolean prepareOnStartup; 054 private final Pattern includePattern; 055 private final Pattern excludePattern; 056 057 public GenericFileConsumer(GenericFileEndpoint<T> endpoint, Processor processor, GenericFileOperations<T> operations) { 058 super(endpoint, processor); 059 this.endpoint = endpoint; 060 this.operations = operations; 061 062 this.includePattern = endpoint.getIncludePattern(); 063 this.excludePattern = endpoint.getExcludePattern(); 064 } 065 066 public Processor getCustomProcessor() { 067 return customProcessor; 068 } 069 070 /** 071 * Use a custom processor to process the exchange. 072 * <p/> 073 * Only set this if you need to do custom processing, instead of the regular processing. 074 * <p/> 075 * This is for example used to browse file endpoints by leveraging the file consumer to poll 076 * the directory to gather the list of exchanges. But to avoid processing the files regularly 077 * we can use a custom processor. 078 * 079 * @param processor a custom processor 080 */ 081 public void setCustomProcessor(Processor processor) { 082 this.customProcessor = processor; 083 } 084 085 public boolean isEagerLimitMaxMessagesPerPoll() { 086 return eagerLimitMaxMessagesPerPoll; 087 } 088 089 public void setEagerLimitMaxMessagesPerPoll(boolean eagerLimitMaxMessagesPerPoll) { 090 this.eagerLimitMaxMessagesPerPoll = eagerLimitMaxMessagesPerPoll; 091 } 092 093 /** 094 * Poll for files 095 */ 096 protected int poll() throws Exception { 097 // must prepare on startup the very first time 098 if (!prepareOnStartup) { 099 // prepare on startup 100 endpoint.getGenericFileProcessStrategy().prepareOnStartup(operations, endpoint); 101 prepareOnStartup = true; 102 } 103 104 // must reset for each poll 105 fileExpressionResult = null; 106 shutdownRunningTask = null; 107 pendingExchanges = 0; 108 109 // before we poll is there anything we need to check? 110 // such as are we connected to the FTP Server still? 111 if (!prePollCheck()) { 112 log.debug("Skipping poll as pre poll check returned false"); 113 return 0; 114 } 115 116 // gather list of files to process 117 List<GenericFile<T>> files = new ArrayList<GenericFile<T>>(); 118 String name = endpoint.getConfiguration().getDirectory(); 119 120 // time how long it takes to poll 121 StopWatch stop = new StopWatch(); 122 boolean limitHit; 123 try { 124 limitHit = !pollDirectory(name, files, 0); 125 } catch (Exception e) { 126 // during poll directory we add files to the in progress repository, in case of any exception thrown after this work 127 // we must then drain the in progress files before rethrowing the exception 128 log.debug("Error occurred during poll directory: " + name + " due " + e.getMessage() + ". Removing " + files.size() + " files marked as in-progress."); 129 removeExcessiveInProgressFiles(files); 130 throw e; 131 } 132 133 long delta = stop.stop(); 134 if (log.isDebugEnabled()) { 135 log.debug("Took {} to poll: {}", TimeUtils.printDuration(delta), name); 136 } 137 138 // log if we hit the limit 139 if (limitHit) { 140 log.debug("Limiting maximum messages to poll at {} files as there were more messages in this poll.", maxMessagesPerPoll); 141 } 142 143 // sort files using file comparator if provided 144 if (endpoint.getSorter() != null) { 145 files.sort(endpoint.getSorter()); 146 } 147 148 // sort using build in sorters so we can use expressions 149 // use a linked list so we can dequeue the exchanges 150 LinkedList<Exchange> exchanges = new LinkedList<Exchange>(); 151 for (GenericFile<T> file : files) { 152 Exchange exchange = endpoint.createExchange(file); 153 endpoint.configureExchange(exchange); 154 endpoint.configureMessage(file, exchange.getIn()); 155 exchanges.add(exchange); 156 } 157 // sort files using exchange comparator if provided 158 if (endpoint.getSortBy() != null) { 159 exchanges.sort(endpoint.getSortBy()); 160 } 161 if (endpoint.isShuffle()) { 162 Collections.shuffle(exchanges); 163 } 164 165 // use a queue for the exchanges 166 Deque<Exchange> q = exchanges; 167 168 // we are not eager limiting, but we have configured a limit, so cut the list of files 169 if (!eagerLimitMaxMessagesPerPoll && maxMessagesPerPoll > 0) { 170 if (files.size() > maxMessagesPerPoll) { 171 log.debug("Limiting maximum messages to poll at {} files as there were more messages in this poll.", maxMessagesPerPoll); 172 // must first remove excessive files from the in progress repository 173 removeExcessiveInProgressFiles(q, maxMessagesPerPoll); 174 } 175 } 176 177 // consume files one by one 178 int total = exchanges.size(); 179 if (total > 0) { 180 log.debug("Total {} files to consume", total); 181 } 182 183 int polledMessages = processBatch(CastUtils.cast(q)); 184 185 postPollCheck(polledMessages); 186 187 return polledMessages; 188 } 189 190 public int processBatch(Queue<Object> exchanges) { 191 int total = exchanges.size(); 192 int answer = total; 193 194 // limit if needed 195 if (maxMessagesPerPoll > 0 && total > maxMessagesPerPoll) { 196 log.debug("Limiting to maximum messages to poll {} as there were {} messages in this poll.", maxMessagesPerPoll, total); 197 total = maxMessagesPerPoll; 198 } 199 200 for (int index = 0; index < total && isBatchAllowed(); index++) { 201 // only loop if we are started (allowed to run) 202 // use poll to remove the head so it does not consume memory even after we have processed it 203 Exchange exchange = (Exchange) exchanges.poll(); 204 // add current index and total as properties 205 exchange.setProperty(Exchange.BATCH_INDEX, index); 206 exchange.setProperty(Exchange.BATCH_SIZE, total); 207 exchange.setProperty(Exchange.BATCH_COMPLETE, index == total - 1); 208 209 // update pending number of exchanges 210 pendingExchanges = total - index - 1; 211 212 // process the current exchange 213 boolean started; 214 if (customProcessor != null) { 215 // use a custom processor 216 started = customProcessExchange(exchange, customProcessor); 217 } else { 218 // process the exchange regular 219 started = processExchange(exchange); 220 } 221 222 // if we did not start process the file then decrement the counter 223 if (!started) { 224 answer--; 225 } 226 } 227 228 // drain any in progress files as we are done with this batch 229 removeExcessiveInProgressFiles(CastUtils.cast((Deque<?>) exchanges, Exchange.class), 0); 230 231 return answer; 232 } 233 234 /** 235 * Drain any in progress files as we are done with this batch 236 * 237 * @param exchanges the exchanges 238 * @param limit the limit 239 */ 240 protected void removeExcessiveInProgressFiles(Deque<Exchange> exchanges, int limit) { 241 // remove the file from the in progress list in case the batch was limited by max messages per poll 242 while (exchanges.size() > limit) { 243 // must remove last 244 Exchange exchange = exchanges.removeLast(); 245 GenericFile<?> file = exchange.getProperty(FileComponent.FILE_EXCHANGE_FILE, GenericFile.class); 246 String key = file.getAbsoluteFilePath(); 247 endpoint.getInProgressRepository().remove(key); 248 } 249 } 250 251 /** 252 * Drain any in progress files as we are done with the files 253 * 254 * @param files the files 255 */ 256 protected void removeExcessiveInProgressFiles(List<GenericFile<T>> files) { 257 for (GenericFile file : files) { 258 String key = file.getAbsoluteFilePath(); 259 endpoint.getInProgressRepository().remove(key); 260 } 261 } 262 263 /** 264 * Whether or not we can continue polling for more files 265 * 266 * @param fileList the current list of gathered files 267 * @return <tt>true</tt> to continue, <tt>false</tt> to stop due hitting maxMessagesPerPoll limit 268 */ 269 public boolean canPollMoreFiles(List<?> fileList) { 270 // at this point we should not limit if we are not eager 271 if (!eagerLimitMaxMessagesPerPoll) { 272 return true; 273 } 274 275 if (maxMessagesPerPoll <= 0) { 276 // no limitation 277 return true; 278 } 279 280 // then only poll if we haven't reached the max limit 281 return fileList.size() < maxMessagesPerPoll; 282 } 283 284 /** 285 * Override if required. Perform some checks (and perhaps actions) before we poll. 286 * 287 * @return <tt>true</tt> to poll, <tt>false</tt> to skip this poll. 288 */ 289 protected boolean prePollCheck() throws Exception { 290 return true; 291 } 292 293 /** 294 * Override if required. Perform some checks (and perhaps actions) after we have polled. 295 * 296 * @param polledMessages number of polled messages 297 */ 298 protected void postPollCheck(int polledMessages) { 299 // noop 300 } 301 302 /** 303 * Polls the given directory for files to process 304 * 305 * @param fileName current directory or file 306 * @param fileList current list of files gathered 307 * @param depth the current depth of the directory (will start from 0) 308 * @return whether or not to continue polling, <tt>false</tt> means the maxMessagesPerPoll limit has been hit 309 */ 310 protected abstract boolean pollDirectory(String fileName, List<GenericFile<T>> fileList, int depth); 311 312 /** 313 * Sets the operations to be used. 314 * <p/> 315 * Can be used to set a fresh operations in case of recovery attempts 316 * 317 * @param operations the operations 318 */ 319 public void setOperations(GenericFileOperations<T> operations) { 320 this.operations = operations; 321 } 322 323 /** 324 * Whether to ignore if the file cannot be retrieved. 325 * <p/> 326 * By default an {@link GenericFileOperationFailedException} is thrown if the file cannot be retrieved. 327 * <p/> 328 * This method allows to suppress this and just ignore that. 329 * 330 * @param name the file name 331 * @param exchange the exchange 332 * @param cause optional exception occurred during retrieving file 333 * @return <tt>true</tt> to ignore, <tt>false</tt> is the default. 334 */ 335 protected boolean ignoreCannotRetrieveFile(String name, Exchange exchange, Exception cause) { 336 return false; 337 } 338 339 /** 340 * Processes the exchange 341 * 342 * @param exchange the exchange 343 * @return <tt>true</tt> if the file was started to be processed, <tt>false</tt> if the file was not started 344 * to be processed, for some reason (not found, or aborted etc) 345 */ 346 protected boolean processExchange(final Exchange exchange) { 347 GenericFile<T> file = getExchangeFileProperty(exchange); 348 log.trace("Processing file: {}", file); 349 350 // must extract the absolute name before the begin strategy as the file could potentially be pre moved 351 // and then the file name would be changed 352 String absoluteFileName = file.getAbsoluteFilePath(); 353 354 // check if we can begin processing the file 355 final GenericFileProcessStrategy<T> processStrategy = endpoint.getGenericFileProcessStrategy(); 356 357 Exception beginCause = null; 358 boolean begin = false; 359 try { 360 begin = processStrategy.begin(operations, endpoint, exchange, file); 361 } catch (Exception e) { 362 beginCause = e; 363 } 364 365 if (!begin) { 366 // no something was wrong, so we need to abort and remove the file from the in progress list 367 Exception abortCause = null; 368 log.debug("{} cannot begin processing file: {}", endpoint, file); 369 try { 370 // abort 371 processStrategy.abort(operations, endpoint, exchange, file); 372 } catch (Exception e) { 373 abortCause = e; 374 } finally { 375 // begin returned false, so remove file from the in progress list as its no longer in progress 376 endpoint.getInProgressRepository().remove(absoluteFileName); 377 } 378 if (beginCause != null) { 379 String msg = endpoint + " cannot begin processing file: " + file + " due to: " + beginCause.getMessage(); 380 handleException(msg, beginCause); 381 } 382 if (abortCause != null) { 383 String msg2 = endpoint + " cannot abort processing file: " + file + " due to: " + abortCause.getMessage(); 384 handleException(msg2, abortCause); 385 } 386 return false; 387 } 388 389 // must use file from exchange as it can be updated due the 390 // preMoveNamePrefix/preMoveNamePostfix options 391 final GenericFile<T> target = getExchangeFileProperty(exchange); 392 393 // we can begin processing the file so update file headers on the Camel message 394 // in case it took some time to acquire read lock, and file size/timestamp has been updated since etc 395 updateFileHeaders(target, exchange.getIn()); 396 397 // must use full name when downloading so we have the correct path 398 final String name = target.getAbsoluteFilePath(); 399 try { 400 401 if (isRetrieveFile()) { 402 // retrieve the file using the stream 403 log.trace("Retrieving file: {} from: {}", name, endpoint); 404 405 // retrieve the file and check it was a success 406 boolean retrieved; 407 Exception cause = null; 408 try { 409 retrieved = operations.retrieveFile(name, exchange); 410 } catch (Exception e) { 411 retrieved = false; 412 cause = e; 413 } 414 415 if (!retrieved) { 416 if (ignoreCannotRetrieveFile(name, exchange, cause)) { 417 log.trace("Cannot retrieve file {} maybe it does not exists. Ignoring.", name); 418 // remove file from the in progress list as we could not retrieve it, but should ignore 419 endpoint.getInProgressRepository().remove(absoluteFileName); 420 return false; 421 } else { 422 // throw exception to handle the problem with retrieving the file 423 // then if the method return false or throws an exception is handled the same in here 424 // as in both cases an exception is being thrown 425 if (cause != null && cause instanceof GenericFileOperationFailedException) { 426 throw cause; 427 } else { 428 throw new GenericFileOperationFailedException("Cannot retrieve file: " + file + " from: " + endpoint, cause); 429 } 430 } 431 } 432 433 log.trace("Retrieved file: {} from: {}", name, endpoint); 434 } else { 435 log.trace("Skipped retrieval of file: {} from: {}", name, endpoint); 436 exchange.getIn().setBody(null); 437 } 438 439 // register on completion callback that does the completion strategies 440 // (for instance to move the file after we have processed it) 441 exchange.addOnCompletion(new GenericFileOnCompletion<T>(endpoint, operations, target, absoluteFileName)); 442 443 log.debug("About to process file: {} using exchange: {}", target, exchange); 444 445 if (endpoint.isSynchronous()) { 446 // process synchronously 447 getProcessor().process(exchange); 448 } else { 449 // process the exchange using the async consumer to support async routing engine 450 // which can be supported by this file consumer as all the done work is 451 // provided in the GenericFileOnCompletion 452 getAsyncProcessor().process(exchange, new AsyncCallback() { 453 public void done(boolean doneSync) { 454 // noop 455 if (log.isTraceEnabled()) { 456 log.trace("Done processing file: {} {}", target, doneSync ? "synchronously" : "asynchronously"); 457 } 458 } 459 }); 460 } 461 462 } catch (Exception e) { 463 // remove file from the in progress list due to failure 464 // (cannot be in finally block due to GenericFileOnCompletion will remove it 465 // from in progress when it takes over and processes the file, which may happen 466 // by another thread at a later time. So its only safe to remove it if there was an exception) 467 endpoint.getInProgressRepository().remove(absoluteFileName); 468 469 String msg = "Error processing file " + file + " due to " + e.getMessage(); 470 handleException(msg, e); 471 } 472 473 return true; 474 } 475 476 /** 477 * Updates the information on {@link Message} after we have acquired read-lock and 478 * can begin process the file. 479 * 480 * @param file the file 481 * @param message the Camel message to update its headers 482 */ 483 protected abstract void updateFileHeaders(GenericFile<T> file, Message message); 484 485 /** 486 * Override if required. Files are retrieved / returns true by default 487 * 488 * @return <tt>true</tt> to retrieve files, <tt>false</tt> to skip retrieval of files. 489 */ 490 protected boolean isRetrieveFile() { 491 return true; 492 } 493 494 /** 495 * Processes the exchange using a custom processor. 496 * 497 * @param exchange the exchange 498 * @param processor the custom processor 499 */ 500 protected boolean customProcessExchange(final Exchange exchange, final Processor processor) { 501 GenericFile<T> file = getExchangeFileProperty(exchange); 502 log.trace("Custom processing file: {}", file); 503 504 // must extract the absolute name before the begin strategy as the file could potentially be pre moved 505 // and then the file name would be changed 506 String absoluteFileName = file.getAbsoluteFilePath(); 507 508 try { 509 // process using the custom processor 510 processor.process(exchange); 511 } catch (Exception e) { 512 if (log.isDebugEnabled()) { 513 log.debug(endpoint + " error custom processing: " + file + " due to: " + e.getMessage() + ". This exception will be ignored.", e); 514 } 515 handleException(e); 516 } finally { 517 // always remove file from the in progress list as its no longer in progress 518 // use the original file name that was used to add it to the repository 519 // as the name can be different when using preMove option 520 endpoint.getInProgressRepository().remove(absoluteFileName); 521 } 522 523 return true; 524 } 525 526 /** 527 * Strategy for validating if the given remote file should be included or not 528 * 529 * @param file the file 530 * @param isDirectory whether the file is a directory or a file 531 * @param files files in the directory 532 * @return <tt>true</tt> to include the file, <tt>false</tt> to skip it 533 */ 534 protected boolean isValidFile(GenericFile<T> file, boolean isDirectory, List<T> files) { 535 String absoluteFilePath = file.getAbsoluteFilePath(); 536 537 if (!isMatched(file, isDirectory, files)) { 538 log.trace("File did not match. Will skip this file: {}", file); 539 return false; 540 } 541 542 // directory is always valid 543 if (isDirectory) { 544 return true; 545 } 546 547 // check if file is already in progress 548 if (endpoint.getInProgressRepository().contains(absoluteFilePath)) { 549 if (log.isTraceEnabled()) { 550 log.trace("Skipping as file is already in progress: {}", file.getFileName()); 551 } 552 return false; 553 } 554 555 // if its a file then check we have the file in the idempotent registry already 556 if (endpoint.isIdempotent()) { 557 // use absolute file path as default key, but evaluate if an expression key was configured 558 String key = file.getAbsoluteFilePath(); 559 if (endpoint.getIdempotentKey() != null) { 560 Exchange dummy = endpoint.createExchange(file); 561 key = endpoint.getIdempotentKey().evaluate(dummy, String.class); 562 } 563 if (key != null && endpoint.getIdempotentRepository().contains(key)) { 564 log.trace("This consumer is idempotent and the file has been consumed before matching idempotentKey: {}. Will skip this file: {}", key, file); 565 return false; 566 } 567 } 568 569 // okay so final step is to be able to add atomic as in-progress, so we are the 570 // only thread processing this file 571 return endpoint.getInProgressRepository().add(absoluteFilePath); 572 } 573 574 /** 575 * Strategy to perform file matching based on endpoint configuration. 576 * <p/> 577 * Will always return <tt>false</tt> for certain files/folders: 578 * <ul> 579 * <li>Starting with a dot</li> 580 * <li>lock files</li> 581 * </ul> 582 * And then <tt>true</tt> for directories. 583 * 584 * @param file the file 585 * @param isDirectory whether the file is a directory or a file 586 * @param files files in the directory 587 * @return <tt>true</tt> if the file is matched, <tt>false</tt> if not 588 */ 589 protected boolean isMatched(GenericFile<T> file, boolean isDirectory, List<T> files) { 590 String name = file.getFileNameOnly(); 591 592 // folders/names starting with dot is always skipped (eg. ".", ".camel", ".camelLock") 593 if (name.startsWith(".")) { 594 return false; 595 } 596 597 // lock files should be skipped 598 if (name.endsWith(FileComponent.DEFAULT_LOCK_FILE_POSTFIX)) { 599 return false; 600 } 601 602 if (endpoint.getFilter() != null) { 603 if (!endpoint.getFilter().accept(file)) { 604 return false; 605 } 606 } 607 608 if (endpoint.getAntFilter() != null) { 609 if (!endpoint.getAntFilter().accept(file)) { 610 return false; 611 } 612 } 613 614 if (isDirectory && endpoint.getFilterDirectory() != null) { 615 // create a dummy exchange as Exchange is needed for expression evaluation 616 Exchange dummy = endpoint.createExchange(file); 617 boolean matches = endpoint.getFilterDirectory().matches(dummy); 618 if (!matches) { 619 return false; 620 } 621 } 622 623 // directories are regarded as matched if filter accepted them 624 if (isDirectory) { 625 return true; 626 } 627 628 // exclude take precedence over include 629 if (excludePattern != null) { 630 if (excludePattern.matcher(name).matches()) { 631 return false; 632 } 633 } 634 if (includePattern != null) { 635 if (!includePattern.matcher(name).matches()) { 636 return false; 637 } 638 } 639 640 // use file expression for a simple dynamic file filter 641 if (endpoint.getFileName() != null) { 642 fileExpressionResult = evaluateFileExpression(); 643 if (fileExpressionResult != null) { 644 if (!name.equals(fileExpressionResult)) { 645 return false; 646 } 647 } 648 } 649 650 if (endpoint.getFilterFile() != null) { 651 // create a dummy exchange as Exchange is needed for expression evaluation 652 Exchange dummy = endpoint.createExchange(file); 653 boolean matches = endpoint.getFilterFile().matches(dummy); 654 if (!matches) { 655 return false; 656 } 657 } 658 659 // if done file name is enabled, then the file is only valid if a done file exists 660 if (endpoint.getDoneFileName() != null) { 661 // done file must be in same path as the file 662 String doneFileName = endpoint.createDoneFileName(file.getAbsoluteFilePath()); 663 StringHelper.notEmpty(doneFileName, "doneFileName", endpoint); 664 665 // is it a done file name? 666 if (endpoint.isDoneFile(file.getFileNameOnly())) { 667 log.trace("Skipping done file: {}", file); 668 return false; 669 } 670 671 if (!isMatched(file, doneFileName, files)) { 672 return false; 673 } 674 } 675 676 return true; 677 } 678 679 /** 680 * Strategy to perform file matching based on endpoint configuration in terms of done file name. 681 * 682 * @param file the file 683 * @param doneFileName the done file name (without any paths) 684 * @param files files in the directory 685 * @return <tt>true</tt> if the file is matched, <tt>false</tt> if not 686 */ 687 protected abstract boolean isMatched(GenericFile<T> file, String doneFileName, List<T> files); 688 689 /** 690 * Is the given file already in progress. 691 * 692 * @param file the file 693 * @return <tt>true</tt> if the file is already in progress 694 * @deprecated no longer in use, use {@link org.apache.camel.component.file.GenericFileEndpoint#getInProgressRepository()} instead. 695 */ 696 @Deprecated 697 protected boolean isInProgress(GenericFile<T> file) { 698 String key = file.getAbsoluteFilePath(); 699 // must use add, to have operation as atomic 700 return !endpoint.getInProgressRepository().add(key); 701 } 702 703 protected String evaluateFileExpression() { 704 if (fileExpressionResult == null && endpoint.getFileName() != null) { 705 // create a dummy exchange as Exchange is needed for expression evaluation 706 Exchange dummy = endpoint.createExchange(); 707 fileExpressionResult = endpoint.getFileName().evaluate(dummy, String.class); 708 if (dummy.getException() != null) { 709 throw ObjectHelper.wrapRuntimeCamelException(dummy.getException()); 710 } 711 } 712 return fileExpressionResult; 713 } 714 715 @SuppressWarnings("unchecked") 716 private GenericFile<T> getExchangeFileProperty(Exchange exchange) { 717 return (GenericFile<T>) exchange.getProperty(FileComponent.FILE_EXCHANGE_FILE); 718 } 719 720 @Override 721 protected void doStart() throws Exception { 722 super.doStart(); 723 } 724 725 @Override 726 protected void doStop() throws Exception { 727 prepareOnStartup = false; 728 super.doStop(); 729 } 730}