/*
 * RHQ Management Platform
 * Copyright (C) 2005-2008 Red Hat, Inc.
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation version 2 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
package org.rhq.enterprise.server.util.concurrent;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class AvailabilityReportSerializer {
    private final Log log = LogFactory.getLog(AvailabilityReportSerializer.class);

    private static Map<String, ReentrantReadWriteLock> locks = new HashMap<String, ReentrantReadWriteLock>();
    private static Map<String, Long> lockTimes = Collections.synchronizedMap(new HashMap<String, Long>());
    private static AvailabilityReportSerializer singleton = new AvailabilityReportSerializer();

    public static AvailabilityReportSerializer getSingleton() {
        return singleton;
    }

    public void lock(String agentName) {
        String msg = "tid=" + Thread.currentThread().getId() + "; agent=" + agentName;
        boolean debug = this.log.isDebugEnabled();

        ReentrantReadWriteLock lock = null;
        logDebug(debug, msg, ": about to synchronize");
        synchronized (this) {
            logDebug(debug, msg, ": synchronized");
            lock = AvailabilityReportSerializer.locks.get(agentName);
            if (lock == null) {
                logDebug(debug, msg, ": creating new lock");
                lock = new ReentrantReadWriteLock();
                AvailabilityReportSerializer.locks.put(agentName, lock);
            }
        }

        logDebug(debug, msg, ": acquiring write lock");
        long start = System.currentTimeMillis();
        lock.writeLock().lock();
        long end = System.currentTimeMillis();
        long duration = end - start;
        AvailabilityReportSerializer.lockTimes.put(agentName, Long.valueOf(end));
        if (duration < 5000L) {
            logDebug(debug, msg, ": acquired write lock in millis=" + duration);
        } else {
            this.log.info(msg + ": acquired write lock in millis=" + duration);
        }

        return;
    }

    public void unlock(String agentName) {
        String msg = "tid=" + Thread.currentThread().getId() + "; agent=" + agentName;
        boolean debug = this.log.isDebugEnabled();

        ReentrantReadWriteLock lock = AvailabilityReportSerializer.locks.get(agentName);

        if (lock != null) {
            Long lockedTime = AvailabilityReportSerializer.lockTimes.get(agentName);
            long duration = System.currentTimeMillis() - ((lockedTime != null) ? lockedTime : Long.MAX_VALUE);

            if (duration < 5000L) {
                logDebug(debug, msg, ": releasing write lock after being locked for millis=" + duration);
            } else {
                this.log.info(msg + ": releasing write lock after being locked for millis=" + duration);
            }

            lock.writeLock().unlock();
            logDebug(debug, msg, ": released write lock");
        } else {
            this.log.warn(msg + ": cannot release write lock");
        }

        return;
    }

    private void logDebug(boolean enabled, String arg1, String arg2) {
        if (enabled) {
            this.log.debug(arg1 + arg2);
        }
    }
}