]> source.dussan.org Git - archiva.git/blob
db26076b9a945fa8c59c93f70a50a656f13647ec
[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.util.concurrent.ConcurrentHashMap;
32 import java.util.concurrent.ConcurrentMap;
33
34 /**
35  * @author Olivier Lamy
36  * @since 2.0.0
37  */
38 @Service( "fileLockManager#default" )
39 public class DefaultFileLockManager
40     implements FileLockManager
41 {
42     // TODO currently we create lock for read and write!!
43     // the idea could be to store lock here with various clients read/write
44     // only read could be a more simple lock and acquire a write lock means waiting the end of all reading threads
45     private static final ConcurrentMap<File, Lock> lockFiles = new ConcurrentHashMap<File, Lock>( 64 );
46
47     private boolean skipLocking = true;
48
49     private Logger log = LoggerFactory.getLogger( getClass() );
50
51     private int timeout = 0;
52
53
54     @Override
55     public Lock readFileLock( File file )
56         throws FileLockException, FileLockTimeoutException
57     {
58         if ( skipLocking )
59         {
60             return new Lock( file );
61
62         }
63         StopWatch stopWatch = new StopWatch();
64         boolean acquired = false;
65         mkdirs( file.getParentFile() );
66         try
67         {
68
69             Lock lock = null;
70
71             stopWatch.start();
72
73             while ( !acquired )
74             {
75
76                 if ( timeout > 0 )
77                 {
78                     long delta = stopWatch.getTime();
79                     log.debug( "delta {}, timeout {}", delta, timeout );
80                     if ( delta > timeout )
81                     {
82                         log.warn( "Cannot acquire read lock within {} millis. Will skip the file: {}", timeout, file );
83                         // we could not get the lock within the timeout period, so  throw  FileLockTimeoutException
84                         throw new FileLockTimeoutException();
85                     }
86                 }
87
88                 lock = new Lock( file, false );
89
90                 Lock current = lockFiles.get( file );
91
92                 if ( current != null )
93                 {
94                     log.debug( "read lock file exist continue wait" );
95                     // close RandomAccessFile!!!
96                     RandomAccessFile raf =  lock.getRandomAccessFile();
97                     if (raf != null)
98                     {
99                         raf.close();
100                     }
101                     continue;
102                 }
103
104                 try
105                 {
106                     createNewFileQuietly( file );
107                     lock.openLock( false, timeout > 0 );
108                     acquired = true;
109                 }
110                 catch ( FileNotFoundException e )
111                 {
112                     // can happen if an other thread has deleted the file
113                     log.debug( "read Lock skip: {} try to create file", e.getMessage() );
114                     createNewFileQuietly( file );
115                 }
116                 catch ( IOException e )
117                 {
118                     throw new FileLockException( e.getMessage(), e );
119                 }
120                 catch ( IllegalStateException e )
121                 {
122                     log.debug( "openLock {}:{}", e.getClass(), e.getMessage() );
123                 }
124             }
125             Lock current = lockFiles.putIfAbsent( file, lock );
126             if ( current != null )
127             {
128                 lock = current;
129             }
130             return lock;
131         }
132         catch ( IOException e )
133         {
134             throw new FileLockException( e.getMessage(), e );
135         }
136     }
137
138
139     @Override
140     public Lock writeFileLock( File file )
141         throws FileLockException, FileLockTimeoutException
142     {
143         if ( skipLocking )
144         {
145             return new Lock( file );
146         }
147
148         mkdirs( file.getParentFile() );
149
150         StopWatch stopWatch = new StopWatch();
151         boolean acquired = false;
152
153         try
154         {
155             Lock lock = null;
156
157             stopWatch.start();
158
159             while ( !acquired )
160             {
161
162                 if ( timeout > 0 )
163                 {
164                     long delta = stopWatch.getTime();
165                     log.debug( "delta {}, timeout {}", delta, timeout );
166                     if ( delta > timeout )
167                     {
168                         log.warn( "Cannot acquire read lock within {} millis. Will skip the file: {}", timeout, file );
169                         // we could not get the lock within the timeout period, so throw FileLockTimeoutException
170                         throw new FileLockTimeoutException();
171                     }
172                 }
173
174                 lock = new Lock( file, true );
175
176                 Lock current = lockFiles.get( file );
177
178                 try
179                 {
180
181                     if ( current != null )
182                     {
183                         log.debug( "write lock file exist continue wait" );
184                         // close RandomAccessFile!!!
185                         RandomAccessFile raf =  lock.getRandomAccessFile();
186                         if (raf != null)
187                         {
188                             raf.close();
189                         }
190                         continue;
191                     }
192
193                     createNewFileQuietly( file );
194                     lock.openLock( true, timeout > 0 );
195                     acquired = true;
196                 }
197                 catch ( FileNotFoundException e )
198                 {
199                     // can happen if an other thread has deleted the file
200                     log.debug( "write Lock skip: {} try to create file", e.getMessage() );
201                     createNewFileQuietly( file );
202                 }
203                 catch ( IOException e )
204                 {
205                     throw new FileLockException( e.getMessage(), e );
206                 }
207                 catch ( IllegalStateException e )
208                 {
209                     log.debug( "openLock {}:{}", e.getClass(), e.getMessage() );
210                 }
211             }
212
213             Lock current = lockFiles.putIfAbsent( file, lock );
214             if ( current != null )
215             {
216                 lock = current;
217             }
218
219             return lock;
220         }
221
222         catch (
223
224             FileNotFoundException e
225
226             )
227
228         {
229             throw new FileLockException( e.getMessage(), e );
230         }
231
232     }
233
234     private void createNewFileQuietly( File file )
235     {
236         try
237         {
238             file.createNewFile();
239         }
240         catch ( IOException e )
241         {
242             // skip that
243         }
244     }
245
246     @Override
247     public void release( Lock lock )
248         throws FileLockException
249     {
250         if ( lock == null )
251         {
252             log.debug( "skip releasing null" );
253             return;
254         }
255         if ( skipLocking )
256         {
257             return;
258         }
259         try
260         {
261             lockFiles.remove( lock.getFile() );
262             lock.close();
263         }
264         catch ( IOException e )
265         {
266             throw new FileLockException( e.getMessage(), e );
267         }
268     }
269
270     public void clearLockFiles()
271     {
272         lockFiles.clear();
273     }
274
275     private boolean mkdirs( File directory )
276     {
277         if ( directory == null )
278         {
279             return false;
280         }
281
282         if ( directory.exists() )
283         {
284             return false;
285         }
286         if ( directory.mkdir() )
287         {
288             return true;
289         }
290
291         File canonDir = null;
292         try
293         {
294             canonDir = directory.getCanonicalFile();
295         }
296         catch ( IOException e )
297         {
298             return false;
299         }
300
301         File parentDir = canonDir.getParentFile();
302         return ( parentDir != null && ( mkdirs( parentDir ) || parentDir.exists() ) && canonDir.mkdir() );
303     }
304
305     public int getTimeout()
306     {
307         return timeout;
308     }
309
310     public void setTimeout( int timeout )
311     {
312         this.timeout = timeout;
313     }
314
315     public boolean isSkipLocking()
316     {
317         return skipLocking;
318     }
319
320     public void setSkipLocking( boolean skipLocking )
321     {
322         this.skipLocking = skipLocking;
323     }
324 }