/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999-2004 Bull S.A.
 * Contact: jonas-team@ow2.org 
 *
 * 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;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.rmi.Remote;
import java.rmi.server.RemoteObject;
import java.rmi.server.RemoteStub;

import javax.ejb.EJBHome;
import javax.ejb.EJBObject;
import javax.ejb.Handle;
import javax.ejb.SessionContext;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.transaction.UserTransaction;

import org.ow2.jonas.lib.naming.ComponentContext;

import org.objectweb.util.monolog.api.BasicLevel;

/**
 * Extends ObjectOutputStream because we want to enable replaceObject.
 * This class is very related to JStatefulInputStream
 */
public class JStatefulOutputStream extends ObjectOutputStream {

    /**
     * Constructor.
     * @throws IOException
     */
    protected JStatefulOutputStream(OutputStream out) throws IOException {
        super(out);
//        TraceEjb.ssfpool.log(BasicLevel.DEBUG, "constructor");
        enableReplaceObject(true);
    }

    /**
     * We must care about some object types. See EJB spec. 7.4.1
     */
    protected Object replaceObject(Object obj) throws IOException {
        Object ret;
        
        if (obj instanceof EJBObject) {
            // EJBObject -> its Handle
//            TraceEjb.ssfpool.log(BasicLevel.DEBUG, "EJBObject");
            ret = ((EJBObject) obj).getHandle();
        } else if (obj instanceof EJBHome) {
            // EJBHome -> its HomeHandle
//            TraceEjb.ssfpool.log(BasicLevel.DEBUG, "EJBHome");
            ret = ((EJBHome) obj).getHomeHandle();
        } else if (obj instanceof JEntityLocal) {
            // JEntityLocal -> Home jndi local name + PK
//            TraceEjb.ssfpool.log(BasicLevel.DEBUG, "JEntityLocal");
            JEntityLocal local = (JEntityLocal) obj;
            ret = local.getJWrapper();
/*            JEntityLocalHome lhome = (JEntityLocalHome) local.getEJBLocalHome();
            String jndiname = lhome.getJndiLocalName();
            Object pk = local.getPrimaryKey();
            ret = new JWrapper(JWrapper.LOCAL_ENTITY, jndiname, pk);*/
        } else if (obj instanceof JLocalHome) {
            // JLocalHome -> JNDI local name
//            TraceEjb.ssfpool.log(BasicLevel.DEBUG, "JLocalHome");
            JLocalHome lhome = (JLocalHome) obj;
            String jndiname = lhome.getJndiLocalName();
            ret = new JWrapper(JWrapper.LOCAL_HOME, jndiname);
        } else if (obj instanceof ComponentContext) {
            // ref to the env. naming context
            try {
                String cname = ((Context)obj).getNameInNamespace();
                ret = new JWrapper(JWrapper.NAMING_CONTEXT, cname);
            } catch (NamingException e) {
                TraceEjb.ssfpool.log(BasicLevel.ERROR, "Cannot convert Context: " + e);
                ret = obj;
            }
        } else if (obj instanceof UserTransaction) {
            // USerTransaction ref -> just mark it.
//            TraceEjb.ssfpool.log(BasicLevel.DEBUG, "UserTransaction");
            ret = new JWrapper(JWrapper.USER_TX, null);
        } else if (obj instanceof SessionContext) {
            // SessionContext ref -> just mark it.
//            TraceEjb.ssfpool.log(BasicLevel.DEBUG, "SessionContext");
            ret = new JWrapper(JWrapper.SESSION_CTX, null);
        } else if (obj instanceof Handle) {
            // Handle -> wrap it to avoid a confusion with EJBObject
            // when the object will be deserialized.
//            TraceEjb.ssfpool.log(BasicLevel.DEBUG, "Handle");

            // Serialize the Handle without JStatefulOutputStream
            Handle theHandle = (Handle) obj;
            byte ser[];
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(theHandle);
            ser = baos.toByteArray();
            
            ret = new JWrapper(JWrapper.HANDLE, ser);
            // ret = new JWrapper(JWrapper.HANDLE, (Handle) obj);
        } else if ((obj instanceof Remote) && !(obj instanceof RemoteStub)) {
            // Remote ref -> its stub.
//            TraceEjb.ssfpool.log(BasicLevel.DEBUG, "Remote");
            Remote remote = (Remote) obj;
            try {
                ret = RemoteObject.toStub(remote);
            } catch (IOException ignore) {
                TraceEjb.ssfpool.log(BasicLevel.DEBUG, "Cannot convert to stub");
                ret = obj;
            }
        } else {
            // Default -> keep the object as is.
            if (TraceEjb.isDebugSsfpool()) {
                TraceEjb.ssfpool.log(BasicLevel.DEBUG, "Other:" + obj);
            }
            ret = obj;
        }
        return ret;
    }
}
