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