001 /*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License"). You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at
010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012 * See the License for the specific language governing permissions
013 * and limitations under the License.
014 *
015 * When distributing Covered Code, include this CDDL HEADER in each
016 * file and include the License file at
017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
018 * add the following below this CDDL HEADER, with the fields enclosed
019 * by brackets "[]" replaced with your own identifying information:
020 * Portions Copyright [yyyy] [name of copyright owner]
021 *
022 * CDDL HEADER END
023 *
024 *
025 * Copyright 2006-2008 Sun Microsystems, Inc.
026 */
027 package org.opends.server.core;
028
029
030
031 import java.io.File;
032 import java.io.RandomAccessFile;
033 import java.nio.channels.FileChannel;
034 import java.nio.channels.FileLock;
035 import java.util.HashMap;
036
037 import org.opends.server.api.Backend;
038 import org.opends.server.loggers.debug.DebugTracer;
039 import org.opends.server.types.DebugLogLevel;
040
041 import static org.opends.messages.CoreMessages.*;
042 import static org.opends.server.loggers.debug.DebugLogger.*;
043 import static org.opends.server.util.ServerConstants.*;
044 import static org.opends.server.util.StaticUtils.*;
045
046
047
048 /**
049 * This class provides a mechanism for allowing the Directory Server to utilize
050 * file locks as provided by the underlying OS. File locks may be exclusive or
051 * shared, and will be visible between different processes on the same system.
052 */
053 public class LockFileManager
054 {
055 /**
056 * The tracer object for the debug logger.
057 */
058 private static final DebugTracer TRACER = getTracer();
059
060
061
062
063 // A map between the filenames and the lock files for exclusive locks.
064 private static HashMap<String,FileLock> exclusiveLocks =
065 new HashMap<String,FileLock>();
066
067 // A map between the filenames and the lock files for shared locks.
068 private static HashMap<String,FileLock> sharedLocks =
069 new HashMap<String,FileLock>();
070
071 // A map between the filenames and reference counts for shared locks.
072 private static HashMap<String,Integer> sharedLockReferences =
073 new HashMap<String,Integer>();
074
075 // The lock providing threadsafe access to the lock map data.
076 private static Object mapLock = new Object();
077
078
079
080 /**
081 * Attempts to acquire a shared lock on the specified file.
082 *
083 * @param lockFile The file for which to obtain the shared lock.
084 * @param failureReason A buffer that can be used to hold a reason that the
085 * lock could not be acquired.
086 *
087 * @return <CODE>true</CODE> if the lock was obtained successfully, or
088 * <CODE>false</CODE> if it could not be obtained.
089 */
090 public static boolean acquireSharedLock(String lockFile,
091 StringBuilder failureReason)
092 {
093 synchronized (mapLock)
094 {
095 // Check to see if there's already an exclusive lock on the file. If so,
096 // then we can't get a shared lock on it.
097 if (exclusiveLocks.containsKey(lockFile))
098 {
099
100 failureReason.append(
101 ERR_FILELOCKER_LOCK_SHARED_REJECTED_BY_EXCLUSIVE.get(lockFile));
102 return false;
103 }
104
105
106 // Check to see if we already hold a shared lock on the file. If so, then
107 // increase its refcount and return true.
108 FileLock sharedLock = sharedLocks.get(lockFile);
109 if (sharedLock != null)
110 {
111 int numReferences = sharedLockReferences.get(lockFile);
112 numReferences++;
113 sharedLockReferences.put(lockFile, numReferences);
114 return true;
115 }
116
117
118 // We don't hold a lock on the file so we need to create it. First,
119 // create the file only if it doesn't already exist.
120 File f = getFileForPath(lockFile);
121 try
122 {
123 if (! f.exists())
124 {
125 f.createNewFile();
126 }
127 }
128 catch (Exception e)
129 {
130 if (debugEnabled())
131 {
132 TRACER.debugCaught(DebugLogLevel.ERROR, e);
133 }
134
135 failureReason.append(
136 ERR_FILELOCKER_LOCK_SHARED_FAILED_CREATE.get(lockFile,
137 getExceptionMessage(e)));
138 return false;
139 }
140
141
142 // Open the file for reading and get the corresponding file channel.
143 FileChannel channel = null;
144 RandomAccessFile raf = null;
145 try
146 {
147 raf = new RandomAccessFile(lockFile, "r");
148 channel = raf.getChannel();
149 }
150 catch (Exception e)
151 {
152 if (debugEnabled())
153 {
154 TRACER.debugCaught(DebugLogLevel.ERROR, e);
155 }
156
157 failureReason.append(ERR_FILELOCKER_LOCK_SHARED_FAILED_OPEN.get(
158 lockFile, getExceptionMessage(e)));
159
160 if (raf != null)
161 {
162 try
163 {
164 raf.close();
165 }
166 catch (Throwable t)
167 {
168 }
169 }
170 return false;
171 }
172
173
174 // Try to obtain a shared lock on the file channel.
175 FileLock fileLock;
176 try
177 {
178 fileLock = channel.tryLock(0L, Long.MAX_VALUE, true);
179 }
180 catch (Exception e)
181 {
182 if (debugEnabled())
183 {
184 TRACER.debugCaught(DebugLogLevel.ERROR, e);
185 }
186
187 failureReason.append(
188 ERR_FILELOCKER_LOCK_SHARED_FAILED_LOCK.get(
189 lockFile, getExceptionMessage(e)));
190 if (channel != null)
191 {
192 try
193 {
194 channel.close();
195 }
196 catch (Throwable t)
197 {
198 }
199 }
200 if (raf != null)
201 {
202 try
203 {
204 raf.close();
205 }
206 catch (Throwable t)
207 {
208 }
209 }
210 return false;
211 }
212
213
214 // If we could not get the lock, then return false. Otherwise, put it in
215 // the shared lock table with a reference count of 1 and return true.
216 if (fileLock == null)
217 {
218 failureReason.append(
219 ERR_FILELOCKER_LOCK_SHARED_NOT_GRANTED.get(lockFile));
220 if (channel != null)
221 {
222 try
223 {
224 channel.close();
225 }
226 catch (Throwable t)
227 {
228 }
229 }
230 if (raf != null)
231 {
232 try
233 {
234 raf.close();
235 }
236 catch (Throwable t)
237 {
238 }
239 }
240 return false;
241 }
242 else
243 {
244 sharedLocks.put(lockFile, fileLock);
245 sharedLockReferences.put(lockFile, 1);
246 return true;
247 }
248 }
249 }
250
251
252
253 /**
254 * Attempts to acquire an exclusive lock on the specified file.
255 *
256 * @param lockFile The file for which to obtain the exclusive lock.
257 * @param failureReason A buffer that can be used to hold a reason that the
258 * lock could not be acquired.
259 *
260 * @return <CODE>true</CODE> if the lock was obtained successfully, or
261 * <CODE>false</CODE> if it could not be obtained.
262 */
263 public static boolean acquireExclusiveLock(String lockFile,
264 StringBuilder failureReason)
265 {
266 synchronized (mapLock)
267 {
268 // Check to see if there's already an exclusive lock on the file. If so,
269 // then we can't get another exclusive lock on it.
270 if (exclusiveLocks.containsKey(lockFile))
271 {
272 failureReason.append(
273 ERR_FILELOCKER_LOCK_EXCLUSIVE_REJECTED_BY_EXCLUSIVE.get(
274 lockFile));
275 return false;
276 }
277
278
279 // Check to see if we already hold a shared lock on the file. If so, then
280 // we can't get an exclusive lock on it.
281 if (sharedLocks.containsKey(lockFile))
282 {
283 failureReason.append(
284 ERR_FILELOCKER_LOCK_EXCLUSIVE_REJECTED_BY_SHARED.get(lockFile));
285 return false;
286 }
287
288
289 // We don't hold a lock on the file so we need to create it. First,
290 // create the file only if it doesn't already exist.
291 File f = getFileForPath(lockFile);
292 try
293 {
294 if (! f.exists())
295 {
296 f.createNewFile();
297 }
298 }
299 catch (Exception e)
300 {
301 if (debugEnabled())
302 {
303 TRACER.debugCaught(DebugLogLevel.ERROR, e);
304 }
305 failureReason.append(
306 ERR_FILELOCKER_LOCK_EXCLUSIVE_FAILED_CREATE.get(lockFile,
307 getExceptionMessage(e)));
308 return false;
309 }
310
311
312 // Open the file read+write and get the corresponding file channel.
313 FileChannel channel = null;
314 RandomAccessFile raf = null;
315 try
316 {
317 raf = new RandomAccessFile(lockFile, "rw");
318 channel = raf.getChannel();
319 }
320 catch (Exception e)
321 {
322 if (debugEnabled())
323 {
324 TRACER.debugCaught(DebugLogLevel.ERROR, e);
325 }
326
327 failureReason.append(ERR_FILELOCKER_LOCK_EXCLUSIVE_FAILED_OPEN.get(
328 lockFile, getExceptionMessage(e)));
329 if (raf != null)
330 {
331 try
332 {
333 raf.close();
334 }
335 catch (Throwable t)
336 {
337 }
338 }
339 return false;
340 }
341
342
343 // Try to obtain an exclusive lock on the file channel.
344 FileLock fileLock;
345 try
346 {
347 fileLock = channel.tryLock(0L, Long.MAX_VALUE, false);
348 }
349 catch (Exception e)
350 {
351 if (debugEnabled())
352 {
353 TRACER.debugCaught(DebugLogLevel.ERROR, e);
354 }
355
356 failureReason.append(
357 ERR_FILELOCKER_LOCK_EXCLUSIVE_FAILED_LOCK.get(lockFile,
358 getExceptionMessage(e)));
359 if (channel != null)
360 {
361 try
362 {
363 channel.close();
364 }
365 catch (Throwable t)
366 {
367 }
368 }
369 if (raf != null)
370 {
371 try
372 {
373 raf.close();
374 }
375 catch (Throwable t)
376 {
377 }
378 }
379
380 return false;
381 }
382
383
384 // If we could not get the lock, then return false. Otherwise, put it in
385 // the exclusive lock table and return true.
386 if (fileLock == null)
387 {
388 failureReason.append(
389 ERR_FILELOCKER_LOCK_EXCLUSIVE_NOT_GRANTED.get(lockFile));
390 if (channel != null)
391 {
392 try
393 {
394 channel.close();
395 }
396 catch (Throwable t)
397 {
398 }
399 }
400 if (raf != null)
401 {
402 try
403 {
404 raf.close();
405 }
406 catch (Throwable t)
407 {
408 }
409 }
410 return false;
411 }
412 else
413 {
414 exclusiveLocks.put(lockFile, fileLock);
415 return true;
416 }
417 }
418 }
419
420
421
422 /**
423 * Attempts to release the lock on the specified file. If an exclusive lock
424 * is held, then it will be released. If a shared lock is held, then its
425 * reference count will be reduced, and the lock will be released if the
426 * resulting reference count is zero. If we don't know anything about the
427 * requested file, then don't do anything.
428 *
429 * @param lockFile The file for which to release the associated lock.
430 * @param failureReason A buffer that can be used to hold information about
431 * a problem that occurred preventing the successful
432 * release.
433 *
434 * @return <CODE>true</CODE> if the lock was found and released successfully,
435 * or <CODE>false</CODE> if a problem occurred that might have
436 * prevented the lock from being released.
437 */
438 public static boolean releaseLock(String lockFile,
439 StringBuilder failureReason)
440 {
441 synchronized (mapLock)
442 {
443 // See if we hold an exclusive lock on the file. If so, then release it
444 // and get remove it from the lock table.
445 FileLock lock = exclusiveLocks.remove(lockFile);
446 if (lock != null)
447 {
448 try
449 {
450 lock.release();
451 }
452 catch (Exception e)
453 {
454 if (debugEnabled())
455 {
456 TRACER.debugCaught(DebugLogLevel.ERROR, e);
457 }
458
459 failureReason.append(
460 ERR_FILELOCKER_UNLOCK_EXCLUSIVE_FAILED_RELEASE.get(lockFile,
461 getExceptionMessage(e)));
462 return false;
463 }
464
465 try
466 {
467 lock.channel().close();
468 }
469 catch (Exception e)
470 {
471 if (debugEnabled())
472 {
473 TRACER.debugCaught(DebugLogLevel.ERROR, e);
474 }
475
476 // Even though we couldn't close the channel for some reason, this
477 // should still be OK because we released the lock above.
478 }
479
480 return true;
481 }
482
483
484 // See if we hold a shared lock on the file. If so, then reduce its
485 // refcount and release only if the resulting count is zero.
486 lock = sharedLocks.get(lockFile);
487 if (lock != null)
488 {
489 int refCount = sharedLockReferences.get(lockFile);
490 refCount--;
491 if (refCount <= 0)
492 {
493 sharedLocks.remove(lockFile);
494 sharedLockReferences.remove(lockFile);
495
496 try
497 {
498 lock.release();
499 }
500 catch (Exception e)
501 {
502 if (debugEnabled())
503 {
504 TRACER.debugCaught(DebugLogLevel.ERROR, e);
505 }
506
507 failureReason.append(ERR_FILELOCKER_UNLOCK_SHARED_FAILED_RELEASE
508 .get(lockFile, getExceptionMessage(e)));
509 return false;
510 }
511
512 try
513 {
514 lock.channel().close();
515 }
516 catch (Exception e)
517 {
518 if (debugEnabled())
519 {
520 TRACER.debugCaught(DebugLogLevel.ERROR, e);
521 }
522
523 // Even though we couldn't close the channel for some reason, this
524 // should still be OK because we released the lock above.
525 }
526 }
527 else
528 {
529 sharedLockReferences.put(lockFile, refCount);
530 }
531
532 return true;
533 }
534
535
536 // We didn't find a reference to the file. We'll have to return false
537 // since either we lost the reference or we're trying to release a lock
538 // we never had. Both of them are bad.
539 failureReason.append(ERR_FILELOCKER_UNLOCK_UNKNOWN_FILE.get(lockFile));
540 return false;
541 }
542 }
543
544
545
546 /**
547 * Retrieves the path to the directory that should be used to hold the lock
548 * files.
549 *
550 * @return The path to the directory that should be used to hold the lock
551 * files.
552 */
553 public static String getLockDirectoryPath()
554 {
555 File lockDirectory =
556 DirectoryServer.getEnvironmentConfig().getLockDirectory();
557 return lockDirectory.getAbsolutePath();
558 }
559
560
561
562 /**
563 * Retrieves the filename that should be used for the lock file for the
564 * Directory Server instance.
565 *
566 * @return The filename that should be used for the lock file for the
567 * Directory Server instance.
568 */
569 public static String getServerLockFileName()
570 {
571 StringBuilder buffer = new StringBuilder();
572 buffer.append(getLockDirectoryPath());
573 buffer.append(File.separator);
574 buffer.append(SERVER_LOCK_FILE_NAME);
575 buffer.append(LOCK_FILE_SUFFIX);
576
577 return buffer.toString();
578 }
579
580
581
582 /**
583 * Retrieves the filename that should be used for the lock file for the
584 * specified backend.
585 *
586 * @param backend The backend for which to retrieve the filename for the
587 * lock file.
588 *
589 * @return The filename that should be used for the lock file for the
590 * specified backend.
591 */
592 public static String getBackendLockFileName(Backend backend)
593 {
594 StringBuilder buffer = new StringBuilder();
595 buffer.append(getLockDirectoryPath());
596 buffer.append(File.separator);
597 buffer.append(BACKEND_LOCK_FILE_PREFIX);
598 buffer.append(backend.getBackendID());
599 buffer.append(LOCK_FILE_SUFFIX);
600
601 return buffer.toString();
602 }
603 }
604