]> source.dussan.org Git - archiva.git/blob
dcfeb04b3472ec3c62705a095345375191a89e9f
[archiva.git] /
1 package org.apache.archiva.common.filelock;
2
3 /*
4  * Licensed to the Apache Software Foundation (ASF) under one
5  * or more contributor license agreements.  See the NOTICE file
6  * distributed with this work for additional information
7  * regarding copyright ownership.  The ASF licenses this file
8  * to you under the Apache License, Version 2.0 (the
9  * "License"); you may not use this file except in compliance
10  * with the License.  You may obtain a copy of the License at
11  *
12  *  http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing,
15  * software distributed under the License is distributed on an
16  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17  * KIND, either express or implied.  See the License for the
18  * specific language governing permissions and limitations
19  * under the License.
20  */
21
22 import org.apache.commons.lang.time.StopWatch;
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
25 import org.springframework.stereotype.Service;
26
27 import java.io.File;
28 import java.io.FileNotFoundException;
29 import java.io.IOException;
30 import java.io.RandomAccessFile;
31 import java.nio.channels.ClosedChannelException;
32 import java.util.concurrent.ConcurrentHashMap;
33 import java.util.concurrent.ConcurrentMap;
34
35 /**
36  * @author Olivier Lamy
37  * @since 2.0.0
38  */
39 @Service("fileLockManager#default")
40 public class DefaultFileLockManager
41     implements FileLockManager
42 {
43     // TODO currently we create lock for read and write!!
44     // the idea could be to store lock here with various clients read/write
45     // only read could be a more simple lock and acquire a write lock means waiting the end of all reading threads
46     private static final ConcurrentMap<File, Lock> lockFiles = new ConcurrentHashMap<File, Lock>( 64 );
47
48     private boolean skipLocking = true;
49
50     private Logger log = LoggerFactory.getLogger( getClass() );
51
52     private int timeout = 0;
53
54
55     @Override
56     public Lock readFileLock( File file )
57         throws FileLockException, FileLockTimeoutException
58     {
59         if ( skipLocking )
60         {
61             return new Lock( file );
62
63         }
64         StopWatch stopWatch = new StopWatch();
65         boolean acquired = false;
66         mkdirs( file.getParentFile() );
67
68         Lock lock = null;
69
70         stopWatch.start();
71
72         while ( !acquired )
73         {
74             // Make sure that not a bad lock is returned, if a exception was thrown.
75             lock = null;
76
77             if ( timeout > 0 )
78             {
79                 long delta = stopWatch.getTime();
80                 log.debug( "delta {}, timeout {}", delta, timeout );
81                 if ( delta > timeout )
82                 {
83                     log.warn( "Cannot acquire read lock within {} millis. Will skip the file: {}", timeout, file );
84                     // we could not get the lock within the timeout period, so  throw  FileLockTimeoutException
85                     throw new FileLockTimeoutException();
86                 }
87             }
88
89             Lock current = lockFiles.get( file );
90
91             if ( current != null )
92             {
93                 log.trace( "read lock file exist continue wait" );
94                 continue;
95             }
96
97             try
98             {
99                 lock = new Lock( file, false );
100                 createNewFileQuietly( file );
101                 lock.openLock( false, timeout > 0 );
102                 // We are not returning an existing lock. If the lock is not
103                 // exclusive, another thread may release the lock and the client
104                 // knows nothing about it.
105                 // The only atomic operation is the putIfAbsent operation, so if
106                 // this returns null everything is OK, otherwise we should start at
107                 // the beginning.
108                 current = lockFiles.putIfAbsent( file, lock );
109                 if ( current == null )
110                 {
111                     // Success
112                     acquired = true;
113                 } else {
114                     // We try again
115                     lock.close();
116                     lock=null;
117                 }
118             }
119             catch ( FileNotFoundException e )
120             {
121                 // can happen if an other thread has deleted the file
122                 // close RandomAccessFile!!!
123                 if ( lock != null )
124                 {
125                     closeQuietly( lock.getRandomAccessFile() );
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( File file )
147         throws FileLockException, FileLockTimeoutException
148     {
149         if ( skipLocking )
150         {
151             return new Lock( file );
152         }
153
154         mkdirs( file.getParentFile() );
155
156         StopWatch stopWatch = new StopWatch();
157         boolean acquired = false;
158
159         Lock lock = null;
160
161         stopWatch.start();
162
163         while ( !acquired )
164         {
165             // Make sure that not a bad lock is returned, if a exception was thrown.
166             lock = null;
167             if ( timeout > 0 )
168             {
169                 long delta = stopWatch.getTime();
170                 log.debug( "delta {}, timeout {}", delta, timeout );
171                 if ( delta > timeout )
172                 {
173                     log.warn( "Cannot acquire read lock within {} millis. Will skip the file: {}", timeout, file );
174                     // we could not get the lock within the timeout period, so throw FileLockTimeoutException
175                     throw new FileLockTimeoutException();
176                 }
177             }
178
179             Lock current = lockFiles.get( file );
180
181             try
182             {
183
184                 if ( current != null )
185                 {
186                     log.trace( "write lock file exist continue wait" );
187
188                     continue;
189                 }
190                 lock = new Lock( file, true );
191                 createNewFileQuietly( file );
192                 lock.openLock( true, timeout > 0 );
193                 // We are not returning an existing lock. If the lock is not
194                 // exclusive, another thread may release the lock and the client
195                 // knows nothing about it.
196                 // The only atomic operation is the putIfAbsent operation, so if
197                 // this returns null everything is OK, otherwise we should start at
198                 // the beginning.
199                 current = lockFiles.putIfAbsent( file, lock );
200                 if ( current == null )
201                 {
202                     // Success
203                     acquired = true;
204                 } else {
205                     // We try again
206                     lock.close();
207                     lock=null;
208                 }
209             }
210             catch ( FileNotFoundException e )
211             {
212                 // can happen if an other thread has deleted the file
213                 // close RandomAccessFile!!!
214                 if ( lock != null )
215                 {
216                     closeQuietly( lock.getRandomAccessFile() );
217                 }
218                 log.debug( "write Lock skip: {} try to create file", e.getMessage() );
219                 createNewFileQuietly( file );
220             }
221             catch ( IOException e )
222             {
223                 throw new FileLockException( e.getMessage(), e );
224             }
225             catch ( IllegalStateException e )
226             {
227                 log.trace( "openLock {}:{}", e.getClass(), e.getMessage() );
228             }
229         }
230
231         return lock;
232
233
234     }
235
236     private void closeQuietly( RandomAccessFile randomAccessFile )
237     {
238         if ( randomAccessFile == null )
239         {
240             return;
241         }
242
243         try
244         {
245             randomAccessFile.close();
246         }
247         catch ( IOException e )
248         {
249             // ignore
250         }
251     }
252
253     private void createNewFileQuietly( File file )
254     {
255         try
256         {
257             file.createNewFile();
258         }
259         catch ( IOException e )
260         {
261             // skip that
262         }
263     }
264
265     @Override
266     public void release( Lock lock )
267         throws FileLockException
268     {
269         if ( lock == null )
270         {
271             log.debug( "skip releasing null" );
272             return;
273         }
274         if ( skipLocking )
275         {
276             return;
277         }
278         try
279         {
280             lockFiles.remove( lock.getFile() );
281             lock.close();
282         }
283         catch ( ClosedChannelException e )
284         {
285             // skip this one
286             log.debug( "ignore ClosedChannelException: {}", e.getMessage() );
287         }
288         catch ( IOException e )
289         {
290             throw new FileLockException( e.getMessage(), e );
291         }
292     }
293
294     @Override
295     public void clearLockFiles()
296     {
297         lockFiles.clear();
298     }
299
300     private boolean mkdirs( File directory )
301     {
302         return directory.mkdirs();
303     }
304
305     @Override
306     public int getTimeout()
307     {
308         return timeout;
309     }
310
311     @Override
312     public void setTimeout( int timeout )
313     {
314         this.timeout = timeout;
315     }
316
317     @Override
318     public boolean isSkipLocking()
319     {
320         return skipLocking;
321     }
322
323     @Override
324     public void setSkipLocking( boolean skipLocking )
325     {
326         this.skipLocking = skipLocking;
327     }
328 }