This project has retired. For details please refer to its Attic page.
DefaultFileLockManager xref
View Javadoc
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.lang3.time.StopWatch;
23  import org.slf4j.Logger;
24  import org.slf4j.LoggerFactory;
25  import org.springframework.stereotype.Service;
26  
27  import java.io.FileNotFoundException;
28  import java.io.IOException;
29  import java.nio.channels.ClosedChannelException;
30  import java.nio.file.Files;
31  import java.nio.file.NoSuchFileException;
32  import java.nio.file.Path;
33  import java.util.concurrent.ConcurrentHashMap;
34  import java.util.concurrent.ConcurrentMap;
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<Path, Lock> lockFiles = new ConcurrentHashMap<Path, Lock>( 64 );
48  
49      private boolean skipLocking = true;
50  
51      private Logger log = LoggerFactory.getLogger( getClass() );
52  
53      private int timeout = 0;
54  
55  
56      @Override
57      public Lock readFileLock( Path file )
58          throws FileLockException, FileLockTimeoutException
59      {
60          if ( skipLocking )
61          {
62              return new Lock( file );
63  
64          }
65          StopWatch stopWatch = new StopWatch();
66          boolean acquired = false;
67          try {
68              mkdirs(file.getParent());
69          } catch (IOException e) {
70              throw new FileLockException("Could not create directories "+file.getParent(), e);
71          }
72  
73          Lock lock = null;
74  
75          stopWatch.start();
76  
77          while ( !acquired )
78          {
79              // Make sure that not a bad lock is returned, if a exception was thrown.
80              lock = null;
81  
82              if ( timeout > 0 )
83              {
84                  long delta = stopWatch.getTime();
85                  log.debug( "delta {}, timeout {}", delta, timeout );
86                  if ( delta > timeout )
87                  {
88                      log.warn( "Cannot acquire read lock within {} millis. Will skip the file: {}", timeout, file );
89                      // we could not get the lock within the timeout period, so  throw  FileLockTimeoutException
90                      throw new FileLockTimeoutException();
91                  }
92              }
93  
94              Lock current = lockFiles.get( file );
95  
96              if ( current != null )
97              {
98                  log.trace( "read lock file exist continue wait" );
99                  continue;
100             }
101 
102             try
103             {
104                 lock = new Lock( file, false );
105                 createNewFileQuietly( file );
106                 lock.openLock( false, timeout > 0 );
107                 // We are not returning an existing lock. If the lock is not
108                 // exclusive, another thread may release the lock and the client
109                 // knows nothing about it.
110                 // The only atomic operation is the putIfAbsent operation, so if
111                 // this returns null everything is OK, otherwise we should start at
112                 // the beginning.
113                 current = lockFiles.putIfAbsent( file, lock );
114                 if ( current == null )
115                 {
116                     // Success
117                     acquired = true;
118                 } else {
119                     // We try again
120                     lock.close();
121                     lock=null;
122                 }
123             }
124             catch ( FileNotFoundException | NoSuchFileException e )
125             {
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( Path file )
147         throws FileLockException, FileLockTimeoutException
148     {
149         if ( skipLocking )
150         {
151             return new Lock( file );
152         }
153 
154         try {
155             mkdirs( file.getParent() );
156         } catch (IOException e) {
157             throw new FileLockException("Could not create directory "+file.getParent(), e);
158         }
159 
160         StopWatch stopWatch = new StopWatch();
161         boolean acquired = false;
162 
163         Lock lock = null;
164 
165         stopWatch.start();
166 
167         while ( !acquired )
168         {
169             // Make sure that not a bad lock is returned, if a exception was thrown.
170             lock = null;
171             if ( timeout > 0 )
172             {
173                 long delta = stopWatch.getTime();
174                 log.debug( "delta {}, timeout {}", delta, timeout );
175                 if ( delta > timeout )
176                 {
177                     log.warn( "Cannot acquire read lock within {} millis. Will skip the file: {}", timeout, file );
178                     // we could not get the lock within the timeout period, so throw FileLockTimeoutException
179                     throw new FileLockTimeoutException();
180                 }
181             }
182 
183             Lock current = lockFiles.get( file );
184 
185             try
186             {
187 
188                 if ( current != null )
189                 {
190                     log.trace( "write lock file exist continue wait" );
191 
192                     continue;
193                 }
194                 lock = new Lock( file, true );
195                 createNewFileQuietly( file );
196                 lock.openLock( true, timeout > 0 );
197                 // We are not returning an existing lock. If the lock is not
198                 // exclusive, another thread may release the lock and the client
199                 // knows nothing about it.
200                 // The only atomic operation is the putIfAbsent operation, so if
201                 // this returns null everything is OK, otherwise we should start at
202                 // the beginning.
203                 current = lockFiles.putIfAbsent( file, lock );
204                 if ( current == null )
205                 {
206                     // Success
207                     acquired = true;
208                 } else {
209                     // We try again
210                     lock.close();
211                     lock=null;
212                 }
213             }
214             catch ( FileNotFoundException | NoSuchFileException e )
215             {
216 
217                 log.debug( "write Lock skip: {} try to create file", e.getMessage() );
218                 createNewFileQuietly( file );
219             }
220             catch ( IOException e )
221             {
222                 throw new FileLockException( e.getMessage(), e );
223             }
224             catch ( IllegalStateException e )
225             {
226                 log.trace( "openLock {}:{}", e.getClass(), e.getMessage() );
227             }
228         }
229 
230         return lock;
231 
232 
233     }
234 
235      private void createNewFileQuietly( Path file )
236     {
237         try
238         {
239             Files.createFile(file);
240         }
241         catch ( IOException e )
242         {
243             // skip that
244         }
245     }
246 
247     @Override
248     public void release( Lock lock )
249         throws FileLockException
250     {
251         if ( lock == null )
252         {
253             log.debug( "skip releasing null" );
254             return;
255         }
256         if ( skipLocking )
257         {
258             return;
259         }
260         try
261         {
262             lockFiles.remove( lock.getFile() );
263             lock.close();
264         }
265         catch ( ClosedChannelException e )
266         {
267             // skip this one
268             log.debug( "ignore ClosedChannelException: {}", e.getMessage() );
269         }
270         catch ( IOException e )
271         {
272             throw new FileLockException( e.getMessage(), e );
273         }
274     }
275 
276     @Override
277     public void clearLockFiles()
278     {
279         lockFiles.clear();
280     }
281 
282     private Path mkdirs( Path directory ) throws IOException {
283         return Files.createDirectories(directory);
284     }
285 
286     @Override
287     public int getTimeout()
288     {
289         return timeout;
290     }
291 
292     @Override
293     public void setTimeout( int timeout )
294     {
295         this.timeout = timeout;
296     }
297 
298     @Override
299     public boolean isSkipLocking()
300     {
301         return skipLocking;
302     }
303 
304     @Override
305     public void setSkipLocking( boolean skipLocking )
306     {
307         this.skipLocking = skipLocking;
308     }
309 }