/**
* High Availability Service (HA) for JOnAS
*
* Copyright (C) 2007,2008 Bull S.A.S.
* Copyright (C) 2006 Distributed Systems Lab.
* Universidad Politecnica de Madrid (Spain)
* Contact: http://lsd.ls.fi.upm.es/lsd
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*
* --------------------------------------------------------------------------
* $Id$
* --------------------------------------------------------------------------
*/
package org.ow2.jonas.lib.ejb21.ha;


import java.util.Stack;

import javax.ejb.EntityContext;

import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;
import org.ow2.cmi.ha.EntityBeanReference;
import org.ow2.cmi.ha.ReplicationManager;
import org.ow2.cmi.ha.RequestId;
import org.ow2.cmi.ha.SessionId;
import org.ow2.cmi.ha.StatefulBeanReference;
import org.ow2.cmi.ha.interceptor.HACurrent;
import org.ow2.jonas.lib.ejb21.JSessionSwitch;
import org.ow2.jonas.lib.ejb21.JStatefulSwitch;
import org.ow2.jonas.lib.ejb21.RequestCtx;
import org.ow2.jonas.lib.ejb21.jorm.JEntityContext;
import org.ow2.jonas.lib.util.Log;

/**
 * This class implements common function for SFSB replication.
 * @author Francisco Perez-Sorrosal (fpsorrosal@no-spam@fi.upm.es)
 * @author Alberto Paz-Jimenez (apaz@no-spam@fi.upm.es)
 */
public class JRepUtil {

    /**
     * Logger for traces
     */
    protected static Logger cmilogger = Log.getLogger("org.ow2.jonas.lib.ejb21.ha");

    static boolean hasBackupResponse(final RequestId requestId) {
        boolean result = false;

        if (cmilogger.isLoggable(BasicLevel.DEBUG)) {
            cmilogger.log(BasicLevel.DEBUG, " for: " + requestId);
        }

        try {
            // Only injects state during failover
            if (HACurrent.getHACurrent().isOnFailover()) {
                result = repMgr.hasBackupResponse(requestId);
            }
        } catch (Exception e) {
            cmilogger.log(BasicLevel.ERROR, "Unable to obtain the info", e);
        }

        return result;
    }

    /**
     * Injects the state of the bean if the cluster object id received in the
     * request is in the current node.
     * Only injects the state if we are on failover
     */
    static void injectState(final SessionId clusterOId, final JStatefulSwitch bs) {
        if (cmilogger.isLoggable(BasicLevel.DEBUG)) {
            cmilogger.log(BasicLevel.DEBUG, " for: " + clusterOId);
        }

        try {
            // Only injects state during failover
            if (HACurrent.getHACurrent().isOnFailover()) {
                StatefulBeanReference bean = new StatefulBeanRefImpl(bs);
                repMgr.restoreBeanChanges(clusterOId, bean);
            }
        } catch (Exception e) {
            cmilogger.log(BasicLevel.ERROR, "Unable to replicate the state", e);
        }
    }

    /**
     * Sets the clusterOId in the context, and if the bean does not implement
     * isModifiedMethod stores the state in the context
     * @param bs the session switch
     * @param clusterOId the cluster object Id
     * @param rctx the request context
     */
    static void preInvokeHook(final JSessionSwitch bs, final SessionId clusterOId, final RequestCtx rctx) {

    }

    static void postInvokeHook(final RequestCtx rctx, final SessionId clusterOId, final JSessionSwitch bs, final boolean remove) {
        if (cmilogger.isLoggable(BasicLevel.DEBUG)) {
            cmilogger.log(BasicLevel.DEBUG, " for: " + clusterOId);
        }

        // Create the rootId from the current HACtx/Thread association
        HACurrent current = HACurrent.getHACurrent();
        // Obtain the root request
        RequestId rootId = null;
        try {
            Stack<RequestId> requests = current.getRequests();
            if (requests != null) {
                rootId = requests.get(0);

                if (remove) {
                    addRemovedBean(clusterOId, rootId);
                } else {
                    addModifiedBean((JStatefulSwitch) bs, clusterOId, rootId);
                }
            }
        } catch (ArrayIndexOutOfBoundsException e) {
            cmilogger.log(BasicLevel.ERROR, "Don't need to be replicate.");
        }
    }

    /**
     * Adds a bean to the beans associated with rootId
     * @param bs the bean switch
     * @param clusterOId the cluster object id
     * @param rootId the root request id
     */
    static void addModifiedBean(final JStatefulSwitch bs, final SessionId clusterOId, final RequestId rootId) {
        StatefulBeanReference bean = new StatefulBeanRefImpl(bs);

        try {
            repMgr.addModifiedBean(rootId, clusterOId, bean);
        } catch (Exception e) {
            cmilogger.log(BasicLevel.ERROR, "Unable to add modified bean: " + clusterOId, e);
        }
    }

    /**
     * Adds a bean to the beans associated with rootId marked for remove
     * @param clusterOId the cluster object id
     * @param rootId the root request id
     */
    public static void addRemovedBean(final SessionId clusterOId, final RequestId rootId) {
        try {
            repMgr.addModifiedBean(rootId, clusterOId, null);
        } catch (Exception e) {
            cmilogger.log(BasicLevel.ERROR, "Unable to add removed bean: " + clusterOId, e);
        }
    }

    /**
     * Adds an entity bean to the beans associated with rootId
     * @param bctx The entity context
     * @param rootId the root request id
     */
    public static void addEntityBean(final EntityContext bctx, final RequestId rootId) {
        EntityBeanReference bean = new EntityBeanRefImpl((JEntityContext) bctx);

        try {
            repMgr.addEntityBean(rootId, bean);
        } catch (Exception e) {
            try {
                cmilogger.log(BasicLevel.ERROR, "Unable to add entity bean: " + bean.getPrimaryKey(), e);
            } catch (Exception e1) {
                ;
            }
        }
    }

    /**
     * Removes the current request from the requests heap.
     */
    static void removeCurrentRequest() {
        HACurrent current = HACurrent.getHACurrent();
        Stack<RequestId> requests = current.getRequests();
        if (requests != null && !requests.isEmpty()) {
            requests.pop();
        }
    }

    /**
     * Replication manager
     */
    private static ReplicationManager repMgr = null;


    public static ReplicationManager getRepMgr() {
        return repMgr;
    }

    public static void setRepMgr(final ReplicationManager repMgr) {
        JRepUtil.repMgr = repMgr;
    }

}
