001/*
002 * jPOS Project [http://jpos.org]
003 * Copyright (C) 2000-2026 jPOS Software SRL
004 *
005 * This program is free software: you can redistribute it and/or modify
006 * it under the terms of the GNU Affero General Public License as
007 * published by the Free Software Foundation, either version 3 of the
008 * License, or (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
013 * GNU Affero General Public License for more details.
014 *
015 * You should have received a copy of the GNU Affero General Public License
016 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
017 */
018
019package org.jpos.q2.qbean;
020
021import java.io.File;
022import java.io.FileOutputStream;
023import java.io.RandomAccessFile;
024import java.nio.channels.FileChannel;
025import java.nio.channels.FileLock;
026
027import org.jpos.q2.QBeanSupport;
028
029/**
030 * QBean that enforces a single live Q2 instance by acquiring an exclusive
031 * file lock on a configured lockfile; a second instance fails to start when
032 * the file is already locked.
033 */
034public class QSingleInstanceFileBasedManager extends QBeanSupport {
035    /** Default constructor; no instance state to initialise. */
036    public QSingleInstanceFileBasedManager() {}
037
038    File             lockFile;
039    FileChannel      lockChannel;
040    FileLock         lock;
041    FileOutputStream lockFileOS;
042
043    /*
044     * (non-Javadoc)
045     *
046     * @see org.jpos.q2.QBeanSupport#initService()
047     */
048    @Override
049    protected void initService() throws Exception {
050
051        try {
052            lockFile = new File("./" + getServer().getDeployDir(), "instance.lock");
053            if (lockFile.exists()) {
054                // Either an orphan lock or lock from another instance. Orphan
055                // lock will get deleted, the latter will not get a lock on it.
056                lockFile.delete();
057            }
058            lockFileOS = new FileOutputStream(lockFile);
059            lockFileOS.close();
060            lockChannel = new RandomAccessFile(lockFile, "rw").getChannel();
061            lock = lockChannel.tryLock();
062            if (lock == null) {
063                throw new Exception("Unable to obtain lock");
064            }
065        }
066        catch (Exception e) {
067            getLog().error("An instance of Q2 is already running. Shutting this instance");
068            if (lock != null) {
069                lock.release();
070            }
071            if (lockChannel != null) {
072                lockChannel.close();
073            }
074            if (lockFile != null) {
075                lockFile.delete();
076            }
077            getServer().shutdown();
078        }
079
080    }
081
082    @Override
083    protected void stopService() throws Exception {
084
085        lock.release();
086        lockChannel.close();
087        lockFile.delete();
088    }
089
090}