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 org.apache.camel.Exchange;
020 import org.apache.camel.impl.LoggingExceptionHandler;
021 import org.apache.camel.spi.ExceptionHandler;
022 import org.apache.camel.spi.Synchronization;
023 import org.apache.camel.util.ObjectHelper;
024 import org.slf4j.Logger;
025 import org.slf4j.LoggerFactory;
026
027 /**
028 * On completion strategy that performs the required work after the {@link Exchange} has been processed.
029 * <p/>
030 * The work is for example to move the processed file into a backup folder, delete the file or
031 * in case of processing failure do a rollback.
032 *
033 * @version
034 */
035 public class GenericFileOnCompletion<T> implements Synchronization {
036
037 private final transient Logger log = LoggerFactory.getLogger(GenericFileOnCompletion.class);
038 private GenericFileEndpoint<T> endpoint;
039 private GenericFileOperations<T> operations;
040 private ExceptionHandler exceptionHandler;
041 private GenericFile<T> file;
042 private String absoluteFileName;
043
044 public GenericFileOnCompletion(GenericFileEndpoint<T> endpoint, GenericFileOperations<T> operations,
045 GenericFile<T> file, String absoluteFileName) {
046 this.endpoint = endpoint;
047 this.operations = operations;
048 this.file = file;
049 this.absoluteFileName = absoluteFileName;
050 }
051
052 public void onComplete(Exchange exchange) {
053 onCompletion(exchange);
054 }
055
056 public void onFailure(Exchange exchange) {
057 onCompletion(exchange);
058 }
059
060 public ExceptionHandler getExceptionHandler() {
061 if (exceptionHandler == null) {
062 exceptionHandler = new LoggingExceptionHandler(getClass());
063 }
064 return exceptionHandler;
065 }
066
067 public void setExceptionHandler(ExceptionHandler exceptionHandler) {
068 this.exceptionHandler = exceptionHandler;
069 }
070
071 protected void onCompletion(Exchange exchange) {
072 GenericFileProcessStrategy<T> processStrategy = endpoint.getGenericFileProcessStrategy();
073
074 log.debug("Done processing file: {} using exchange: {}", file, exchange);
075
076 // commit or rollback
077 boolean committed = false;
078 try {
079 boolean failed = exchange.isFailed();
080 if (!failed) {
081 // commit the file strategy if there was no failure or already handled by the DeadLetterChannel
082 processStrategyCommit(processStrategy, exchange, file);
083 committed = true;
084 }
085 // if we failed, then it will be handled by the rollback in the finally block below
086 } finally {
087 if (!committed) {
088 processStrategyRollback(processStrategy, exchange, file);
089 }
090
091 // remove file from the in progress list as its no longer in progress
092 // use the original file name that was used to add it to the repository
093 // as the name can be different when using preMove option
094 endpoint.getInProgressRepository().remove(absoluteFileName);
095 }
096 }
097
098 /**
099 * Strategy when the file was processed and a commit should be executed.
100 *
101 * @param processStrategy the strategy to perform the commit
102 * @param exchange the exchange
103 * @param file the file processed
104 */
105 protected void processStrategyCommit(GenericFileProcessStrategy<T> processStrategy,
106 Exchange exchange, GenericFile<T> file) {
107 if (endpoint.isIdempotent()) {
108 // only add to idempotent repository if we could process the file
109 endpoint.getIdempotentRepository().add(absoluteFileName);
110 }
111
112 // delete done file if used (and not noop=true)
113 if (endpoint.getDoneFileName() != null && !endpoint.isNoop()) {
114 // done file must be in same path as the original input file
115 String doneFileName = endpoint.createDoneFileName(absoluteFileName);
116 ObjectHelper.notEmpty(doneFileName, "doneFileName", endpoint);
117
118 try {
119 // delete done file
120 boolean deleted = operations.deleteFile(doneFileName);
121 log.trace("Done file: {} was deleted: {}", doneFileName, deleted);
122 if (!deleted) {
123 log.warn("Done file: " + doneFileName + " could not be deleted");
124 }
125 } catch (Exception e) {
126 handleException(e);
127 }
128 }
129
130 try {
131 log.trace("Commit file strategy: {} for file: {}", processStrategy, file);
132 processStrategy.commit(operations, endpoint, exchange, file);
133 } catch (Exception e) {
134 handleException(e);
135 }
136 }
137
138 /**
139 * Strategy when the file was not processed and a rollback should be executed.
140 *
141 * @param processStrategy the strategy to perform the commit
142 * @param exchange the exchange
143 * @param file the file processed
144 */
145 protected void processStrategyRollback(GenericFileProcessStrategy<T> processStrategy,
146 Exchange exchange, GenericFile<T> file) {
147
148 if (log.isWarnEnabled()) {
149 log.warn("Rollback file strategy: " + processStrategy + " for file: " + file);
150 }
151 try {
152 processStrategy.rollback(operations, endpoint, exchange, file);
153 } catch (Exception e) {
154 handleException(e);
155 }
156 }
157
158 protected void handleException(Throwable t) {
159 Throwable newt = (t == null) ? new IllegalArgumentException("Handling [null] exception") : t;
160 getExceptionHandler().handleException(newt);
161 }
162
163 @Override
164 public String toString() {
165 return "GenericFileOnCompletion";
166 }
167 }