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.strategy; 018 019import java.io.File; 020 021import org.apache.camel.CamelContext; 022import org.apache.camel.CamelContextAware; 023import org.apache.camel.Exchange; 024import org.apache.camel.LoggingLevel; 025import org.apache.camel.component.file.GenericFile; 026import org.apache.camel.component.file.GenericFileEndpoint; 027import org.apache.camel.component.file.GenericFileExclusiveReadLockStrategy; 028import org.apache.camel.component.file.GenericFileOperations; 029import org.apache.camel.spi.IdempotentRepository; 030import org.apache.camel.support.ServiceSupport; 031import org.apache.camel.util.CamelLogger; 032import org.apache.camel.util.ObjectHelper; 033import org.slf4j.Logger; 034import org.slf4j.LoggerFactory; 035 036/** 037 * A file read lock that uses an {@link org.apache.camel.spi.IdempotentRepository} as the lock strategy. This allows to plugin and use existing 038 * idempotent repositories that for example supports clustering. The other read lock strategies that are using marker files or file locks, 039 * are not guaranteed to work in clustered setup with various platform and file systems. 040 */ 041public class FileIdempotentRepositoryReadLockStrategy extends ServiceSupport implements GenericFileExclusiveReadLockStrategy<File>, CamelContextAware { 042 043 private static final transient Logger LOG = LoggerFactory.getLogger(FileIdempotentRepositoryReadLockStrategy.class); 044 045 private GenericFileEndpoint<File> endpoint; 046 private LoggingLevel readLockLoggingLevel = LoggingLevel.DEBUG; 047 private CamelContext camelContext; 048 private IdempotentRepository<String> idempotentRepository; 049 private boolean removeOnRollback = true; 050 private boolean removeOnCommit; 051 052 @Override 053 public void prepareOnStartup(GenericFileOperations<File> operations, GenericFileEndpoint<File> endpoint) throws Exception { 054 this.endpoint = endpoint; 055 LOG.info("Using FileIdempotentRepositoryReadLockStrategy: {} on endpoint: {}", idempotentRepository, endpoint); 056 } 057 058 @Override 059 public boolean acquireExclusiveReadLock(GenericFileOperations<File> operations, GenericFile<File> file, Exchange exchange) throws Exception { 060 // in clustered mode then another node may have processed the file so we must check here again if the file exists 061 File path = file.getFile(); 062 if (!path.exists()) { 063 return false; 064 } 065 066 // check if we can begin on this file 067 String key = asKey(file); 068 boolean answer = idempotentRepository.add(key); 069 if (!answer) { 070 // another node is processing the file so skip 071 CamelLogger.log(LOG, readLockLoggingLevel, "Cannot acquire read lock. Will skip the file: " + file); 072 } 073 return answer; 074 } 075 076 @Override 077 public void releaseExclusiveReadLockOnAbort(GenericFileOperations<File> operations, GenericFile<File> file, Exchange exchange) throws Exception { 078 // noop 079 } 080 081 @Override 082 public void releaseExclusiveReadLockOnRollback(GenericFileOperations<File> operations, GenericFile<File> file, Exchange exchange) throws Exception { 083 String key = asKey(file); 084 if (removeOnRollback) { 085 idempotentRepository.remove(key); 086 } else { 087 // okay we should not remove then confirm it instead 088 idempotentRepository.confirm(key); 089 } 090 } 091 092 @Override 093 public void releaseExclusiveReadLockOnCommit(GenericFileOperations<File> operations, GenericFile<File> file, Exchange exchange) throws Exception { 094 String key = asKey(file); 095 if (removeOnCommit) { 096 idempotentRepository.remove(key); 097 } else { 098 // confirm on commit 099 idempotentRepository.confirm(key); 100 } 101 } 102 103 public void setTimeout(long timeout) { 104 // noop 105 } 106 107 public void setCheckInterval(long checkInterval) { 108 // noop 109 } 110 111 public void setReadLockLoggingLevel(LoggingLevel readLockLoggingLevel) { 112 this.readLockLoggingLevel = readLockLoggingLevel; 113 } 114 115 public void setMarkerFiler(boolean markerFile) { 116 // noop 117 } 118 119 public void setDeleteOrphanLockFiles(boolean deleteOrphanLockFiles) { 120 // noop 121 } 122 123 public CamelContext getCamelContext() { 124 return camelContext; 125 } 126 127 public void setCamelContext(CamelContext camelContext) { 128 this.camelContext = camelContext; 129 } 130 131 /** 132 * The idempotent repository to use as the store for the read locks. 133 */ 134 public IdempotentRepository<String> getIdempotentRepository() { 135 return idempotentRepository; 136 } 137 138 /** 139 * The idempotent repository to use as the store for the read locks. 140 */ 141 public void setIdempotentRepository(IdempotentRepository<String> idempotentRepository) { 142 this.idempotentRepository = idempotentRepository; 143 } 144 145 /** 146 * Whether to remove the file from the idempotent repository when doing a rollback. 147 * <p/> 148 * By default this is true. 149 */ 150 public boolean isRemoveOnRollback() { 151 return removeOnRollback; 152 } 153 154 /** 155 * Whether to remove the file from the idempotent repository when doing a rollback. 156 * <p/> 157 * By default this is true. 158 */ 159 public void setRemoveOnRollback(boolean removeOnRollback) { 160 this.removeOnRollback = removeOnRollback; 161 } 162 163 /** 164 * Whether to remove the file from the idempotent repository when doing a commit. 165 * <p/> 166 * By default this is false. 167 */ 168 public boolean isRemoveOnCommit() { 169 return removeOnCommit; 170 } 171 172 /** 173 * Whether to remove the file from the idempotent repository when doing a commit. 174 * <p/> 175 * By default this is false. 176 */ 177 public void setRemoveOnCommit(boolean removeOnCommit) { 178 this.removeOnCommit = removeOnCommit; 179 } 180 181 protected String asKey(GenericFile<File> file) { 182 // use absolute file path as default key, but evaluate if an expression key was configured 183 String key = file.getAbsoluteFilePath(); 184 if (endpoint.getIdempotentKey() != null) { 185 Exchange dummy = endpoint.createExchange(file); 186 key = endpoint.getIdempotentKey().evaluate(dummy, String.class); 187 } 188 return key; 189 } 190 191 @Override 192 protected void doStart() throws Exception { 193 ObjectHelper.notNull(camelContext, "camelContext", this); 194 ObjectHelper.notNull(idempotentRepository, "idempotentRepository", this); 195 196 // ensure the idempotent repository is added as a service so CamelContext will stop the repo when it shutdown itself 197 camelContext.addService(idempotentRepository, true); 198 } 199 200 @Override 201 protected void doStop() throws Exception { 202 // noop 203 } 204 205}