package org.jacorb.orb.portableInterceptor;

/*
 *        JacORB - a free Java ORB
 *
 *   Copyright (C) 1999-2014 Gerald Brose / The JacORB Team.
 *
 *   This library is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU Library General Public
 *   License as published by the Free Software Foundation; either
 *   version 2 of the License, or (at your option) 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
 *   Library General Public License for more details.
 *
 *   You should have received a copy of the GNU Library General Public
 *   License along with this library; if not, write to the Free
 *   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

import java.util.ArrayList;
import java.util.Collection;
import org.jacorb.orb.dsi.ServerRequest;
import org.jacorb.orb.giop.GIOPConnection;
import org.jacorb.orb.giop.ReplyOutputStream;
import org.jacorb.orb.giop.RequestInputStream;
import org.jacorb.poa.POA;
import org.omg.CORBA.Any;
import org.omg.CORBA.BAD_INV_ORDER;
import org.omg.CORBA.CompletionStatus;
import org.omg.CORBA.INV_POLICY;
import org.omg.CORBA.NO_RESOURCES;
import org.omg.CORBA.Policy;
import org.omg.CORBA.SystemException;
import org.omg.CORBA.TypeCode;
import org.omg.CORBA.UserException;
import org.omg.Dynamic.Parameter;
import org.omg.IOP.ServiceContext;
import org.omg.PortableInterceptor.InvalidSlot;
import org.omg.PortableInterceptor.LOCATION_FORWARD;
import org.omg.PortableInterceptor.SYSTEM_EXCEPTION;
import org.omg.PortableInterceptor.ServerRequestInfo;
import org.omg.PortableInterceptor.UNKNOWN;
import org.omg.PortableInterceptor.USER_EXCEPTION;
import org.omg.PortableServer.Servant;

/**
 * This class represents the type of info object
 * that will be passed to the ServerRequestInterceptors. <br>
 * See PI Spec p.5-50ff
 *
 * @author Nicolas Noffke
 */

