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.camel.util.StopWatch;
025    import org.slf4j.Logger;
026    import org.slf4j.LoggerFactory;
027    
028    /**
029     * Acquires exclusive read lock to the given file. Will wait until the lock is granted.
030     * After granting the read lock it is released, we just want to make sure that when we start
031     * consuming the file its not currently in progress of being written by third party.
032     */
033    public class GenericFileRenameExclusiveReadLockStrategy<T> implements GenericFileExclusiveReadLockStrategy<T> {
034        private static final transient Logger LOG = LoggerFactory.getLogger(GenericFileRenameExclusiveReadLockStrategy.class);
035        private long timeout;
036        private long checkInterval;
037    
038        public void prepareOnStartup(GenericFileOperations<T> operations, GenericFileEndpoint<T> endpoint) throws Exception {
039            // noop
040        }
041    
042        public boolean acquireExclusiveReadLock(GenericFileOperations<T> operations, GenericFile<T> file,
043                                                Exchange exchange) throws Exception {
044            LOG.trace("Waiting for exclusive read lock to file: {}", file);
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            StopWatch watch = new StopWatch();
054    
055            boolean exclusive = false;
056            while (!exclusive) {
057                // timeout check
058                if (timeout > 0) {
059                    long delta = watch.taken();
060                    if (delta > timeout) {
061                        LOG.warn("Cannot acquire read lock within " + timeout + " millis. Will skip the file: " + file);
062                        // we could not get the lock within the timeout period, so return false
063                        return false;
064                    }
065                }
066    
067                exclusive = operations.renameFile(file.getAbsoluteFilePath(), newFile.getAbsoluteFilePath());
068                if (exclusive) {
069                    LOG.trace("Acquired exclusive read lock to file: {}", file);
070                    // rename it back so we can read it
071                    operations.renameFile(newFile.getAbsoluteFilePath(), file.getAbsoluteFilePath());
072                } else {
073                    boolean interrupted = sleep();
074                    if (interrupted) {
075                        // we were interrupted while sleeping, we are likely being shutdown so return false
076                        return false;
077                    }
078                }
079            }
080    
081            return true;
082        }
083    
084        public void releaseExclusiveReadLock(GenericFileOperations<T> operations, GenericFile<T> file,
085                                             Exchange exchange) throws Exception {
086            // noop
087        }
088    
089        private boolean sleep() {
090            LOG.trace("Exclusive read lock not granted. Sleeping for {} millis.", checkInterval);
091            try {
092                Thread.sleep(checkInterval);
093                return false;
094            } catch (InterruptedException e) {
095                LOG.debug("Sleep interrupted while waiting for exclusive read lock, so breaking out");
096                return true;
097            }
098        }
099    
100        public long getTimeout() {
101            return timeout;
102        }
103    
104        public void setTimeout(long timeout) {
105            this.timeout = timeout;
106        }
107    
108        public void setCheckInterval(long checkInterval) {
109            this.checkInterval = checkInterval;
110        }
111    }