]> source.dussan.org Git - archiva.git/blob
2ce2f188c2912f02d51cc0f5c159438c4fad90fd
[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 import java.util.concurrent.locks.ReentrantReadWriteLock;
35
36 /**
37  * @author Olivier Lamy
38  * @since 2.0.0
39  */
40 @Service("fileLockManager#default")
41 public class DefaultFileLockManager
42     implements FileLockManager
43 {
44     // TODO currently we create lock for read and write!!
45     // the idea could be to store lock here with various clients read/write
46     // only read could be a more simple lock and acquire a write lock means waiting the end of all reading threads
47     private static final ConcurrentMap<File, Lock> lockFiles = new ConcurrentHashMap<File, Lock>( 64 );
48
49     private boolean skipLocking = true;
50
51     private Logger log = LoggerFactory.getLogger( getClass() );
52
53     private int timeout = 0;
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             try
166             {
167                 Lock current = lockFiles.get( file );
168                 if ( current != null )
169                 {
170                     log.debug( "write lock file exist continue wait" );
171
172                     continue;
173                 }
174                 lock = new Lock(file, true);
175                 createNewFileQuietly(file);
176                 lock.openLock(true, timeout > 0);
177                 acquired = true;
178             }
179             catch ( FileNotFoundException e )
180             {
181                 // can happen if an other thread has deleted the file
182                 // close RandomAccessFile!!!
183                 if ( lock != null )
184                 {
185                     closeQuietly( lock.getRandomAccessFile() );
186                 }
187                 log.debug( "write Lock skip: {} try to create file", e.getMessage() );
188                 createNewFileQuietly( file );
189             }
190             catch ( IOException e )
191             {
192                 if (lock!=null && lock.isValid()) {
193                     try {
194                         lock.close();
195                     } catch (IOException ex) {
196                         // Ignore
197                     }
198                 }
199                 throw new FileLockException( e.getMessage(), e );
200             }
201             catch ( Throwable e )
202             {
203                 if (lock!=null && lock.isValid()) {
204                     try {
205                         lock.close();
206                     } catch (IOException ex) {
207                         // Ignore
208                     } finally {
209                         lock = null;
210                     }
211                 }
212                 log.debug( "openLock {}:{}", e.getClass(), e.getMessage() );
213             }
214
215         }
216
217         Lock current = lockFiles.putIfAbsent( file, lock );
218         if ( current != null )
219         {
220             lock = current;
221         }
222
223         return lock;
224
225
226     }
227
228     private void closeQuietly( RandomAccessFile randomAccessFile )
229     {
230         if ( randomAccessFile == null )
231         {
232             return;
233         }
234
235         try
236         {
237             randomAccessFile.close();
238         }
239         catch ( IOException e )
240         {
241             // ignore
242         }
243     }
244
245     private void createNewFileQuietly( File file )
246     {
247         try
248         {
249             file.createNewFile();
250         }
251         catch ( IOException e )
252         {
253             // skip that
254         }
255     }
256
257     @Override
258     public void release( Lock lock )
259         throws FileLockException
260     {
261         if ( lock == null )
262         {
263             log.debug( "skip releasing null" );
264             return;
265         }
266         if ( skipLocking )
267         {
268             return;
269         }
270         try
271         {
272             lockFiles.remove( lock.getFile() );
273             lock.close();
274         }
275         catch ( ClosedChannelException e )
276         {
277             // skip this one
278             log.debug( "ignore ClosedChannelException: {}", e.getMessage() );
279         }
280         catch ( IOException e )
281         {
282             throw new FileLockException( e.getMessage(), e );
283         }
284     }
285
286     @Override
287     public void clearLockFiles()
288     {
289         lockFiles.clear();
290     }
291
292     private boolean mkdirs( File directory )
293     {
294         return directory.mkdirs();
295     }
296
297     @Override
298     public int getTimeout()
299     {
300         return timeout;
301     }
302
303     @Override
304     public void setTimeout( int timeout )
305     {
306         this.timeout = timeout;
307     }
308
309     @Override
310     public boolean isSkipLocking()
311     {
312         return skipLocking;
313     }
314
315     @Override
316     public void setSkipLocking( boolean skipLocking )
317     {
318         this.skipLocking = skipLocking;
319     }
320 }