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.lang3.time.StopWatch;
023import org.slf4j.Logger;
024import org.slf4j.LoggerFactory;
025import org.springframework.stereotype.Service;
026
027import java.io.FileNotFoundException;
028import java.io.IOException;
029import java.nio.channels.ClosedChannelException;
030import java.nio.file.Files;
031import java.nio.file.NoSuchFileException;
032import java.nio.file.Path;
033import java.util.concurrent.ConcurrentHashMap;
034import java.util.concurrent.ConcurrentMap;
035
036/**
037 * @author Olivier Lamy
038 * @since 2.0.0
039 */
040@Service("fileLockManager#default")
041public class DefaultFileLockManager
042    implements FileLockManager
043{
044    // TODO currently we create lock for read and write!!
045    // the idea could be to store lock here with various clients read/write
046    // only read could be a more simple lock and acquire a write lock means waiting the end of all reading threads
047    private static final ConcurrentMap<Path, Lock> lockFiles = new ConcurrentHashMap<Path, Lock>( 64 );
048
049    private boolean skipLocking = true;
050
051    private Logger log = LoggerFactory.getLogger( getClass() );
052
053    private int timeout = 0;
054
055
056    @Override
057    public Lock readFileLock( Path file )
058        throws FileLockException, FileLockTimeoutException
059    {
060        if ( skipLocking )
061        {
062            return new Lock( file );
063
064        }
065        StopWatch stopWatch = new StopWatch();
066        boolean acquired = false;
067        try {
068            mkdirs(file.getParent());
069        } catch (IOException e) {
070            throw new FileLockException("Could not create directories "+file.getParent(), e);
071        }
072
073        Lock lock = null;
074
075        stopWatch.start();
076
077        while ( !acquired )
078        {
079            // Make sure that not a bad lock is returned, if a exception was thrown.
080            lock = null;
081
082            if ( timeout > 0 )
083            {
084                long delta = stopWatch.getTime();
085                log.debug( "delta {}, timeout {}", delta, timeout );
086                if ( delta > timeout )
087                {
088                    log.warn( "Cannot acquire read lock within {} millis. Will skip the file: {}", timeout, file );
089                    // we could not get the lock within the timeout period, so  throw  FileLockTimeoutException
090                    throw new FileLockTimeoutException();
091                }
092            }
093
094            Lock current = lockFiles.get( file );
095
096            if ( current != null )
097            {
098                log.trace( "read lock file exist continue wait" );
099                continue;
100            }
101
102            try
103            {
104                lock = new Lock( file, false );
105                createNewFileQuietly( file );
106                lock.openLock( false, timeout > 0 );
107                // We are not returning an existing lock. If the lock is not
108                // exclusive, another thread may release the lock and the client
109                // knows nothing about it.
110                // The only atomic operation is the putIfAbsent operation, so if
111                // this returns null everything is OK, otherwise we should start at
112                // the beginning.
113                current = lockFiles.putIfAbsent( file, lock );
114                if ( current == null )
115                {
116                    // Success
117                    acquired = true;
118                } else {
119                    // We try again
120                    lock.close();
121                    lock=null;
122                }
123            }
124            catch ( FileNotFoundException | NoSuchFileException e )
125            {
126
127                log.debug( "read Lock skip: {} try to create file", e.getMessage() );
128                createNewFileQuietly( file );
129            }
130            catch ( IOException e )
131            {
132                throw new FileLockException( e.getMessage(), e );
133            }
134            catch ( IllegalStateException e )
135            {
136                log.trace( "openLock {}:{}", e.getClass(), e.getMessage() );
137            }
138        }
139
140        return lock;
141
142    }
143
144
145    @Override
146    public Lock writeFileLock( Path file )
147        throws FileLockException, FileLockTimeoutException
148    {
149        if ( skipLocking )
150        {
151            return new Lock( file );
152        }
153
154        try {
155            mkdirs( file.getParent() );
156        } catch (IOException e) {
157            throw new FileLockException("Could not create directory "+file.getParent(), e);
158        }
159
160        StopWatch stopWatch = new StopWatch();
161        boolean acquired = false;
162
163        Lock lock = null;
164
165        stopWatch.start();
166
167        while ( !acquired )
168        {
169            // Make sure that not a bad lock is returned, if a exception was thrown.
170            lock = null;
171            if ( timeout > 0 )
172            {
173                long delta = stopWatch.getTime();
174                log.debug( "delta {}, timeout {}", delta, timeout );
175                if ( delta > timeout )
176                {
177                    log.warn( "Cannot acquire read lock within {} millis. Will skip the file: {}", timeout, file );
178                    // we could not get the lock within the timeout period, so throw FileLockTimeoutException
179                    throw new FileLockTimeoutException();
180                }
181            }
182
183            Lock current = lockFiles.get( file );
184
185            try
186            {
187
188                if ( current != null )
189                {
190                    log.trace( "write lock file exist continue wait" );
191
192                    continue;
193                }
194                lock = new Lock( file, true );
195                createNewFileQuietly( file );
196                lock.openLock( true, timeout > 0 );
197                // We are not returning an existing lock. If the lock is not
198                // exclusive, another thread may release the lock and the client
199                // knows nothing about it.
200                // The only atomic operation is the putIfAbsent operation, so if
201                // this returns null everything is OK, otherwise we should start at
202                // the beginning.
203                current = lockFiles.putIfAbsent( file, lock );
204                if ( current == null )
205                {
206                    // Success
207                    acquired = true;
208                } else {
209                    // We try again
210                    lock.close();
211                    lock=null;
212                }
213            }
214            catch ( FileNotFoundException | NoSuchFileException e )
215            {
216
217                log.debug( "write Lock skip: {} try to create file", e.getMessage() );
218                createNewFileQuietly( file );
219            }
220            catch ( IOException e )
221            {
222                throw new FileLockException( e.getMessage(), e );
223            }
224            catch ( IllegalStateException e )
225            {
226                log.trace( "openLock {}:{}", e.getClass(), e.getMessage() );
227            }
228        }
229
230        return lock;
231
232
233    }
234
235     private void createNewFileQuietly( Path file )
236    {
237        try
238        {
239            Files.createFile(file);
240        }
241        catch ( IOException e )
242        {
243            // skip that
244        }
245    }
246
247    @Override
248    public void release( Lock lock )
249        throws FileLockException
250    {
251        if ( lock == null )
252        {
253            log.debug( "skip releasing null" );
254            return;
255        }
256        if ( skipLocking )
257        {
258            return;
259        }
260        try
261        {
262            lockFiles.remove( lock.getFile() );
263            lock.close();
264        }
265        catch ( ClosedChannelException e )
266        {
267            // skip this one
268            log.debug( "ignore ClosedChannelException: {}", e.getMessage() );
269        }
270        catch ( IOException e )
271        {
272            throw new FileLockException( e.getMessage(), e );
273        }
274    }
275
276    @Override
277    public void clearLockFiles()
278    {
279        lockFiles.clear();
280    }
281
282    private Path mkdirs( Path directory ) throws IOException {
283        return Files.createDirectories(directory);
284    }
285
286    @Override
287    public int getTimeout()
288    {
289        return timeout;
290    }
291
292    @Override
293    public void setTimeout( int timeout )
294    {
295        this.timeout = timeout;
296    }
297
298    @Override
299    public boolean isSkipLocking()
300    {
301        return skipLocking;
302    }
303
304    @Override
305    public void setSkipLocking( boolean skipLocking )
306    {
307        this.skipLocking = skipLocking;
308    }
309}