/* * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code 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 General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.corba.se.impl.interceptors; import org.omg.CORBA.Any; import org.omg.CORBA.BAD_INV_ORDER; import org.omg.CORBA.CompletionStatus; import org.omg.CORBA.INTERNAL; import org.omg.CORBA.LocalObject; import org.omg.CORBA.NO_IMPLEMENT; import org.omg.CORBA.NO_RESOURCES; import org.omg.CORBA.NVList; import org.omg.CORBA.Object; import org.omg.CORBA.Policy; import org.omg.CORBA.TypeCode; import org.omg.PortableServer.Servant; import org.omg.IOP.TaggedProfile; import org.omg.IOP.ServiceContext; import org.omg.Dynamic.Parameter; import org.omg.PortableInterceptor.InvalidSlot; import org.omg.PortableInterceptor.ServerRequestInfo; import org.omg.PortableInterceptor.LOCATION_FORWARD; import org.omg.PortableInterceptor.SUCCESSFUL; import org.omg.PortableInterceptor.SYSTEM_EXCEPTION; import org.omg.PortableInterceptor.TRANSPORT_RETRY; import org.omg.PortableInterceptor.USER_EXCEPTION; import com.sun.corba.se.spi.oa.ObjectAdapter; import com.sun.corba.se.spi.presentation.rmi.StubAdapter; import com.sun.corba.se.impl.protocol.giopmsgheaders.ReplyMessage; import com.sun.corba.se.spi.servicecontext.ServiceContexts; import com.sun.corba.se.spi.orb.ORB; import com.sun.corba.se.spi.ior.ObjectKeyTemplate; import com.sun.corba.se.spi.ior.ObjectAdapterId ; import com.sun.corba.se.spi.protocol.CorbaMessageMediator; import java.util.*; /** * Implementation of the ServerRequestInfo interface as specified in * orbos/99-12-02 section 5.4.3. */ public final class ServerRequestInfoImpl extends RequestInfoImpl implements ServerRequestInfo { // The available constants for startingPointCall static final int CALL_RECEIVE_REQUEST_SERVICE_CONTEXT = 0; // The available constants for intermediatePointCall. The default (0) // is receive_request, but can be set to none on demand. static final int CALL_RECEIVE_REQUEST = 0; static final int CALL_INTERMEDIATE_NONE = 1; // The available constants for endingPointCall static final int CALL_SEND_REPLY = 0; static final int CALL_SEND_EXCEPTION = 1; static final int CALL_SEND_OTHER = 2; ////////////////////////////////////////////////////////////////////// // // NOTE: IF AN ATTRIBUTE IS ADDED, PLEASE UPDATE RESET(); // ////////////////////////////////////////////////////////////////////// // Set to true if the server ending point raised ForwardRequest at some // point in the ending point. private boolean forwardRequestRaisedInEnding; // Sources of server request information: private CorbaMessageMediator request; private java.lang.Object servant; private byte[] objectId; private ObjectKeyTemplate oktemp ; // Information cached from calls to oktemp private byte[] adapterId; private String[] adapterName; private ArrayList addReplyServiceContextQueue; private ReplyMessage replyMessage; private String targetMostDerivedInterface; private NVList dsiArguments; private Any dsiResult; private Any dsiException; private boolean isDynamic; private ObjectAdapter objectAdapter; private int serverRequestId; // Cached information: private Parameter[] cachedArguments; private Any cachedSendingException; // key = Integer, value = IOP.ServiceContext. private HashMap cachedRequestServiceContexts; // key = Integer, value = IOP.ServiceContext. private HashMap cachedReplyServiceContexts; ////////////////////////////////////////////////////////////////////// // // NOTE: IF AN ATTRIBUTE IS ADDED, PLEASE UPDATE RESET(); // ////////////////////////////////////////////////////////////////////// /** * Reset the info object so that it can be reused for a retry, * for example. */ void reset() { super.reset(); // Please keep these in the same order as declared above. forwardRequestRaisedInEnding = false; request = null; servant = null; objectId = null; oktemp = null; adapterId = null; adapterName = null; addReplyServiceContextQueue = null; replyMessage = null; targetMostDerivedInterface = null; dsiArguments = null; dsiResult = null; dsiException = null; isDynamic = false; objectAdapter = null; serverRequestId = myORB.getPIHandler().allocateServerRequestId(); // reset cached attributes: cachedArguments = null; cachedSendingException = null; cachedRequestServiceContexts = null; cachedReplyServiceContexts = null; startingPointCall = CALL_RECEIVE_REQUEST_SERVICE_CONTEXT; intermediatePointCall = CALL_RECEIVE_REQUEST; endingPointCall = CALL_SEND_REPLY; } /* ********************************************************************** * Access protection **********************************************************************/ // Method IDs for all methods in ServerRequestInfo. This allows for a // convenient O(1) lookup for checkAccess(). protected static final int MID_SENDING_EXCEPTION = MID_RI_LAST + 1; protected static final int MID_OBJECT_ID = MID_RI_LAST + 2; protected static final int MID_ADAPTER_ID = MID_RI_LAST + 3; protected static final int MID_TARGET_MOST_DERIVED_INTERFACE = MID_RI_LAST + 4; protected static final int MID_GET_SERVER_POLICY = MID_RI_LAST + 5; protected static final int MID_SET_SLOT = MID_RI_LAST + 6; protected static final int MID_TARGET_IS_A = MID_RI_LAST + 7; protected static final int MID_ADD_REPLY_SERVICE_CONTEXT = MID_RI_LAST + 8; protected static final int MID_SERVER_ID = MID_RI_LAST + 9; protected static final int MID_ORB_ID = MID_RI_LAST + 10; protected static final int MID_ADAPTER_NAME = MID_RI_LAST + 11; // ServerRequestInfo validity table (see ptc/00-08-06 table 21-2). // Note: These must be in the same order as specified in contants. private static final boolean validCall[][] = { // LEGEND: // r_rsc = receive_request_service_contexts // r_req = receive_request // s_rep = send_reply // s_exc = send_exception // s_oth = send_other // // A true value indicates call is valid at specified point. // A false value indicates the call is invalid. // // NOTE: If the order or number of columns change, update // checkAccess() accordingly. // // { r_rsc, r_req, s_rep, s_exc, s_oth } // RequestInfo methods: /*request_id*/ { true , true , true , true , true }, /*operation*/ { true , true , true , true , true }, /*arguments*/ { false, true , true , false, false }, /*exceptions*/ { false, true , true , true , true }, /*contexts*/ { false, true , true , true , true }, /*operation_context*/ { false, true , true , false, false }, /*result*/ { false, false, true , false, false }, /*response_expected*/ { true , true , true , true , true }, /*sync_scope*/ { true , true , true , true , true }, /*reply_status*/ { false, false, true , true , true }, /*forward_reference*/ { false, false, false, false, true }, /*get_slot*/ { true , true , true , true , true }, /*get_request_service_context*/ { true , true , true , true , true }, /*get_reply_service_context*/ { false, false, true , true , true }, // // ServerRequestInfo methods:: /*sending_exception*/ { false, false, false, true , false }, /*object_id*/ { false, true , true , true , true }, /*adapter_id*/ { false, true , true , true , true }, /*target_most_derived_inte...*/ { false, true , false, false, false }, /*get_server_policy*/ { true , true , true , true , true }, /*set_slot*/ { true , true , true , true , true }, /*target_is_a*/ { false, true , false, false, false }, /*add_reply_service_context*/ { true , true , true , true , true }, /*orb_id*/ { false, true , true , true , true }, /*server_id*/ { false, true , true , true , true }, /*adapter_name*/ { false, true , true , true , true } }; /* ********************************************************************** * Public interfaces **********************************************************************/ /** * Creates a new ServerRequestInfo implementation. * The constructor is package scope since no other package need create * an instance of this class. */ ServerRequestInfoImpl( ORB myORB ) { super( myORB ); startingPointCall = CALL_RECEIVE_REQUEST_SERVICE_CONTEXT; intermediatePointCall = CALL_RECEIVE_REQUEST; endingPointCall = CALL_SEND_REPLY; serverRequestId = myORB.getPIHandler().allocateServerRequestId(); } /** * Any containing the exception to be returned to the client. */ public Any sending_exception () { checkAccess( MID_SENDING_EXCEPTION ); if( cachedSendingException == null ) { Any result = null ; if( dsiException != null ) { result = dsiException; } else if( exception != null ) { result = exceptionToAny( exception ); } else { // sending_exception should not be callable if both dsiException // and exception are null. throw wrapper.exceptionUnavailable() ; } cachedSendingException = result; } return cachedSendingException; } /** * The opaque object_id describing the target of the operation invocation. */ public byte[] object_id () { checkAccess( MID_OBJECT_ID ); if( objectId == null ) { // For some reason, we never set object id. This could be // because a servant locator caused a location forward or // raised an exception. As per ptc/00-08-06, section 21.3.14, // we throw NO_RESOURCES throw stdWrapper.piOperationNotSupported6() ; } // Good citizen: In the interest of efficiency, we will assume // interceptors will not change the resulting byte[] array. // Otherwise, we would need to make a clone of this array. return objectId; } private void checkForNullTemplate() { if (oktemp == null) { // For some reason, we never set the ObjectKeyTemplate // because a servant locator caused a location forward or // raised an exception. As per ptc/00-08-06, section 21.3.14, // we throw NO_RESOURCES throw stdWrapper.piOperationNotSupported7() ; } } public String server_id() { checkAccess( MID_SERVER_ID ) ; checkForNullTemplate() ; // Good citizen: In the interest of efficiency, we will assume // interceptors will not change the resulting byte[] array. // Otherwise, we would need to make a clone of this array. return Integer.toString( oktemp.getServerId() ) ; } public String orb_id() { checkAccess( MID_ORB_ID ) ; return myORB.getORBData().getORBId() ; } synchronized public String[] adapter_name() { checkAccess( MID_ADAPTER_NAME ) ; if (adapterName == null) { checkForNullTemplate() ; ObjectAdapterId oaid = oktemp.getObjectAdapterId() ; adapterName = oaid.getAdapterName() ; } return adapterName ; } /** * The opaque identifier for the object adapter. */ synchronized public byte[] adapter_id () { checkAccess( MID_ADAPTER_ID ); if( adapterId == null ) { checkForNullTemplate() ; adapterId = oktemp.getAdapterId() ; } return adapterId; } /** * The RepositoryID for the most derived interface of the servant. */ public String target_most_derived_interface () { checkAccess( MID_TARGET_MOST_DERIVED_INTERFACE ); return targetMostDerivedInterface; } /** * Returns the policy in effect for this operation for the given policy * type. */ public Policy get_server_policy (int type) { // access is currently valid for all states: //checkAccess( MID_GET_SERVER_POLICY ); Policy result = null; if( objectAdapter != null ) { result = objectAdapter.getEffectivePolicy( type ); } // _REVISIT_ RTF Issue: get_server_policy spec not in sync with // get_effective_policy spec. return result; } /** * Allows an Interceptor to set a slot in the Current that is in the scope * of the request. If data already exists in that slot, it will be * overwritten. If the ID does not define an allocated slot, InvalidSlot * is raised. */ public void set_slot (int id, Any data) throws InvalidSlot { // access is currently valid for all states: //checkAccess( MID_SET_SLOT ); slotTable.set_slot( id, data ); } /** * Returns true if the servant is the given RepositoryId, false if it is * not. */ public boolean target_is_a (String id) { checkAccess( MID_TARGET_IS_A ); boolean result = false ; if( servant instanceof Servant ) { result = ((Servant)servant)._is_a( id ); } else if (StubAdapter.isStub( servant )) { result = ((org.omg.CORBA.Object)servant)._is_a( id ); } else { throw wrapper.servantInvalid() ; } return result; } /** * Allows Interceptors to add service contexts to the request. */ public void add_reply_service_context ( ServiceContext service_context, boolean replace ) { // access is currently valid for all states: //checkAccess( MID_ADD_REPLY_SERVICE_CONTEXT ); if( currentExecutionPoint == EXECUTION_POINT_ENDING ) { ServiceContexts scs = replyMessage.getServiceContexts(); // May be null. If this is null, create a new one in its place. if( scs == null ) { scs = new ServiceContexts( myORB ); replyMessage.setServiceContexts( scs ); } if( cachedReplyServiceContexts == null ) { cachedReplyServiceContexts = new HashMap(); } // This is during and ending point, so we now have enough // information to add the reply service context. addServiceContext( cachedReplyServiceContexts, scs, service_context, replace ); } // We enqueue all adds for the following reasons: // // If we are not in the ending point then we do not yet have a // pointer to the ServiceContexts object so we cannot access the // service contexts until we get to the ending point. // So we enqueue this add reply service context request. // It is added when we do have a handle on the service contexts object. // // If we are in the ending point and we just add directly to the // SC container but then an interceptor raises a SystemException // then that add will be lost since a new container is created // for the SystemException response. // // Therefore we always enqueue and never dequeue (per request) so // that all adds will be completed. AddReplyServiceContextCommand addReply = new AddReplyServiceContextCommand(); addReply.service_context = service_context; addReply.replace = replace; if( addReplyServiceContextQueue == null ) { addReplyServiceContextQueue = new ArrayList(); } // REVISIT: this does not add to the cache. enqueue( addReply ); } // NOTE: When adding a method, be sure to: // 1. Add a MID_* constant for that method // 2. Call checkAccess at the start of the method // 3. Define entries in the validCall[][] table for interception points. /* ********************************************************************** * Public RequestInfo interfaces * * These are implemented here because they have differing * implementations depending on whether this is a client or a server * request info object. **********************************************************************/ /** * See ServerRequestInfo for javadocs. */ public int request_id (){ // access is currently valid for all states: //checkAccess( MID_REQUEST_ID ); /* * NOTE: The request id in server interceptors is NOT the * same as the GIOP request id. The ORB may be servicing several * connections, each with possibly overlapping sets of request ids. * Therefore we create a request id specific to interceptors. */ return serverRequestId; } /** * See ServerRequestInfo for javadocs. */ public String operation (){ // access is currently valid for all states: //checkAccess( MID_OPERATION ); return request.getOperationName(); } /** * See ServerRequestInfo for javadocs. */ public Parameter[] arguments (){ checkAccess( MID_ARGUMENTS ); if( cachedArguments == null ) { if( !isDynamic ) { throw stdWrapper.piOperationNotSupported1() ; } if( dsiArguments == null ) { throw stdWrapper.piOperationNotSupported8() ; } // If it is a DSI request then get the arguments from the DSI req // and convert that into parameters. cachedArguments = nvListToParameterArray( dsiArguments ); } // Good citizen: In the interest of efficiency, we assume // interceptors will be "good citizens" in that they will not // modify the contents of the Parameter[] array. We also assume // they will not change the values of the containing Anys. return cachedArguments; } /** * See ServerRequestInfo for javadocs. */ public TypeCode[] exceptions (){ checkAccess( MID_EXCEPTIONS ); // _REVISIT_ PI RTF Issue: No exception list on server side. throw stdWrapper.piOperationNotSupported2() ; } /** * See ServerRequestInfo for javadocs. */ public String[] contexts (){ checkAccess( MID_CONTEXTS ); // We do not support this because our ORB does not send contexts. throw stdWrapper.piOperationNotSupported3() ; } /** * See ServerRequestInfo for javadocs. */ public String[] operation_context (){ checkAccess( MID_OPERATION_CONTEXT ); // We do not support this because our ORB does not send // operation_context. throw stdWrapper.piOperationNotSupported4() ; } /** * See ServerRequestInfo for javadocs. */ public Any result (){ checkAccess( MID_RESULT ); if( !isDynamic ) { throw stdWrapper.piOperationNotSupported5() ; } if( dsiResult == null ) { throw wrapper.piDsiResultIsNull() ; } // Good citizen: In the interest of efficiency, we assume that // interceptors will not modify the contents of the result Any. // Otherwise, we would need to create a deep copy of the Any. return dsiResult; } /** * See ServerRequestInfo for javadocs. */ public boolean response_expected (){ // access is currently valid for all states: //checkAccess( MID_RESPONSE_EXPECTED ); return !request.isOneWay(); } /** * See ServerRequestInfo for javadocs. */ public Object forward_reference (){ checkAccess( MID_FORWARD_REFERENCE ); // Check to make sure we are in LOCATION_FORWARD // state as per ptc/00-08-06, table 21-2 // footnote 2. if( replyStatus != LOCATION_FORWARD.value ) { throw stdWrapper.invalidPiCall1() ; } return getForwardRequestException().forward; } /** * See ServerRequestInfo for javadocs. */ public org.omg.IOP.ServiceContext get_request_service_context( int id ) { checkAccess( MID_GET_REQUEST_SERVICE_CONTEXT ); if( cachedRequestServiceContexts == null ) { cachedRequestServiceContexts = new HashMap(); } return getServiceContext( cachedRequestServiceContexts, request.getRequestServiceContexts(), id ); } /** * See ServerRequestInfo for javadocs. */ public org.omg.IOP.ServiceContext get_reply_service_context( int id ) { checkAccess( MID_GET_REPLY_SERVICE_CONTEXT ); if( cachedReplyServiceContexts == null ) { cachedReplyServiceContexts = new HashMap(); } return getServiceContext( cachedReplyServiceContexts, replyMessage.getServiceContexts(), id ); } /* ********************************************************************** * Private-scope classes and methods **********************************************************************/ // A command encapsulating a request to add a reply service context. // These commands are enqueued until we have a handle on the actual // reply service context, at which point they are executed. private class AddReplyServiceContextCommand { ServiceContext service_context; boolean replace; } // Adds the given add reply service context command to the queue of // such commands. If a command is detected to have the same id as // the service context in this command, and replace is false, // BAD_INV_ORDER is thrown. If replace is true, the original command // in the queue is replaced by this command. private void enqueue( AddReplyServiceContextCommand addReply ) { int size = addReplyServiceContextQueue.size(); boolean found = false; for( int i = 0; i < size; i++ ) { AddReplyServiceContextCommand cmd = (AddReplyServiceContextCommand) addReplyServiceContextQueue.get( i ); if( cmd.service_context.context_id == addReply.service_context.context_id ) { found = true; if( addReply.replace ) { addReplyServiceContextQueue.set( i, addReply ); } else { throw stdWrapper.serviceContextAddFailed( new Integer( cmd.service_context.context_id ) ) ; } break; } } if( !found ) { addReplyServiceContextQueue.add( addReply ); } } /* ********************************************************************** * Package and protected-scope methods **********************************************************************/ /** * Overridden from RequestInfoImpl. This version calls the super * and then, if we are changing to ending points, executes all * enqueued AddReplyServiceContextCommands. */ protected void setCurrentExecutionPoint( int executionPoint ) { super.setCurrentExecutionPoint( executionPoint ); // If we are transitioning to ending point, we will now have a pointer // to the reply service contexts, so we can execute all queued // add reply service context requests. if( (executionPoint == EXECUTION_POINT_ENDING) && (addReplyServiceContextQueue != null) ) { int size = addReplyServiceContextQueue.size(); for( int i = 0; i < size; i++ ) { AddReplyServiceContextCommand addReply = (AddReplyServiceContextCommand) addReplyServiceContextQueue.get( i ); try { add_reply_service_context( addReply.service_context, addReply.replace ); } catch( BAD_INV_ORDER e ) { // _REVISIT_ The only way this can happen is if during // rrsc or rr, the interceptor tried to add with // replace=false to a service context that is present in // the reply message. At that time there was no way for // us to check for this, so the best we can do is ignore // the original request. } } // We specifically do not empty the SC queue so that if // the interceptor raises an exception the queued service contexts // will be put in the exception response. } } /** * Stores the various sources of information used for this info object. */ protected void setInfo( CorbaMessageMediator request, ObjectAdapter oa, byte[] objectId, ObjectKeyTemplate oktemp ) { this.request = request; this.objectId = objectId; this.oktemp = oktemp; this.objectAdapter = oa ; this.connection = (com.sun.corba.se.spi.legacy.connection.Connection) request.getConnection(); } /** * Stores the various sources of information used for this info object. */ protected void setDSIArguments( NVList arguments ) { this.dsiArguments = arguments; } /** * Stores the various sources of information used for this info object. */ protected void setDSIException( Any exception ) { this.dsiException = exception; // Clear cached exception value: cachedSendingException = null; } /** * Stores the various sources of information used for this info object. */ protected void setDSIResult( Any result ) { this.dsiResult = result; } /** * Sets the exception to be returned by received_exception and * received_exception_id. */ protected void setException( Exception exception ) { super.setException( exception ); // Make sure DSIException is null because this is the more recent one. this.dsiException = null; // Clear cached exception value: cachedSendingException = null; } /** * Stores the various sources of information used for this info object. */ protected void setInfo( java.lang.Object servant, String targetMostDerivedInterface ) { this.servant = servant; this.targetMostDerivedInterface = targetMostDerivedInterface; this.isDynamic = (servant instanceof org.omg.PortableServer.DynamicImplementation) || (servant instanceof org.omg.CORBA.DynamicImplementation); } /** * Set reply message */ void setReplyMessage( ReplyMessage replyMessage ) { this.replyMessage = replyMessage; } /** * Overridden from RequestInfoImpl. Calls the super class, then * sets the ending point call depending on the reply status. */ protected void setReplyStatus( short replyStatus ) { super.setReplyStatus( replyStatus ); switch( replyStatus ) { case SUCCESSFUL.value: endingPointCall = CALL_SEND_REPLY; break; case SYSTEM_EXCEPTION.value: case USER_EXCEPTION.value: endingPointCall = CALL_SEND_EXCEPTION; break; case LOCATION_FORWARD.value: case TRANSPORT_RETRY.value: endingPointCall = CALL_SEND_OTHER; break; } } /** * Release the servant object so the user has control over its lifetime. * Called after receive_request is finished executing. */ void releaseServant() { this.servant = null; } /** * Sets the forwardRequestRaisedInEnding flag to true, indicating that * a server ending point has raised location forward at some point. */ void setForwardRequestRaisedInEnding() { this.forwardRequestRaisedInEnding = true; } /** * Returns true if ForwardRequest was raised by a server ending point * or false otherwise. */ boolean isForwardRequestRaisedInEnding() { return this.forwardRequestRaisedInEnding; } /** * Returns true if this is a dynamic invocation, or false if not */ boolean isDynamic() { return this.isDynamic; } /** * See description for RequestInfoImpl.checkAccess */ protected void checkAccess( int methodID ) { // Make sure currentPoint matches the appropriate index in the // validCall table: int validCallIndex = 0; switch( currentExecutionPoint ) { case EXECUTION_POINT_STARTING: validCallIndex = 0; break; case EXECUTION_POINT_INTERMEDIATE: validCallIndex = 1; break; case EXECUTION_POINT_ENDING: switch( endingPointCall ) { case CALL_SEND_REPLY: validCallIndex = 2; break; case CALL_SEND_EXCEPTION: validCallIndex = 3; break; case CALL_SEND_OTHER: validCallIndex = 4; break; } break; } // Check the validCall table: if( !validCall[methodID][validCallIndex] ) { throw stdWrapper.invalidPiCall2() ; } } }