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 java.io.File;
020    import java.io.IOException;
021    
022    import org.apache.camel.Exchange;
023    import org.apache.camel.component.file.GenericFile;
024    import org.apache.camel.component.file.GenericFileEndpoint;
025    import org.apache.camel.component.file.GenericFileOperations;
026    import org.apache.commons.logging.Log;
027    import org.apache.commons.logging.LogFactory;
028    
029    /**
030     * Acquires exclusive read lock to the given file by checking whether the file is being
031     * changed by scanning the file at different intervals (to detec changes).
032     */
033    public class FileChangedExclusiveReadLockStrategy extends MarkerFileExclusiveReadLockStrategy {
034        private static final transient Log LOG = LogFactory.getLog(FileChangedExclusiveReadLockStrategy.class);
035        private long timeout;
036    
037        @Override
038        public void prepareOnStartup(GenericFileOperations<File> operations, GenericFileEndpoint<File> endpoint) {
039            // noop
040        }
041    
042        public boolean acquireExclusiveReadLock(GenericFileOperations<File> operations, GenericFile<File> file, Exchange exchange) throws Exception {
043            File target = new File(file.getAbsoluteFilePath());
044            boolean exclusive = false;
045    
046            if (LOG.isTraceEnabled()) {
047                LOG.trace("Waiting for exclusive read lock to file: " + file);
048            }
049    
050            try {
051                long lastModified = Long.MIN_VALUE;
052                long length = Long.MIN_VALUE;
053    
054                long start = System.currentTimeMillis();
055    
056                while (!exclusive) {
057                    // timeout check
058                    if (timeout > 0) {
059                        long delta = System.currentTimeMillis() - start;
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                    long newLastModified = target.lastModified();
068                    long newLength = target.length();
069    
070                    if (LOG.isTraceEnabled()) {
071                        LOG.trace("Previous last modified: " + lastModified + ", new last modified: " + newLastModified);
072                        LOG.trace("Previous length: " + length + ", new length: " + newLength);
073                    }
074    
075                    if (newLastModified == lastModified && newLength == length) {
076                        // let super handle the last part of acquiring the lock now the file is not
077                        // currently being in progress of being copied as file length and modified
078                        // are stable
079                        exclusive = super.acquireExclusiveReadLock(operations, file, exchange);
080                    } else {
081                        // set new base file change information
082                        lastModified = newLastModified;
083                        length = newLength;
084    
085                        boolean interrupted = sleep();
086                        if (interrupted) {
087                            // we were interrupted while sleeping, we are likely being shutdown so return false
088                            return false;
089                        }
090                    }
091                }
092            } catch (IOException e) {
093                // must handle IOException as some apps on Windows etc. will still somehow hold a lock to a file
094                // such as AntiVirus or MS Office that has special locks for it's supported files
095                if (timeout == 0) {
096                    // if not using timeout, then we cant retry, so rethrow
097                    throw e;
098                }
099                if (LOG.isDebugEnabled()) {
100                    LOG.debug("Cannot acquire read lock. Will try again.", e);
101                }
102                boolean interrupted = sleep();
103                if (interrupted) {
104                    // we were interrupted while sleeping, we are likely being shutdown so return false
105                    return false;
106                }
107            }
108    
109            return exclusive;
110        }
111    
112        private boolean sleep() {
113            if (LOG.isTraceEnabled()) {
114                LOG.trace("Exclusive read lock not granted. Sleeping for 1000 millis.");
115            }
116            try {
117                Thread.sleep(1000);
118                return false;
119            } catch (InterruptedException e) {
120                if (LOG.isDebugEnabled()) {
121                    LOG.debug("Sleep interrupted while waiting for exclusive read lock, so breaking out");
122                }
123                return true;
124            }
125        }
126    
127        public long getTimeout() {
128            return timeout;
129        }
130    
131        public void setTimeout(long timeout) {
132            this.timeout = timeout;
133        }
134    
135    }