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.FileNotFoundException; 020 021import org.apache.camel.Exchange; 022import org.apache.camel.LoggingLevel; 023import org.apache.camel.component.file.GenericFile; 024import org.apache.camel.component.file.GenericFileEndpoint; 025import org.apache.camel.component.file.GenericFileExclusiveReadLockStrategy; 026import org.apache.camel.component.file.GenericFileOperationFailedException; 027import org.apache.camel.component.file.GenericFileOperations; 028import org.apache.camel.util.CamelLogger; 029import org.apache.camel.util.StopWatch; 030import org.slf4j.Logger; 031import org.slf4j.LoggerFactory; 032 033/** 034 * Acquires exclusive read lock to the given file. Will wait until the lock is granted. 035 * After granting the read lock it is released, we just want to make sure that when we start 036 * consuming the file its not currently in progress of being written by third party. 037 */ 038public class GenericFileRenameExclusiveReadLockStrategy<T> implements GenericFileExclusiveReadLockStrategy<T> { 039 private static final Logger LOG = LoggerFactory.getLogger(GenericFileRenameExclusiveReadLockStrategy.class); 040 private long timeout; 041 private long checkInterval; 042 private LoggingLevel readLockLoggingLevel = LoggingLevel.DEBUG; 043 044 @Override 045 public void prepareOnStartup(GenericFileOperations<T> operations, GenericFileEndpoint<T> endpoint) throws Exception { 046 // noop 047 } 048 049 @Override 050 public boolean acquireExclusiveReadLock(GenericFileOperations<T> operations, GenericFile<T> file, 051 Exchange exchange) throws Exception { 052 LOG.trace("Waiting for exclusive read lock to file: {}", file); 053 054 // the trick is to try to rename the file, if we can rename then we have exclusive read 055 // since its a Generic file we cannot use java.nio to get a RW lock 056 String newName = file.getFileName() + ".camelExclusiveReadLock"; 057 058 // make a copy as result and change its file name 059 GenericFile<T> newFile = file.copyFrom(file); 060 newFile.changeFileName(newName); 061 StopWatch watch = new StopWatch(); 062 063 boolean exclusive = false; 064 while (!exclusive) { 065 // timeout check 066 if (timeout > 0) { 067 long delta = watch.taken(); 068 if (delta > timeout) { 069 CamelLogger.log(LOG, readLockLoggingLevel, 070 "Cannot acquire read lock within " + timeout + " millis. Will skip the file: " + file); 071 // we could not get the lock within the timeout period, so return false 072 return false; 073 } 074 } 075 076 try { 077 exclusive = operations.renameFile(file.getAbsoluteFilePath(), newFile.getAbsoluteFilePath()); 078 } catch (GenericFileOperationFailedException ex) { 079 if (ex.getCause() instanceof FileNotFoundException) { 080 exclusive = false; 081 } else { 082 throw ex; 083 } 084 } 085 if (exclusive) { 086 LOG.trace("Acquired exclusive read lock to file: {}", file); 087 // rename it back so we can read it 088 operations.renameFile(newFile.getAbsoluteFilePath(), file.getAbsoluteFilePath()); 089 } else { 090 boolean interrupted = sleep(); 091 if (interrupted) { 092 // we were interrupted while sleeping, we are likely being shutdown so return false 093 return false; 094 } 095 } 096 } 097 098 return true; 099 } 100 101 @Override 102 public void releaseExclusiveReadLockOnAbort(GenericFileOperations<T> operations, GenericFile<T> file, Exchange exchange) throws Exception { 103 // noop 104 } 105 106 @Override 107 public void releaseExclusiveReadLockOnRollback(GenericFileOperations<T> operations, GenericFile<T> file, Exchange exchange) throws Exception { 108 // noop 109 } 110 111 @Override 112 public void releaseExclusiveReadLockOnCommit(GenericFileOperations<T> operations, GenericFile<T> file, Exchange exchange) throws Exception { 113 // noop 114 } 115 116 private boolean sleep() { 117 LOG.trace("Exclusive read lock not granted. Sleeping for {} millis.", checkInterval); 118 try { 119 Thread.sleep(checkInterval); 120 return false; 121 } catch (InterruptedException e) { 122 LOG.debug("Sleep interrupted while waiting for exclusive read lock, so breaking out"); 123 return true; 124 } 125 } 126 127 public long getTimeout() { 128 return timeout; 129 } 130 131 @Override 132 public void setTimeout(long timeout) { 133 this.timeout = timeout; 134 } 135 136 @Override 137 public void setCheckInterval(long checkInterval) { 138 this.checkInterval = checkInterval; 139 } 140 141 @Override 142 public void setReadLockLoggingLevel(LoggingLevel readLockLoggingLevel) { 143 this.readLockLoggingLevel = readLockLoggingLevel; 144 } 145 146 @Override 147 public void setMarkerFiler(boolean markerFile) { 148 // noop - we do not use marker file with the rename strategy 149 } 150 151 @Override 152 public void setDeleteOrphanLockFiles(boolean deleteOrphanLockFiles) { 153 // noop - we do not use marker file with the rename strategy 154 } 155}