/*
 * Copyright (c) 2005- Shinji Kashihara.
 * All rights reserved. This program are made available under
 * the terms of the Eclipse Public License v1.0 which accompanies
 * this distribution, and is available at epl-v10.html.
 */
package jp.sourceforge.mergedoc.pleiades.aspect;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;

import jp.sourceforge.mergedoc.pleiades.Pleiades;
import jp.sourceforge.mergedoc.pleiades.log.Logger;

/**
 * 排他ロック・ユーティリティーです。
 * ロックは 1 つの Pleiades で 1 つ存在し、プロセス間の排他ロックを提供します。
 * <p>
 * @author cypher256
 */
public class Locks {

	/** ロガー */
	private static final Logger log = Logger.getLogger(Locks.class);

	/** ロック・ファイル */
	private static final File lockFile = Pleiades.getResourceFile(".lock");

	/** ロック・ファイル・チャンネル */
    private static volatile FileChannel fileChannel;

	static {
		try {
			lockFile.createNewFile();
		} catch (IOException e) {
			log.error(e, "排他ロック・ファイルの作成に失敗しました。");
		}
	}

	/**
	 * プロセスを排他ロックします。
	 * <pre>
	 * ・このプロセス内ですでにロックされている場合は何も行いません。
	 * ・別プロセスでロックされている場合は一定時間待機します。
	 * </pre>
	 */
	public static void lock() {

		if (fileChannel != null) {
			log.debug("すでにこのプロセス内でロックされています。");
			return;
		}
		log.debug("排他ロックします。");

		RandomAccessFile raf = null;
		try {
			raf = new RandomAccessFile(lockFile.getPath(), "rw");
			fileChannel = raf.getChannel();

			FileLock lock = fileChannel.tryLock();
			for (int i = 0; i < 10 && lock == null; i++) {
				log.debug("排他ロック待機中...");
				Thread.sleep(i * 1000);
				lock = fileChannel.tryLock();
			}
			if (lock == null) {
				log.warn("プロセス間の排他ロックを無視して処理を続行します。");
			}

		} catch (IOException e) {
			throw new IllegalStateException("排他ロックに失敗しました。", e);

		} catch (InterruptedException e) {
			throw new IllegalStateException("排他ロックに失敗しました。", e);

		} finally {
			if (raf != null) {
				try {
					raf.close();
				} catch (IOException e) {
					throw new IllegalStateException("排他ロックのクローズに失敗しました。", e);
				}
			}
		}
	}

	/**
	 * プロセスの排他ロックを解除します。
	 * <pre>
	 * ・このプロセス内でロックされていない場合は何も行いません。
	 * </pre>
	 */
	public static void release() {

		try {
			if (fileChannel != null) {

				log.debug("排他ロックを解除します。");
				fileChannel.close();
				fileChannel = null;
			}
		} catch (IOException e) {
			throw new IllegalStateException("排他ロック解除に失敗しました。", e);
		}
	}
}
