All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.openl.rules.lock.Lock Maven / Gradle / Ivy
package org.openl.rules.lock;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.Writer;
import java.nio.file.AccessDeniedException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.FileTime;
import java.time.Instant;
import java.util.HashMap;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.openl.util.CollectionUtils;
import org.openl.util.FileUtils;
import org.openl.util.PropertiesUtils;
/**
* Shareable, file based locking system.
*
* @author Yury Molchan
*/
public class Lock {
private static final Logger LOG = LoggerFactory.getLogger(Lock.class);
private static final String READY_LOCK = "ready.lock";
// lock info
private static final String USER_NAME = "user";
private static final String DATE = "date";
private final Path locksLocation;
private final Path lockPath;
Lock(Path locksLocation, String lockId) {
this.locksLocation = locksLocation;
this.lockPath = locksLocation.resolve(lockId);
}
public boolean tryLock(String lockedBy) {
LockInfo info = getInfo();
if (info.isLocked()) {
// If lockedBy is empty, will return false. Cannot lock second time with empty user.
return !info.getLockedBy().isEmpty() && info.getLockedBy().equals(lockedBy);
}
boolean lockAcquired = false;
Path prepareLock = null;
try {
prepareLock = createLockFile(lockedBy);
if (prepareLock != null) {
lockAcquired = finishLockCreating(prepareLock);
}
} catch (Exception e) {
LOG.info("Failure to create a lock file '{}'. Because of {} : {}",
lockPath,
e.getClass().getName(),
e.getMessage());
} finally {
if (!lockAcquired) {
// Delete because of it loos lock
deleteLockAndFolders(prepareLock);
}
}
return lockAcquired;
}
public boolean tryLock(String lockedBy, long time, TimeUnit unit) {
long millisTimeout = unit.toMillis(time);
long deadline = System.currentTimeMillis() + millisTimeout;
boolean result = tryLock(lockedBy);
while (!result && !Thread.currentThread().isInterrupted()) {
long restTime = deadline - System.currentTimeMillis();
if (restTime <= 0) {
// No time for waiting! Exit.
break;
}
// 1 is guaranty non zero sleep time
long sleepTime = Math.min(restTime / 10 + 1, ThreadLocalRandom.current().nextLong(20, 1000));
try {
TimeUnit.MILLISECONDS.sleep(sleepTime);
result = tryLock(lockedBy);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
// Thread is interrupted. Quit the loop.
break;
}
}
return result;
}
public boolean forceLock(String lockedBy, long timeToLive, TimeUnit unit) {
boolean result = tryLock(lockedBy, timeToLive, unit);
if (!result && !Thread.currentThread().isInterrupted()) {
LockInfo info = getInfo();
String message = "Too much time after the lock file has been created. Seems the lock file is never gonna be unlocked. Try to unlock it by ourselves.\n" + "Lock path: {}\n" + "Locked at: {}\n" + "Locked by: {}\n" + "Time to live: {} {}";
LOG.warn(message, lockPath, info.getLockedAt(), info.getLockedBy(), timeToLive, unit);
forceUnlock();
result = tryLock(lockedBy);
}
return result;
}
public void unlock() {
unlock(false);
}
public void forceUnlock() {
unlock(true);
}
private void unlock(boolean force) {
try {
if (force) {
FileUtils.delete(lockPath);
} else {
Files.deleteIfExists(lockPath.resolve(READY_LOCK));
}
deleteEmptyParentFolders();
} catch (FileNotFoundException ignored) {
// Ignored
// It was already deleted
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
private void deleteEmptyParentFolders() {
File file = lockPath.toFile();
while (!file.equals(locksLocation.toFile()) && file.delete()) {
file = file.getParentFile();
}
}
public LockInfo info() {
return getInfo();
}
private LockInfo getInfo() {
Path lock = lockPath.resolve(READY_LOCK);
if (!Files.isRegularFile(lock)) {
return LockInfo.NO_LOCK;
}
var properties = new HashMap();
try {
PropertiesUtils.load(lock, properties::put);
String userName = properties.get(USER_NAME);
String stringDate = properties.get(DATE);
Instant date;
try {
date = Instant.parse(stringDate);
} catch (Exception e) {
date = Instant.ofEpochMilli(0);
LOG.warn("Failed to parse date '{}'.", stringDate, e);
}
return new LockInfo(date, userName);
} catch (NoSuchFileException e) {
// Lock can be deleted in another thread
return LockInfo.NO_LOCK;
} catch (IOException e) {
LOG.info("Impossible to read the lock file.", e);
// Lock file exists but failed to retrieve lock info.
return new LockInfo(Instant.now(), null);
}
}
Path createLockFile(String userName) {
String userNameHash = Integer.toString(userName.hashCode(), 24);
try {
Files.createDirectories(lockPath);
Path lock = lockPath.resolve(userNameHash + ".lock");
try (Writer os = Files.newBufferedWriter(lock, StandardOpenOption.CREATE_NEW)) {
os.write("#Lock info\n");
os.append("user=").append(userName).write('\n');
os.append("date=").append(Instant.now().toString()).write('\n');
} catch (FileAlreadyExistsException | AccessDeniedException | NoSuchFileException e) {
// Cannot create lock file
return null;
} catch (Exception e) {
// Lock file is created but with error. So delete it.
LOG.info("Lock file '{}' is created with errors. Lock file is deleted.", lock);
deleteLockAndFolders(lock);
return null;
}
return lock;
} catch (Exception e) {
return null;
}
}
boolean finishLockCreating(Path lock) throws IOException {
File[] files = lockPath.toFile().listFiles();
if (CollectionUtils.isEmpty(files)) {
// We assume that at this step we must have one current lock file in the folder at least.
// So, if there is an empty folder, then unlock is happened, and the lock file has been deleted.
return false;
}
try {
Path lockName = lock.getFileName();
FileTime current = Files.getLastModifiedTime(lock);
for (File file : files) {
Path anotherName = file.toPath().getFileName();
FileTime another = Files.getLastModifiedTime(file.toPath());
if (current.compareTo(
another) > 0 || (current.compareTo(another) == 0 && lockName.compareTo(anotherName) > 0)) {
return false;
}
}
} catch (IOException e) {
return false;
}
Files.move(lock, lockPath.resolve(READY_LOCK));
return true;
}
private void deleteLockAndFolders(Path lock) {
try {
if (lock != null) {
Files.delete(lock);
}
deleteEmptyParentFolders();
} catch (Exception ex) {
LOG.error(ex.getMessage(), ex);
}
}
}