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.strategy;
018
019 import org.apache.camel.Exchange;
020 import org.apache.camel.component.file.GenericFile;
021 import org.apache.camel.component.file.GenericFileExclusiveReadLockStrategy;
022 import org.apache.camel.component.file.GenericFileOperations;
023 import org.apache.commons.logging.Log;
024 import org.apache.commons.logging.LogFactory;
025
026 /**
027 * Acquires exclusive read lock to the given file. Will wait until the lock is granted.
028 * After granting the read lock it is realeased, we just want to make sure that when we start
029 * consuming the file its not currently in progress of being written by third party.
030 */
031 public class GenericFileRenameExclusiveReadLockStrategy<T> implements GenericFileExclusiveReadLockStrategy<T> {
032 private static final transient Log LOG = LogFactory.getLog(GenericFileRenameExclusiveReadLockStrategy.class);
033 private long timeout;
034
035 public boolean acquireExclusiveReadLock(GenericFileOperations<T> operations, GenericFile<T> file,
036 Exchange exchange) throws Exception {
037 if (LOG.isTraceEnabled()) {
038 LOG.trace("Waiting for exclusive read lock to file: " + file);
039 }
040
041 // the trick is to try to rename the file, if we can rename then we have exclusive read
042 // since its a Generic file we cannot use java.nio to get a RW lock
043 String newName = file.getFileName() + ".camelExclusiveReadLock";
044
045 // clone and change the name
046 GenericFile<T> newFile = file.clone();
047 newFile.changeFileName(newName);
048
049 long start = System.currentTimeMillis();
050
051 boolean exclusive = false;
052 while (!exclusive) {
053 // timeout check
054 if (timeout > 0) {
055 long delta = System.currentTimeMillis() - start;
056 if (delta > timeout) {
057 LOG.debug("Cannot acquire read lock within " + timeout + " millis. Will skip the file: " + file);
058 // we could not get the lock within the timeout period, so return false
059 return false;
060 }
061 }
062
063 exclusive = operations.renameFile(file.getAbsoluteFilePath(), newFile.getAbsoluteFilePath());
064 if (exclusive) {
065 if (LOG.isTraceEnabled()) {
066 LOG.trace("Acquired exclusive read lock to file: " + file);
067 }
068 // rename it back so we can read it
069 operations.renameFile(newFile.getAbsoluteFilePath(), file.getAbsoluteFilePath());
070 } else {
071 boolean interrupted = sleep();
072 if (interrupted) {
073 // we were interrputed while sleeping, we are likely being shutdown so return false
074 return false;
075 }
076 }
077 }
078
079 return true;
080 }
081
082 public void releaseExclusiveReadLock(GenericFileOperations<T> opeations, GenericFile<T> file,
083 Exchange exchange) throws Exception {
084 // noop
085 }
086
087 private boolean sleep() {
088 LOG.trace("Exclusive read lock not granted. Sleeping for 1000 millis.");
089 try {
090 Thread.sleep(1000);
091 return true;
092 } catch (InterruptedException e) {
093 LOG.debug("Sleep interrupted while waiting for exclusive read lock, so breaking out");
094 return false;
095 }
096 }
097
098 public long getTimeout() {
099 return timeout;
100 }
101
102 /**
103 * Sets an optional timeout period.
104 * <p/>
105 * If the readlock could not be granted within the timeperiod then the wait is stopped and the
106 * <tt>acquireExclusiveReadLock</tt> returns <tt>false</tt>.
107 *
108 * @param timeout period in millis
109 */
110 public void setTimeout(long timeout) {
111 this.timeout = timeout;
112 }
113 }