This project has retired. For details please refer to its Attic page.
Source code
001package org.apache.archiva.common.filelock;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *  http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import org.apache.commons.lang.time.StopWatch;
023import org.slf4j.Logger;
024import org.slf4j.LoggerFactory;
025import org.springframework.stereotype.Service;
026
027import java.io.File;
028import java.io.FileNotFoundException;
029import java.io.IOException;
030import java.io.RandomAccessFile;
031import java.nio.channels.ClosedChannelException;
032import java.util.concurrent.ConcurrentHashMap;
033import java.util.concurrent.ConcurrentMap;
034
035/**
036 * @author Olivier Lamy
037 * @since 2.0.0
038 */
039@Service("fileLockManager#default")
040public class DefaultFileLockManager
041    implements FileLockManager
042{
043    // TODO currently we create lock for read and write!!
044    // the idea could be to store lock here with various clients read/write
045    // only read could be a more simple lock and acquire a write lock means waiting the end of all reading threads
046    private static final ConcurrentMap<File, Lock> lockFiles = new ConcurrentHashMap<File, Lock>( 64 );
047
048    private boolean skipLocking = true;
049
050    private Logger log = LoggerFactory.getLogger( getClass() );
051
052    private int timeout = 0;
053
054
055    @Override
056    public Lock readFileLock( File file )
057        throws FileLockException, FileLockTimeoutException
058    {
059        if ( skipLocking )
060        {
061            return new Lock( file );
062
063        }
064        StopWatch stopWatch = new StopWatch();
065        boolean acquired = false;
066        mkdirs( file.getParentFile() );
067
068        Lock lock = null;
069
070        stopWatch.start();
071
072        while ( !acquired )
073        {
074
075            if ( timeout > 0 )
076            {
077                long delta = stopWatch.getTime();
078                log.debug( "delta {}, timeout {}", delta, timeout );
079                if ( delta > timeout )
080                {
081                    log.warn( "Cannot acquire read lock within {} millis. Will skip the file: {}", timeout, file );
082                    // we could not get the lock within the timeout period, so  throw  FileLockTimeoutException
083                    throw new FileLockTimeoutException();
084                }
085            }
086
087            Lock current = lockFiles.get( file );
088
089            if ( current != null )
090            {
091                log.debug( "read lock file exist continue wait" );
092                continue;
093            }
094
095            try
096            {
097                lock = new Lock( file, false );
098                createNewFileQuietly( file );
099                lock.openLock( false, timeout > 0 );
100                acquired = true;
101            }
102            catch ( FileNotFoundException e )
103            {
104                // can happen if an other thread has deleted the file
105                // close RandomAccessFile!!!
106                if ( lock != null )
107                {
108                    closeQuietly( lock.getRandomAccessFile() );
109                }
110                log.debug( "read Lock skip: {} try to create file", e.getMessage() );
111                createNewFileQuietly( file );
112            }
113            catch ( IOException e )
114            {
115                throw new FileLockException( e.getMessage(), e );
116            }
117            catch ( IllegalStateException e )
118            {
119                log.debug( "openLock {}:{}", e.getClass(), e.getMessage() );
120            }
121        }
122        Lock current = lockFiles.putIfAbsent( file, lock );
123        if ( current != null )
124        {
125            lock = current;
126        }
127        return lock;
128
129    }
130
131
132    @Override
133    public Lock writeFileLock( File file )
134        throws FileLockException, FileLockTimeoutException
135    {
136        if ( skipLocking )
137        {
138            return new Lock( file );
139        }
140
141        mkdirs( file.getParentFile() );
142
143        StopWatch stopWatch = new StopWatch();
144        boolean acquired = false;
145
146        Lock lock = null;
147
148        stopWatch.start();
149
150        while ( !acquired )
151        {
152
153            if ( timeout > 0 )
154            {
155                long delta = stopWatch.getTime();
156                log.debug( "delta {}, timeout {}", delta, timeout );
157                if ( delta > timeout )
158                {
159                    log.warn( "Cannot acquire read lock within {} millis. Will skip the file: {}", timeout, file );
160                    // we could not get the lock within the timeout period, so throw FileLockTimeoutException
161                    throw new FileLockTimeoutException();
162                }
163            }
164
165            Lock current = lockFiles.get( file );
166
167            try
168            {
169
170                if ( current != null )
171                {
172                    log.debug( "write lock file exist continue wait" );
173
174                    continue;
175                }
176                lock = new Lock( file, true );
177                createNewFileQuietly( file );
178                lock.openLock( true, timeout > 0 );
179                acquired = true;
180            }
181            catch ( FileNotFoundException e )
182            {
183                // can happen if an other thread has deleted the file
184                // close RandomAccessFile!!!
185                if ( lock != null )
186                {
187                    closeQuietly( lock.getRandomAccessFile() );
188                }
189                log.debug( "write Lock skip: {} try to create file", e.getMessage() );
190                createNewFileQuietly( file );
191            }
192            catch ( IOException e )
193            {
194                throw new FileLockException( e.getMessage(), e );
195            }
196            catch ( IllegalStateException e )
197            {
198                log.debug( "openLock {}:{}", e.getClass(), e.getMessage() );
199            }
200        }
201
202        Lock current = lockFiles.putIfAbsent( file, lock );
203        if ( current != null )
204        {
205            lock = current;
206        }
207
208        return lock;
209
210
211    }
212
213    private void closeQuietly( RandomAccessFile randomAccessFile )
214    {
215        if ( randomAccessFile == null )
216        {
217            return;
218        }
219
220        try
221        {
222            randomAccessFile.close();
223        }
224        catch ( IOException e )
225        {
226            // ignore
227        }
228    }
229
230    private void createNewFileQuietly( File file )
231    {
232        try
233        {
234            file.createNewFile();
235        }
236        catch ( IOException e )
237        {
238            // skip that
239        }
240    }
241
242    @Override
243    public void release( Lock lock )
244        throws FileLockException
245    {
246        if ( lock == null )
247        {
248            log.debug( "skip releasing null" );
249            return;
250        }
251        if ( skipLocking )
252        {
253            return;
254        }
255        try
256        {
257            lockFiles.remove( lock.getFile() );
258            lock.close();
259        }
260        catch ( ClosedChannelException e )
261        {
262            // skip this one
263            log.debug( "ignore ClosedChannelException: {}", e.getMessage() );
264        }
265        catch ( IOException e )
266        {
267            throw new FileLockException( e.getMessage(), e );
268        }
269    }
270
271    @Override
272    public void clearLockFiles()
273    {
274        lockFiles.clear();
275    }
276
277    private boolean mkdirs( File directory )
278    {
279        return directory.mkdirs();
280    }
281
282    @Override
283    public int getTimeout()
284    {
285        return timeout;
286    }
287
288    @Override
289    public void setTimeout( int timeout )
290    {
291        this.timeout = timeout;
292    }
293
294    @Override
295    public boolean isSkipLocking()
296    {
297        return skipLocking;
298    }
299
300    @Override
301    public void setSkipLocking( boolean skipLocking )
302    {
303        this.skipLocking = skipLocking;
304    }
305}