/* * ProActive Parallel Suite(TM): * The Open Source library for parallel and distributed * Workflows & Scheduling, Orchestration, Cloud Automation * and Big Data Analysis on Enterprise Grids & Clouds. * * Copyright (c) 2007 - 2017 ActiveEon * Contact: contact@activeeon.com * * This library is free software: you can redistribute it and/or * modify it under the terms of the GNU Affero General Public License * as published by the Free Software Foundation: version 3 of * the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * If needed, contact us to obtain a release under GPL Version 2 or 3 * or a different license than the AGPL. */ package org.ow2.proactive.scheduler.util; import java.io.IOException; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardWatchEventKinds; import java.nio.file.WatchEvent; import java.nio.file.WatchKey; import java.nio.file.WatchService; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; /** * FileLock is in charge to provide a mean for two or more processes * to synchronize based on the existence of a file. */ public class FileLock { private static final String FILE_PREFIX = "pa-file-lock-"; private final String nameSuffix; private Path tmpFile; private boolean isLocked; public FileLock() throws IOException { this(""); } public FileLock(String nameSuffix) throws IOException { this.isLocked = false; this.nameSuffix = nameSuffix; Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { if (tmpFile != null && Files.exists(tmpFile)) { try { Files.delete(tmpFile); } catch (IOException e) { e.printStackTrace(); } } } }); } public Path lock() throws IOException { if (isLocked) { throw new IllegalStateException("Lock already acquired: " + tmpFile); } isLocked = true; tmpFile = Files.createTempFile(FILE_PREFIX, nameSuffix); return tmpFile; } public void unlock() throws IOException { if (isLocked) { unlock(tmpFile); isLocked = false; } } public static void unlock(String file) throws IOException { unlock(Paths.get(file)); } public static void unlock(Path file) throws IOException { if (Files.exists(file)) { Files.delete(file); } } public boolean isLocked() { return isLocked; } public static void waitUntilUnlocked(String lockFilePath) throws InterruptedException, ExecutionException, IOException { waitUntilUnlocked(Paths.get(lockFilePath)); } public static void waitUntilUnlocked(Path lockFile) throws InterruptedException, IOException, ExecutionException { if (!lockFile.getFileName().toString().startsWith(FILE_PREFIX)) { throw new IllegalArgumentException("The specified path to file is probably incorrect: " + lockFile); } while (Files.exists(lockFile)) { TimeUnit.MILLISECONDS.sleep(100); } // TODO: investigate why FileWatcher is not working as expected // FileWatcher fileWatcher = new FileWatcher(lockFile); // Thread thread = new Thread(fileWatcher); // thread.start(); // thread.join(); } public static class FileWatcher implements Runnable { private volatile boolean running; private final WatchService watcher; private final Path lockFile; public FileWatcher(Path lockFile) throws IOException { this.lockFile = lockFile; this.running = true; this.watcher = FileSystems.getDefault().newWatchService(); lockFile.getParent().register(watcher, StandardWatchEventKinds.ENTRY_DELETE); } @Override public void run() { while (running) { WatchKey key; try { key = watcher.take(); } catch (InterruptedException ex) { return; } for (WatchEvent<?> event : key.pollEvents()) { WatchEvent.Kind<?> kind = event.kind(); @SuppressWarnings("unchecked") WatchEvent<Path> ev = (WatchEvent<Path>) event; Path fileName = ev.context(); if (kind == StandardWatchEventKinds.OVERFLOW) { continue; } else if (kind == StandardWatchEventKinds.ENTRY_DELETE) { if (fileName.equals(this.lockFile.getFileName())) { running = false; } } } boolean valid = key.reset(); if (!valid) { break; } } try { watcher.close(); } catch (IOException e) { e.printStackTrace(); } } } }