]> source.dussan.org Git - archiva.git/blob
583d3b8af9c31b569dad77d8ab2348156c4d4ecf
[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
75             if ( timeout > 0 )
76             {
77                 long delta = stopWatch.getTime();
78                 log.debug( "delta {}, timeout {}", delta, timeout );
79                 if ( delta > timeout )
80                 {
81                     log.warn( "Cannot acquire read lock within {} millis. Will skip the file: {}", timeout, file );
82                     // we could not get the lock within the timeout period, so  throw  FileLockTimeoutException
83                     throw new FileLockTimeoutException();
84                 }
85             }
86
87             Lock current = lockFiles.get( file );
88
89             if ( current != null )
90             {
91                 log.debug( "read lock file exist continue wait" );
92                 continue;
93             }
94
95             try
96             {
97                 lock = new Lock( file, false );
98                 createNewFileQuietly( file );
99                 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         if ( directory == null )
280         {
281             return false;
282         }
283
284         if ( directory.exists() )
285         {
286             return false;
287         }
288         if ( directory.mkdir() )
289         {
290             return true;
291         }
292
293         File canonDir = null;
294         try
295         {
296             canonDir = directory.getCanonicalFile();
297         }
298         catch ( IOException e )
299         {
300             return false;
301         }
302
303         File parentDir = canonDir.getParentFile();
304         return ( parentDir != null && ( mkdirs( parentDir ) || parentDir.exists() ) && canonDir.mkdir() );
305     }
306
307     @Override
308     public int getTimeout()
309     {
310         return timeout;
311     }
312
313     @Override
314     public void setTimeout( int timeout )
315     {
316         this.timeout = timeout;
317     }
318
319     @Override
320     public boolean isSkipLocking()
321     {
322         return skipLocking;
323     }
324
325     @Override
326     public void setSkipLocking( boolean skipLocking )
327     {
328         this.skipLocking = skipLocking;
329     }
330 }