public class ServerRequestInfoImpl
    extends RequestInfoImpl
    implements ServerRequestInfo
{
	private static final long serialVersionUID = -9044446354286649195L;

	//from ServerRequestInfo
    private byte[] adapter_id = null;
    private String [] adapter_name = null;
    private String target_most_derived_interface = null;

    private Servant servant = null;

    private final String operation;

    private final boolean response_expected;

    private final int requestId;

    private final byte [] objectId;

    private final ServerRequest request;

    protected Any sending_exception = null;


    public ServerRequestInfoImpl( org.jacorb.orb.ORB orb,
                                  ServerRequest request,
                                  Servant servant)
    {
        super(orb);

        this.request = request;
        operation = request.operation();
        response_expected = request.responseExpected();
        sync_scope = request.syncScope();
        requestId = request.requestId();
        objectId = request.objectId();

        if (servant != null)
        {
            setServant(servant);
        }

        setRequestServiceContexts(request.getServiceContext());

        sending_exception = orb.create_any();
    }

    /**
     * This constructor is to cater for local calls where portable
     * interceptors are involved.  These calls are now handled
     * locally rather than via the remote request mechanism so there
     * will be no request from which to obtain information
     *
     * @param orb the orb
     * @param contexts the contexts
     * @param servant the servant
     * @param objectId the object id
     * @param operation the operation
     * @param response_expected the response_expected
     * @param sync_scope the sync_scope
     */
    public ServerRequestInfoImpl( org.jacorb.orb.ORB orb,
                                  Collection<ServiceContext> contexts,
                                  Servant servant,
                                  byte[] objectId,
                                  String operation,
                                  boolean response_expected,
                                  short sync_scope)
    {
        super(orb);

        this.operation = operation;
        this.response_expected = response_expected;
        this.sync_scope = sync_scope;
        this.objectId = objectId;

        requestId = 0;
        request = null;


        if (servant != null)
        {
            setServant(servant);
        }

        if (contexts != null)
        {
           setRequestServiceContexts(contexts.toArray (new ServiceContext[contexts.size ()]));
        }

        sending_exception = orb.create_any();
    }

    /**
     * The servant is sometimes not available on calling
     * receive_request_service_contexts (e.g. in case of
     * ServantLocators or ServantActivators).
     */

    public final void setServant(Servant servant)
    {
        this.servant = servant;
        org.jacorb.poa.POA poa = (org.jacorb.poa.POA) servant._poa();
        adapter_id = poa.getPOAId();
        String[] all_ifs = servant._all_interfaces(poa, servant._object_id());
        target_most_derived_interface = all_ifs[0];

        POA parent = poa;
        ArrayList<String> al = new ArrayList<String> ();

        while (parent != null)
        {
           al.add (parent.the_name ());
           parent = (POA) parent.the_parent ();
         }
         int size = al.size ();
         adapter_name = new String [size];

         // We can't just do toArray as otherwise adapter_name would be in
         // reverse order.
         for (int j = 0, i = (size-1); i >= 0; i--, j++)
         {
            adapter_name [j] = al.get (i);
         }
    }

    /**
     * Set the sending_exception attribute.
     */

    public void update()
    {
        if (request != null)
        {
            if (! request.streamBased())
            {
                Any user_ex = request.except();
                if (user_ex != null)
                {
                    sending_exception = user_ex;
                }
            }

            SystemException sys_ex = request.getSystemException();
            if (sys_ex != null)
            {
                org.jacorb.orb.SystemExceptionHelper.insert(sending_exception, sys_ex);
            }

            forward_reference = request.getForwardReference();
        }
    }

    /**
     * Method introduced for local invocations as there is no Reply or Stream
     * involved
     */
    public void updateException (Throwable ex)
    {
        if (ex instanceof org.omg.CORBA.SystemException)
        {
            org.jacorb.orb.SystemExceptionHelper.insert (sending_exception,
                                                         ( org.omg.CORBA.SystemException) ex);
            setReplyStatus (SYSTEM_EXCEPTION.value);
        }
        else if (ex instanceof org.omg.PortableInterceptor.ForwardRequest)
        {
            forward_reference = (( org.omg.PortableInterceptor.ForwardRequest) ex).forward;
            setReplyStatus (LOCATION_FORWARD.value);
        }
        else if (ex instanceof UserException)
        {
            org.jacorb.orb.SystemExceptionHelper.insert
            (
                sending_exception,
                new org.omg.CORBA.UNKNOWN ("Received UserException " + ex)
            );

            setReplyStatus (USER_EXCEPTION.value);
        }
    }


    /**
     * returns a reference to the calls target.
     */

    public org.omg.CORBA.Object target()
    {
        return servant._this_object();
    }

    // implementation of RequestInfoOperations interface

    @Override
    public Parameter[] arguments()
    {
        if (!(caller_op == ServerInterceptorIterator.RECEIVE_REQUEST) &&
            !(caller_op == ServerInterceptorIterator.SEND_REPLY))
        {
            throw new BAD_INV_ORDER("The attribute \"arguments\" is currently invalid!",
                                    10, CompletionStatus.COMPLETED_MAYBE);
        }

        if (arguments == null)
        {
            throw new NO_RESOURCES("Stream-based skeletons/stubs do not support this op",
                                   1, CompletionStatus.COMPLETED_MAYBE);
        }

        return arguments;
    }

    @Override
    public TypeCode[] exceptions()
    {
        throw new NO_RESOURCES("This feature is not supported on the server side",
                               1, CompletionStatus.COMPLETED_MAYBE);
    }

    @Override
    public Any result()
    {
        if ( caller_op != ServerInterceptorIterator.SEND_REPLY )
        {
            throw new BAD_INV_ORDER("The attribute \"result\" is currently invalid!",
                                    10, CompletionStatus.COMPLETED_MAYBE);
        }

        Any result = null;

        try
        {
            if (request != null)
            {
                result = request.result();
            }
        }
        catch(Exception e)
        {
        }

        if (result == null)
        {
            throw new NO_RESOURCES("Stream-based skeletons/stubs do not support this op",
                                   1, CompletionStatus.COMPLETED_MAYBE);
        }

        return result;
    }

    @Override
    public short sync_scope()
    {
        return sync_scope;
    }

    @Override
    public short reply_status()
    {
        if ((caller_op == ServerInterceptorIterator.RECEIVE_REQUEST ||
             caller_op == ServerInterceptorIterator.RECEIVE_REQUEST_SERVICE_CONTEXTS) &&
            reply_status != UNKNOWN.value)
        {
            throw new BAD_INV_ORDER("The attribute \"reply_status\" is currently invalid!",
                                    10, CompletionStatus.COMPLETED_MAYBE);
        }

        return reply_status;
    }

    @Override
    public org.omg.CORBA.Object forward_reference()
    {
        if (! (caller_op != ServerInterceptorIterator.SEND_OTHER) ||
            (reply_status != LOCATION_FORWARD.value))
        {
            throw new BAD_INV_ORDER("The attribute \"forward_reference\" is currently " +
                                    "invalid!", 10, CompletionStatus.COMPLETED_MAYBE);
        }

        return forward_reference;
    }

    @Override
    public ServiceContext get_reply_service_context(int id)
    {
        if ((caller_op == ServerInterceptorIterator.RECEIVE_REQUEST) ||
            (caller_op == ServerInterceptorIterator.RECEIVE_REQUEST_SERVICE_CONTEXTS))
        {
            throw new BAD_INV_ORDER("The operation \"get_reply_service_context\" is " +
                                    "currently invalid!", 10,
                                    CompletionStatus.COMPLETED_MAYBE);
        }

        return super.get_reply_service_context(id);
    }

    @Override
    public String operation()
    {
        return operation;
    }

    @Override
    public int request_id()
    {
        return requestId;
    }

    @Override
    public boolean response_expected()
    {
        return response_expected;
    }

    // implementation of ServerRequestInfoOperations interface
    @Override
    public Any sending_exception()
    {
        if (caller_op != ServerInterceptorIterator.SEND_EXCEPTION)
        {
            throw new BAD_INV_ORDER("The attribute \"sending_exception\" is " +
                                    "currently invalid!", 10,
                                    CompletionStatus.COMPLETED_MAYBE);
        }

        return sending_exception;
    }

    @Override
    public byte[] object_id()
    {
        if (caller_op == ServerInterceptorIterator.RECEIVE_REQUEST_SERVICE_CONTEXTS)
        {
            throw new BAD_INV_ORDER("The attribute \"object_id\" is currently invalid!",
                                    10, CompletionStatus.COMPLETED_MAYBE);
        }

        return objectId;
    }

    @Override
    public byte[] adapter_id()
    {
        if (caller_op == ServerInterceptorIterator.RECEIVE_REQUEST_SERVICE_CONTEXTS)
        {
            throw new BAD_INV_ORDER("The attribute \"adapter_id\" is currently invalid!",
                                    10, CompletionStatus.COMPLETED_MAYBE);
        }

        return adapter_id;
    }

    @Override
    public String orb_id()
    {
        if (caller_op == ServerInterceptorIterator.RECEIVE_REQUEST_SERVICE_CONTEXTS)
        {
            throw new BAD_INV_ORDER ("The attribute \"orb_id\" is currently invalid!",
                                     14,
                                     CompletionStatus.COMPLETED_MAYBE);
        }

        return orb.id();
    }

    @Override
    public String server_id()
    {
        if (caller_op == ServerInterceptorIterator.RECEIVE_REQUEST_SERVICE_CONTEXTS)
        {
            throw new NO_RESOURCES ("The attribute \"server_id\" is not available",
                                    1,
                                    CompletionStatus.COMPLETED_MAYBE);
        }
        else
        {
            throw new BAD_INV_ORDER ("The attribute \"server_id\" is currently invalid!",
                                     14,
                                     CompletionStatus.COMPLETED_MAYBE);
        }
    }



    @Override
    public String [] adapter_name()
    {
        if (caller_op == ServerInterceptorIterator.RECEIVE_REQUEST_SERVICE_CONTEXTS)
        {
            throw new BAD_INV_ORDER ("The attribute \"adapter_name\" is currently invalid!",
                                     14,
                                     CompletionStatus.COMPLETED_MAYBE);
        }

        return adapter_name;
    }


    @Override
    public String target_most_derived_interface()
    {
        if (caller_op == ServerInterceptorIterator.RECEIVE_REQUEST_SERVICE_CONTEXTS)
        {
            throw new BAD_INV_ORDER("The attribute \"target_most_derived_interface\" is " +
                                    "currently invalid!", 10,
                                    CompletionStatus.COMPLETED_MAYBE);
        }

        return target_most_derived_interface;
    }

    /**
     * WARNING: This method relies on the DomainService to be available.
     * Make shure that the DS is running, if you want to call this method.
     */
    @Override
    public Policy get_server_policy(int type) {
        if (! orb.hasPolicyFactoryForType(type))
        {
            throw new INV_POLICY("No PolicyFactory for type " + type +
                                 " has been registered!", 2,
                                 CompletionStatus.COMPLETED_MAYBE);
        }

        try
        {
            org.jacorb.orb.ServantDelegate delegate = (org.jacorb.orb.ServantDelegate) servant._get_delegate();
            return delegate._get_policy(servant._this_object(), type);
        }
        catch(INV_POLICY e)
        {
            e.minor = 2;
            throw e;
        }
    }

    @Override
    public void set_slot(int id, Any data) throws InvalidSlot
    {
        current.set_slot(id, data);
    }

    @Override
    public boolean target_is_a(String id)
    {
        if (caller_op == ServerInterceptorIterator.RECEIVE_REQUEST_SERVICE_CONTEXTS)
        {
            throw new BAD_INV_ORDER("The operation \"target_is_a\" is currently invalid!",
                                    10, CompletionStatus.COMPLETED_MAYBE);
        }

        return servant._is_a(id);
    }

    @Override
    public void add_reply_service_context(ServiceContext service_context,
                                          boolean replace)
    {
        Integer _id = Integer.valueOf (service_context.context_id);

        if (! replace && reply_ctx.containsKey(_id))
        {
            throw new BAD_INV_ORDER("The ServiceContext with id " + _id.toString()
                                    + " has already been set!", 11,
                                    CompletionStatus.COMPLETED_MAYBE);
        }

        reply_ctx.put(_id, service_context);
    }


    /**
     * Public accessor to return the connection being used by this ServerRequestInfoImpl.
     *
     * @return the connection (maybe null if local call and no connection has been used).
     */
    public GIOPConnection getConnection()
    {
        return (request == null ? null : request.getConnection());
    }

    /**
     * Public accessor to determine if this interceptor is being used in a local call.
     *
     * @return true if this is a local interceptor.
     */
    public boolean isLocalInterceptor()
    {
        return (getConnection() == null);
    }

    // These functions should NOT be used internally and are only provided
    // to allow users to interrogate the streams.
    /**
     * Public accessor to access the internal reply stream. Using this API it
     * may be possible to corrupt the inputstream and therebye the call
     * chain. Use at your own risk.
     */
    public ReplyOutputStream getReplyStream ()
    {
        return request.get_out();
    }

    /**
     * Public accessor to access the internal request stream. Using this API it
     * may be possible to corrupt the outputstream and therebye the call
     * chain. Use at your own risk.
     */
    public RequestInputStream getRequestStream ()
    {
        return request.get_in();
    }


    /** Set the sending_exception Any. Used by RequestProcessor. */
    public void sending_exception(Any any)
    {
        sending_exception = any;
    }
}
