/******************************************************************************* * Copyright (c) 2005-2011, G. Weirich and Elexis * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * G. Weirich - initial implementation * *******************************************************************************/ package ch.rgw.io; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import ch.rgw.tools.ExHandler; import ch.rgw.tools.TimeTool; /** * A class that creates and detects lockfile to provide cooperative detection od running instances * of an applicatiion (or for other purposes) * * @author gerry * */ public class LockFile { File baseDir; String baseName; int maxNum; int timeOutSeconds; /** * Create an instance of a LockFile class. This will not yet create an actual file. A call to * LockFile#lock() will create a file in the given dir and with the given name and a suffix * ranging from 1 to maxNum. * * @param dir * the dir where the lockfile(s) should be created * @param basename * the basename for the files * @param maxNum * maximum number of instances of this lock that may be aquired simultaneously * @param timeoutSeconds * after what time will a lock treaded as invalid */ public LockFile(File dir, String basename, int maxNum, int timeoutSeconds){ baseDir = dir; baseName = basename; this.maxNum = maxNum; this.timeOutSeconds = timeoutSeconds; } /** * create a lockfile with the parameters as given to the constructor. There will be created at * most maxNUm lockfiles. Each of them will expire after timeoutSeconds, or after the * application exits. If the application does not exist normally, the lockfile might not be * deleted. It will the be considered valid until the expire time is reached. * * @return lock number on success, 0 if there are already maxNum lockfiles and the lock could * not be aquired * @throws IOException * id something went wrong */ public int lock() throws IOException{ int n = 1; while (n <= maxNum) { File file = new File(baseDir, constructFilename(n)); if (!isLockValid(file)) { if (createLockfile(file)) { return n; } } n++; } return 0; } private boolean isLockValid(File file) throws IOException{ if (!file.exists()) { return false; } if (!file.canWrite()) { throw new IOException(("Can't write " + file.getAbsolutePath())); } TimeTool now = new TimeTool(); DataInputStream dais = new DataInputStream(new FileInputStream(file)); String ts = dais.readUTF(); TimeTool tt = new TimeTool(); dais.close(); if (tt.set(ts)) { if (tt.secondsTo(now) > timeOutSeconds) { if (file.delete()) { return false; } } else { return true; } } if (file.delete()) { return false; } throw (new IOException("Can not delete " + file.getAbsolutePath())); } private boolean createLockfile(File file) throws IOException{ if (!file.createNewFile()) { return false; } file.deleteOnExit(); DataOutputStream daos = new DataOutputStream(new FileOutputStream(file)); daos.writeUTF(new TimeTool().toString(TimeTool.FULL_ISO)); daos.close(); return true; } private String constructFilename(int n){ return new StringBuilder().append(baseName).append(".").append(Integer.toString(n)) .toString(); } /** * Refresh the lock i.e. extend its validity time by the original timeout once again. An * application should prefer this update mechanism over a too long timeout, because in case of * abnormal termination, the lock does not stay too long active. * * @param n * number of the lockfile (as received by the lock() call) * @return true on success */ public boolean updateLock(int n){ File file = new File(baseDir, constructFilename(n)); if (!file.exists()) { return false; } try { DataOutputStream daos = new DataOutputStream(new FileOutputStream(file)); daos.writeUTF(new TimeTool().toString(TimeTool.FULL_ISO)); daos.close(); return true; } catch (Exception ex) { ExHandler.handle(ex); return false; } } /** * Check if at least one lockfile with the given pattern exists * * @return true if one ore more lockfiles exist * @throws IOException */ public boolean existsLock() throws IOException{ int n = 1; while (n <= maxNum) { File file = new File(baseDir, constructFilename(n)); if (isLockValid(file)) { return true; } } return false; } }