/**
* High Availability Service (HA) for JOnAS
*
* 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
*
* --------------------------------------------------------------------------
* $Id$
* --------------------------------------------------------------------------
*/
package org.objectweb.jonas.ha;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

import javax.ejb.SessionBean;

import org.objectweb.carol.cmi.configuration.TraceCmi;
import org.objectweb.carol.cmi.ha.StatefulBeanReference;
import org.objectweb.jonas_ejb.container.JRepStatefulInputStream;
import org.objectweb.jonas_ejb.container.JRepStatefulOutputStream;
import org.objectweb.jonas_ejb.container.JStatefulContext;
import org.objectweb.jonas_ejb.container.JStatefulSwitch;
import org.objectweb.jonas_ejb.deployment.api.SessionStatefulDesc;

/**
 * This class implements reference to beans
 * @author Francisco Perez-Sorrosal (fpsorrosal@no-spam@fi.upm.es)
 * @author Alberto Paz-Jimenez (apaz@no-spam@fi.upm.es)
 */
public class StatefulBeanRefImpl implements StatefulBeanReference {

    private JStatefulSwitch bs;

    public StatefulBeanRefImpl(JStatefulSwitch bs) {
        this.bs = bs;
    }

    public byte[] getState() {
        return getStatefulState(bs);
    }

    public boolean isModified() {
        SessionStatefulDesc ssd = (SessionStatefulDesc) bs.getBeanFactory().getDeploymentDescriptor();
        if (ssd.hasIsModifiedMethod()) {
            Method isModifiedMethod = ssd.getIsModifiedMethod();
            try {
                SessionBean sb = ((JStatefulSwitch)bs).getStatefulContext().getInstance();
                return ((Boolean) isModifiedMethod.invoke(sb,(Object[]) null)).booleanValue();
            } catch (Exception e) {
                e.printStackTrace();
                return true;
            }
        } else {
            return true;
        }
    }

    /* (non-Javadoc)
     * @see org.objectweb.carol.cmi.ha.BeanReference#injectState(byte[])
     */
    public void injectState(byte[] state) {
        if (bs != null) {
            injectState(bs, state);
        }
    }

    /**
     * Extracts the state of the stateful session bean
     * @param jss the related switch of the stateful session bean to access the
     *        SFBS's real instance
     */
    static private byte[] getStatefulState(JStatefulSwitch jss) {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        JStatefulContext ctx = jss.getStatefulContext();
        try {
            SessionBean instance = ctx.getInstance();
            JRepStatefulOutputStream out = new JRepStatefulOutputStream(bos);
            out.writeObject(instance);
            out.close();
        } catch (Exception e) {
            TraceCmi.error("Can't get state of " + jss.getBeanFactory().getEJBName() + "inst.");
        }
        return bos.toByteArray();
    }

    /**
     * Injects the serialized state of the bean in the corresponding beanSwitch
     * @param bean target bean
     * @param beanState state to inject
     */
    static private void injectState(JStatefulSwitch bean, byte[] beanState) {

        JStatefulContext bctx = (JStatefulContext) bean.getStatefulContext();
        try {
            ByteArrayInputStream bais = new ByteArrayInputStream(beanState);
            JRepStatefulInputStream in = new JRepStatefulInputStream(bais, bean);
            Object obj = in.readObject();
            //if (TraceCmi.isDebugCmiHA())
            //  TraceCmi.debugCmiHA("\tDeserialized object:" + obj);
            in.close();
            SessionBean sb = (SessionBean) obj;

            // Deal with transient values
            SessionBean oldInstance = bctx.getInstance();
            Field[] fields = sb.getClass().getDeclaredFields();
            for (int i = 0; i < fields.length; i++) {
                if (Modifier.isTransient(fields[i].getModifiers())) {
                    try {
                        fields[i].setAccessible(true);
                        fields[i].set(sb,fields[i].get(oldInstance));
                    } catch (IllegalAccessException iae) {
                        iae.printStackTrace();
                    } catch (SecurityException se) {
                        se.printStackTrace();
                    }
                }
            }

            bctx.setInstance(sb);
            sb.ejbActivate();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
