package org.jacorb.orb;

import org.jacorb.orb.portableInterceptor.DefaultClientInterceptorHandler;
import org.jacorb.orb.portableInterceptor.InterceptorManager;
import org.jacorb.orb.portableInterceptor.ServerInterceptorIterator;
import org.jacorb.orb.portableInterceptor.ServerRequestInfoImpl;
import org.omg.CORBA.UserException;
import org.omg.CORBA.portable.ServantObjectExt;
import org.omg.PortableInterceptor.SUCCESSFUL;
import org.omg.PortableInterceptor.SYSTEM_EXCEPTION;
import org.omg.PortableInterceptor.USER_EXCEPTION;

public class ServantObjectImpl extends ServantObjectExt
{
    /**
     * The Server request info associated with this servant
     */
    private ServerRequestInfoImpl sinfo = null;

    /**
     * The client interceptors associated with this servant
     */
    private DefaultClientInterceptorHandler interceptors = null;

    /**
     * The interceptor manager associated with this call
     */
    private InterceptorManager manager = null;

    /**
     * The server interceptor iterator
     */
    private ServerInterceptorIterator interceptorIterator = null;

    /**
     * Prevent call to sendException if an exception occurs during handling
     * of the reply in normalCompletion
     */
    private boolean normalCompletionCalled = false;

    /**
     * Reference to the orb for this servant
     */
    private org.jacorb.orb.ORB orb = null;

    /**
     * This method is called if a call to the local server is successful.
     * The call to SEND_EXCEPTION can result in further exceptions being
     * thrown, including a ForwardRequest
     */
    public void normalCompletion()
    {
        if (orb.hasServerRequestInterceptors())
        {
           manager = orb.getInterceptorManager();
           interceptorIterator = manager.getServerIterator();

           try
           {
               /**
                * Setting this flag here to prevent a duplicate call to SEND_EXCEPTION
                * if an error occurs during the call to SEND_REPLY.
                *
                * If an exception occurs during SEND_REPLY any necessary SEND_EXCEPTION
                * points will already have been called
                */
               normalCompletionCalled = true;

               interceptorIterator.iterate (sinfo,
                                            ServerInterceptorIterator.SEND_REPLY);

               sinfo.setReplyStatus (SUCCESSFUL.value);
            }
            catch (RuntimeException ex)
            {
                sinfo.setReplyStatus (SYSTEM_EXCEPTION.value);
                sinfo.updateException (ex);
                throw ex;
            }
            catch (UserException ue)
            {
               sinfo.setReplyStatus (USER_EXCEPTION.value);
               sinfo.updateException (ue);
               throw new RuntimeException (ue);
            }
         }
    }

    /**
     * This method is called if the exception thrown is a RuntimeException
     * @param re the RuntimeException
     */
    public void exceptionalCompletion (RuntimeException re)
    {
        exceptionalCompletion ( (Throwable) re);
    }

    /**
     * This method is called if the exception thrown is an Error
     * @param err the RuntimeException
     */
    public void exceptionalCompletion (Error err)
    {
        exceptionalCompletion ( (Throwable) err);
    }

    /**
     * Generic method to handle whatever is thrown
     */
    public void exceptionalCompletion (Throwable t)
    {
        /**
         * If we haven't come from SEND_REPLY and there are interceptors
         * then we need to call SEND_EXCEPTION
         */
        if (!normalCompletionCalled && orb.hasServerRequestInterceptors())
        {
           manager = orb.getInterceptorManager();
           interceptorIterator = manager.getServerIterator();

           sinfo.updateException (t);

           try
           {
              interceptorIterator.iterate (sinfo,
                                           ServerInterceptorIterator.SEND_EXCEPTION);
           }
           catch (UserException ue)
           {
                sinfo.setReplyStatus (USER_EXCEPTION.value);
                sinfo.updateException (ue);
                throw new RuntimeException (ue);
           }
        }
    }

    /**
     * Set a reference to the ORB to be used in subsequent processing
     * @param orb the orb reference
     */
    public void setORB (org.jacorb.orb.ORB orb)
    {
        this.orb = orb;
    }

    /**
     * Set the server request info associated with this Servant
     * @param sinfo the ServerRequestInfoImpl
     */
    public void setServerRequestInfo (ServerRequestInfoImpl sinfo)
    {
        this.sinfo = sinfo;
    }

    /**
     * Set the local client interceptor handler that was instantiated
     * during servant_preinvoke, we need this to handle any necessary
     * interception points if an exception occurs
     * @param interceptors the ClientInterceptorHandler
     */
    public void setClientInterceptorHandler (DefaultClientInterceptorHandler interceptors)
    {
        this.interceptors = interceptors;
    }

    /**
     * Accessor method to get the ServerRequestInfoImpl.
     *
     * @return the server request info
     */
    public ServerRequestInfoImpl getServerRequestInfo()
    {
        return sinfo;
    }

    /**
     * Accessor  method to get the ClientInterceptorHandler - required
     * in servant_postinvoke
     * @return the DefaultClientInterceptorHandler
     */
    public DefaultClientInterceptorHandler getClientInterceptorHandler()
    {
        return interceptors;
    }
}
