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