]> source.dussan.org Git - archiva.git/blob
ba3ab2fa1bfcd7ae06e79091de33b4195e071f68
[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
67         Lock lock = null;
68
69         stopWatch.start();
70
71         while ( !acquired )
72         {
73
74             if ( timeout > 0 )
75             {
76                 long delta = stopWatch.getTime();
77                 log.debug( "delta {}, timeout {}", delta, timeout );
78                 if ( delta > timeout )
79                 {
80                     log.warn( "Cannot acquire read lock within {} millis. Will skip the file: {}", timeout, file );
81                     // we could not get the lock within the timeout period, so  throw  FileLockTimeoutException
82                     throw new FileLockTimeoutException();
83                 }
84             }
85
86             Lock current = lockFiles.get( file );
87
88             if ( current != null )
89             {
90                 log.debug( "read lock file exist continue wait" );
91                 continue;
92             }
93
94             try
95             {
96                 lock = new Lock( file, false );
97                 createNewFileQuietly( file );
98                 lock.openLock( false, timeout > 0 );
99                 acquired = true;
100             }
101             catch ( FileNotFoundException e )
102             {
103                 // can happen if an other thread has deleted the file
104                 // close RandomAccessFile!!!
105                 closeQuietly( lock.getRandomAccessFile() );
106                 log.debug( "read Lock skip: {} try to create file", e.getMessage() );
107                 createNewFileQuietly( file );
108             }
109             catch ( IOException e )
110             {
111                 throw new FileLockException( e.getMessage(), e );
112             }
113             catch ( IllegalStateException e )
114             {
115                 log.debug( "openLock {}:{}", e.getClass(), e.getMessage() );
116             }
117         }
118         Lock current = lockFiles.putIfAbsent( file, lock );
119         if ( current != null )
120         {
121             lock = current;
122         }
123         return lock;
124
125     }
126
127
128     @Override
129     public Lock writeFileLock( File file )
130         throws FileLockException, FileLockTimeoutException
131     {
132         if ( skipLocking )
133         {
134             return new Lock( file );
135         }
136
137         mkdirs( file.getParentFile() );
138
139         StopWatch stopWatch = new StopWatch();
140         boolean acquired = false;
141
142         Lock lock = null;
143
144         stopWatch.start();
145
146         while ( !acquired )
147         {
148
149             if ( timeout > 0 )
150             {
151                 long delta = stopWatch.getTime();
152                 log.debug( "delta {}, timeout {}", delta, timeout );
153                 if ( delta > timeout )
154                 {
155                     log.warn( "Cannot acquire read lock within {} millis. Will skip the file: {}", timeout, file );
156                     // we could not get the lock within the timeout period, so throw FileLockTimeoutException
157                     throw new FileLockTimeoutException();
158                 }
159             }
160
161             Lock current = lockFiles.get( file );
162
163             try
164             {
165
166                 if ( current != null )
167                 {
168                     log.debug( "write lock file exist continue wait" );
169
170                     continue;
171                 }
172                 lock = new Lock( file, true );
173                 createNewFileQuietly( file );
174                 lock.openLock( true, timeout > 0 );
175                 acquired = true;
176             }
177             catch ( FileNotFoundException e )
178             {
179                 // can happen if an other thread has deleted the file
180                 // close RandomAccessFile!!!
181                 closeQuietly( lock.getRandomAccessFile() );
182
183                 log.debug( "write Lock skip: {} try to create file", e.getMessage() );
184                 createNewFileQuietly( file );
185             }
186             catch ( IOException e )
187             {
188                 throw new FileLockException( e.getMessage(), e );
189             }
190             catch ( IllegalStateException e )
191             {
192                 log.debug( "openLock {}:{}", e.getClass(), e.getMessage() );
193             }
194         }
195
196         Lock current = lockFiles.putIfAbsent( file, lock );
197         if ( current != null )
198         {
199             lock = current;
200         }
201
202         return lock;
203
204
205     }
206
207     private void closeQuietly( RandomAccessFile randomAccessFile )
208     {
209         if ( randomAccessFile == null )
210         {
211             return;
212         }
213
214         try
215         {
216             randomAccessFile.close();
217         }
218         catch ( IOException e )
219         {
220             // ignore
221         }
222     }
223
224     private void createNewFileQuietly( File file )
225     {
226         try
227         {
228             file.createNewFile();
229         }
230         catch ( IOException e )
231         {
232             // skip that
233         }
234     }
235
236     @Override
237     public void release( Lock lock )
238         throws FileLockException
239     {
240         if ( lock == null )
241         {
242             log.debug( "skip releasing null" );
243             return;
244         }
245         if ( skipLocking )
246         {
247             return;
248         }
249         try
250         {
251             lockFiles.remove( lock.getFile() );
252             lock.close();
253         }
254         catch ( IOException e )
255         {
256             throw new FileLockException( e.getMessage(), e );
257         }
258     }
259
260     public void clearLockFiles()
261     {
262         lockFiles.clear();
263     }
264
265     private boolean mkdirs( File directory )
266     {
267         if ( directory == null )
268         {
269             return false;
270         }
271
272         if ( directory.exists() )
273         {
274             return false;
275         }
276         if ( directory.mkdir() )
277         {
278             return true;
279         }
280
281         File canonDir = null;
282         try
283         {
284             canonDir = directory.getCanonicalFile();
285         }
286         catch ( IOException e )
287         {
288             return false;
289         }
290
291         File parentDir = canonDir.getParentFile();
292         return ( parentDir != null && ( mkdirs( parentDir ) || parentDir.exists() ) && canonDir.mkdir() );
293     }
294
295     public int getTimeout()
296     {
297         return timeout;
298     }
299
300     public void setTimeout( int timeout )
301     {
302         this.timeout = timeout;
303     }
304
305     public boolean isSkipLocking()
306     {
307         return skipLocking;
308     }
309
310     public void setSkipLocking( boolean skipLocking )
311     {
312         this.skipLocking = skipLocking;
313     }
314 }