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 interrupted 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> operations, 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 false;
092            } catch (InterruptedException e) {            
093                LOG.debug("Sleep interrupted while waiting for exclusive read lock, so breaking out");
094                return true;
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    }