/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 2006 Bull S.A.S.
 * Contact: jonas-team@objectweb.org
 * Copyright (C) 2006 Distributed Systems Lab.
 * Universidad Polit??cnica 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
 *
 * --------------------------------------------------------------------------
 *
 * --------------------------------------------------------------------------
 */

package org.objectweb.jonas_ejb.container;

import java.rmi.RemoteException;

import javax.ejb.EJBException;

import org.objectweb.carol.cmi.ObjectId;
import org.objectweb.carol.cmi.ServerConfigException;
import org.objectweb.carol.cmi.ServerIdFactory;
import org.objectweb.carol.cmi.configuration.TraceCmi;
import org.objectweb.carol.cmi.ha.RequestId;
import org.objectweb.jonas.ha.interceptor.HACurrentDelegateImpl;

/**
 * Generic part of the EJBObject implementation for replicated SFSBs
 * @author Francisco Perez-Sorrosal (fpsorrosal@no-spam@fi.upm.es)
 * @author Alberto Paz-Jimenez (apaz@no-spam@fi.upm.es)
 */
public abstract class JRepStatefulRemote extends JSessionRemote {

    /**
     * Constructor
     * @param bf
     * @throws RemoteException
     */
    public JRepStatefulRemote(JSessionFactory bf) throws RemoteException {
        super(bf);
        if (TraceCmi.isDebugCmiHA()) {
            TraceCmi.debugCmiHA("JRepStatefulRemote");
        }
    }

    /**
     * preInvoke is called before any request.
     * @param txa Transaction Attribute (Supports, Required, ...)
     * @return A RequestCtx object
     * @throws RemoteException Thrown when the method failed due to a
     *         system-level failure.
     */
    public RequestCtx preInvoke(int txa) throws RemoteException {
        RequestCtx rctx = super.preInvoke(txa);
        /* ******************REPLICATION CODE STARTS HERE*********************** */
        preInvokeHook(rctx); // Replication algorithm link
        /* **********************END OF REPLICATION CODE************************ */
        return rctx;
    }

    /**
     * postInvoke is called after any request.
     * @param rctx The RequestCtx that was returned at preInvoke()
     * @param remove TODO
     * @parem response The response that is going to be returned to the client
     *        (Needed by HA)
     * @throws RemoteException Thrown when the method failed due to a
     *         system-level failure.
     */
    public void postInvoke(RequestCtx rctx, Object response, boolean remove) throws RemoteException {
        /* ******************REPLICATION CODE STARTS HERE*********************** */
        try {
            postInvokeHook(rctx, response, remove); // Replication algorithm link
        /* **********************END OF REPLICATION CODE************************ */
        } catch (Exception e) {
            // COMPLETE: Handle errors
            e.printStackTrace();
            TraceCmi.error("Error calling postInvokeHook in sfsb.", e);
        } finally {
            super.postInvoke(rctx);
            // We dont need the request anymore so remove it
            removeCurrentRequest();
        }
    }

    /* ******************REPLICATION CODE STARTS HERE*********************** */

    // ------------------------------------------------------------------------
    // Protected methods ------------------------------------------------------
    // ------------------------------------------------------------------------
    /**
     * Injects the state of the bean if the cluster object id received in the
     * request is in the current node. It extracts the object id passed through
     * the interceptors from the request id included in the current HA context.
     */
    protected void injectState() {
        HACurrentDelegateImpl current = HACurrentDelegateImpl.getCurrent();
        // We still need the request so do not remove it
        RequestId reqId = (RequestId) current.getRequests().peek();
        if (reqId != null) {
            JRepUtil.injectState(reqId.getObjectId(), (JStatefulSwitch) this.bs);
        }
    }
    
    /**
     * Checks if there is a response associated with the current request id
     * @return true if the current rquest id have an associated response
     */
    protected boolean hasResponse() {
        HACurrentDelegateImpl current = HACurrentDelegateImpl.getCurrent();
        // We still need the request so do not remove it
        RequestId reqId = (RequestId) current.getRequests().peek();
        if (reqId != null) {
            try {
                return JRepUtil.hasBackupResponse(reqId);
            } catch (Exception e) {
                TraceCmi.error("Cannot check response : " + reqId, e);
                return false;
            }
        } else {
            return false;
        }
    }
    
    /**
     * Obtain the response associated with the current request id.
     * Returns null if there is not response associated with the current 
     * requestid
     * IMPORTANT: This method remove the request id from the requests heap
     * @return the response associated with the current request id
     */
    protected Object getResponse() {
        Object response = null;
        
        HACurrentDelegateImpl current = HACurrentDelegateImpl.getCurrent();
        // We still need the request so do not remove it
        RequestId reqId = (RequestId) current.getRequests().peek();
        if (reqId != null) {
            try {
                response = JRepUtil.getRepMgr().getBackupResponse(reqId);
            } catch (Exception e) {
                TraceCmi.error("Cannot obtain response : " + reqId, e);
                response = null;
            }
        } else {
            response = null;
        }

        // Remove the request
        current.getRequests().pop();
        return response;
    }

    // ------------------------------------------------------------------------
    // Private methods --------------------------------------------------------
    // ------------------------------------------------------------------------

    /**
     * preInvokeHook implements the hook to perform the required replication
     * tasks before the bean context has been established. In this case, it
     * saves the current state of the bean before perform the invocation, in the
     * request context.
     * @param rctx The RequestCtx that was returned at preInvoke()
     * @throws RemoteException 
     */
    private void preInvokeHook(RequestCtx rctx) throws RemoteException {
        try {
            // If the call is done from other JVM this is the root request
            HACurrentDelegateImpl current = HACurrentDelegateImpl.getCurrent();
            // We still need the request so do not remove it
            RequestId reqId = (RequestId) current.getRequests().peek();
            // Discard external request ids
            if (!reqId.getObjectId().getServerId().equals(ServerIdFactory.getServerId())) {
                current.getRequests().clear();
                current.getRequests().push(reqId);
            }
            
            JRepUtil.preInvokeHook(bs, reqId.getObjectId(), rctx);
        } catch (ServerConfigException sce) {
            TraceCmi.error("Unable to get Server Id.", sce);
        } catch (EJBException e) {
            e.printStackTrace();
            TraceCmi.error("Unable to get sfsb state.", e);
        }
    }

    /**
     * postInvokeHook implements the hook to perform the required replication
     * tasks after the bean context has been established
     * @param rctx The RequestCtx that was returned at preInvoke()
     * @parem response The response that is going to be returned to the client
     * @param remove true if it is an invocation to a remove method
     */
    private void postInvokeHook(RequestCtx rctx, Object response, boolean remove) {
        HACurrentDelegateImpl current = HACurrentDelegateImpl.getCurrent();
        RequestId reqId = null;
        ObjectId clusterOId = null;

        reqId = (RequestId) current.getRequests().peek();
        clusterOId = reqId.getObjectId();
        
        JRepUtil.postInvokeHook(rctx, clusterOId, bs, remove);

        // If this is the root request, then associate response with rootId
        if (current.getRequests().size() == 1) {
            try {
                JRepUtil.getRepMgr().addResponse(reqId, response);
            } catch (Exception e) {
                e.printStackTrace();
                TraceCmi.error("unable to obtain replication manager.", e);
            }
        }
    }
    
    /**
     * Removes the curren request from the requests heap
     */
    private void removeCurrentRequest() {
        HACurrentDelegateImpl current = HACurrentDelegateImpl.getCurrent();
        current.getRequests().pop();
    }


    /* **********************END OF REPLICATION CODE************************ */
}
