InterceptorInvoker.java revision 608:7e06bf1dcb09
1/*
2 * Copyright (c) 2000, 2003, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25package com.sun.corba.se.impl.interceptors;
26
27import org.omg.CORBA.CompletionStatus;
28import org.omg.CORBA.INTERNAL;
29import org.omg.CORBA.SystemException;
30import org.omg.CORBA.portable.Delegate;
31import org.omg.PortableInterceptor.LOCATION_FORWARD;
32import org.omg.PortableInterceptor.SUCCESSFUL;
33import org.omg.PortableInterceptor.SYSTEM_EXCEPTION;
34import org.omg.PortableInterceptor.TRANSPORT_RETRY;
35import org.omg.PortableInterceptor.USER_EXCEPTION;
36import org.omg.PortableInterceptor.ClientRequestInfo;
37import org.omg.PortableInterceptor.ClientRequestInterceptor;
38import org.omg.PortableInterceptor.ForwardRequest;
39import org.omg.PortableInterceptor.IORInterceptor;
40import org.omg.PortableInterceptor.IORInterceptor_3_0;
41import org.omg.PortableInterceptor.ServerRequestInfo;
42import org.omg.PortableInterceptor.ServerRequestInterceptor;
43import org.omg.PortableInterceptor.ObjectReferenceTemplate;
44
45import com.sun.corba.se.spi.ior.IOR;
46import com.sun.corba.se.spi.oa.ObjectAdapter;
47import com.sun.corba.se.spi.orb.ORB;
48import com.sun.corba.se.impl.orbutil.ORBUtility;
49
50/**
51 * Handles invocation of interceptors.  Has specific knowledge of how to
52 * invoke IOR, ClientRequest, and ServerRequest interceptors.
53 * Makes use of the InterceptorList to retrieve the list of interceptors to
54 * be invoked.  Most methods in this class are package scope so that they
55 * may only be called from the PIHandlerImpl.
56 */
57public class InterceptorInvoker {
58
59    // The ORB
60    private ORB orb;
61
62    // The list of interceptors to be invoked
63    private InterceptorList interceptorList;
64
65    // True if interceptors are to be invoked, or false if not
66    // Note: This is a global enable/disable flag, whereas the enable flag
67    // in the RequestInfoStack in PIHandlerImpl is only for a particular Thread.
68    private boolean enabled = false;
69
70    // PICurrent variable.
71    private PICurrent current;
72
73    // NOTE: Be careful about adding additional attributes to this class.
74    // Multiple threads may be calling methods on this invoker at the same
75    // time.
76
77    /**
78     * Creates a new Interceptor Invoker.  Constructor is package scope so
79     * only the ORB can create it.  The invoker is initially disabled, and
80     * must be explicitly enabled using setEnabled().
81     */
82    InterceptorInvoker( ORB orb, InterceptorList interceptorList,
83                        PICurrent piCurrent )
84    {
85        this.orb = orb;
86        this.interceptorList = interceptorList;
87        this.enabled = false;
88        this.current = piCurrent;
89    }
90
91    /**
92     * Enables or disables the interceptor invoker
93     */
94    void setEnabled( boolean enabled ) {
95        this.enabled = enabled;
96    }
97
98    /*
99     **********************************************************************
100     * IOR Interceptor invocation
101     **********************************************************************/
102
103    /**
104     * Called when a new POA is created.
105     *
106     * @param oa The Object Adapter associated with the IOR interceptor.
107     */
108    void objectAdapterCreated( ObjectAdapter oa ) {
109        // If invocation is not yet enabled, don't do anything.
110        if( enabled ) {
111            // Create IORInfo object to pass to IORInterceptors:
112            IORInfoImpl info = new IORInfoImpl( oa );
113
114            // Call each IORInterceptor:
115            IORInterceptor[] iorInterceptors =
116                (IORInterceptor[])interceptorList.getInterceptors(
117                InterceptorList.INTERCEPTOR_TYPE_IOR );
118            int size = iorInterceptors.length;
119
120            // Implementation note:
121            // This loop counts backwards for greater efficiency.
122            // Benchmarks have shown that counting down is more efficient
123            // than counting up in Java for loops, as a compare to zero is
124            // faster than a subtract and compare to zero.  In this case,
125            // it doesn't really matter much, but it's simply a force of habit.
126
127            for( int i = (size - 1); i >= 0; i-- ) {
128                IORInterceptor interceptor = iorInterceptors[i];
129                try {
130                    interceptor.establish_components( info );
131                }
132                catch( Exception e ) {
133                    // as per PI spec (orbos/99-12-02 sec 7.2.1), if
134                    // establish_components throws an exception, ignore it.
135                }
136            }
137
138            // Change the state so that only template operations are valid
139            info.makeStateEstablished() ;
140
141            for( int i = (size - 1); i >= 0; i-- ) {
142                IORInterceptor interceptor = iorInterceptors[i];
143                if (interceptor instanceof IORInterceptor_3_0) {
144                    IORInterceptor_3_0 interceptor30 = (IORInterceptor_3_0)interceptor ;
145                    // Note that exceptions here are NOT ignored, as per the
146                    // ORT spec (orbos/01-01-04)
147                    interceptor30.components_established( info );
148                }
149            }
150
151            // Change the state so that no operations are valid,
152            // in case a reference to info escapes this scope.
153            // This also completes the actions associated with the
154            // template interceptors on this POA.
155            info.makeStateDone() ;
156        }
157    }
158
159    void adapterManagerStateChanged( int managerId, short newState )
160    {
161        if (enabled) {
162            IORInterceptor[] interceptors =
163                (IORInterceptor[])interceptorList.getInterceptors(
164                InterceptorList.INTERCEPTOR_TYPE_IOR );
165            int size = interceptors.length;
166
167            for( int i = (size - 1); i >= 0; i-- ) {
168                try {
169                    IORInterceptor interceptor = interceptors[i];
170                    if (interceptor instanceof IORInterceptor_3_0) {
171                        IORInterceptor_3_0 interceptor30 = (IORInterceptor_3_0)interceptor ;
172                        interceptor30.adapter_manager_state_changed( managerId,
173                            newState );
174                    }
175                } catch (Exception exc) {
176                    // No-op: ignore exception in this case
177                }
178            }
179        }
180    }
181
182    void adapterStateChanged( ObjectReferenceTemplate[] templates,
183        short newState )
184    {
185        if (enabled) {
186            IORInterceptor[] interceptors =
187                (IORInterceptor[])interceptorList.getInterceptors(
188                InterceptorList.INTERCEPTOR_TYPE_IOR );
189            int size = interceptors.length;
190
191            for( int i = (size - 1); i >= 0; i-- ) {
192                try {
193                    IORInterceptor interceptor = interceptors[i];
194                    if (interceptor instanceof IORInterceptor_3_0) {
195                        IORInterceptor_3_0 interceptor30 = (IORInterceptor_3_0)interceptor ;
196                        interceptor30.adapter_state_changed( templates, newState );
197                    }
198                } catch (Exception exc) {
199                    // No-op: ignore exception in this case
200                }
201            }
202        }
203    }
204
205    /*
206     **********************************************************************
207     * Client Interceptor invocation
208     **********************************************************************/
209
210    /**
211     * Invokes either send_request, or send_poll, depending on the value
212     * of info.getStartingPointCall()
213     */
214    void invokeClientInterceptorStartingPoint( ClientRequestInfoImpl info ) {
215        // If invocation is not yet enabled, don't do anything.
216        if( enabled ) {
217            try {
218                // Make a a fresh slot table available to TSC in case
219                // interceptors need to make out calls.
220                // Client's TSC is now RSC via RequestInfo.
221                current.pushSlotTable( );
222                info.setPICurrentPushed( true );
223                info.setCurrentExecutionPoint( info.EXECUTION_POINT_STARTING );
224
225                // Get all ClientRequestInterceptors:
226                ClientRequestInterceptor[] clientInterceptors =
227                    (ClientRequestInterceptor[])interceptorList.
228                    getInterceptors( InterceptorList.INTERCEPTOR_TYPE_CLIENT );
229                int size = clientInterceptors.length;
230
231                // We will assume that all interceptors returned successfully,
232                // and adjust the flowStackIndex to the appropriate value if
233                // we later discover otherwise.
234                int flowStackIndex = size;
235                boolean continueProcessing = true;
236
237                // Determine whether we are calling send_request or send_poll:
238                // (This is currently commented out because our ORB does not
239                // yet support the Messaging specification, so send_poll will
240                // never occur.  Once we have implemented messaging, this may
241                // be uncommented.)
242                // int startingPointCall = info.getStartingPointCall();
243                for( int i = 0; continueProcessing && (i < size); i++ ) {
244                    try {
245                        clientInterceptors[i].send_request( info );
246
247                        // Again, it is not necessary for a switch here, since
248                        // there is only one starting point call type (see
249                        // above comment).
250
251                        //switch( startingPointCall ) {
252                        //case ClientRequestInfoImpl.CALL_SEND_REQUEST:
253                            //clientInterceptors[i].send_request( info );
254                            //break;
255                        //case ClientRequestInfoImpl.CALL_SEND_POLL:
256                            //clientInterceptors[i].send_poll( info );
257                            //break;
258                        //}
259
260                    }
261                    catch( ForwardRequest e ) {
262                        // as per PI spec (orbos/99-12-02 sec 5.2.1.), if
263                        // interception point throws a ForwardRequest,
264                        // no other Interceptors' send_request operations are
265                        // called.
266                        flowStackIndex = i;
267                        info.setForwardRequest( e );
268                        info.setEndingPointCall(
269                            ClientRequestInfoImpl.CALL_RECEIVE_OTHER );
270                        info.setReplyStatus( LOCATION_FORWARD.value );
271
272                        updateClientRequestDispatcherForward( info );
273
274                        // For some reason, using break here causes the VM on
275                        // NT to lose track of the value of flowStackIndex
276                        // after exiting the for loop.  I changed this to
277                        // check a boolean value instead and it seems to work
278                        // fine.
279                        continueProcessing = false;
280                    }
281                    catch( SystemException e ) {
282                        // as per PI spec (orbos/99-12-02 sec 5.2.1.), if
283                        // interception point throws a SystemException,
284                        // no other Interceptors' send_request operations are
285                        // called.
286                        flowStackIndex = i;
287                        info.setEndingPointCall(
288                            ClientRequestInfoImpl.CALL_RECEIVE_EXCEPTION );
289                        info.setReplyStatus( SYSTEM_EXCEPTION.value );
290                        info.setException( e );
291
292                        // For some reason, using break here causes the VM on
293                        // NT to lose track of the value of flowStackIndex
294                        // after exiting the for loop.  I changed this to
295                        // check a boolean value instead and it seems to
296                        // work fine.
297                        continueProcessing = false;
298                    }
299                }
300
301                // Remember where we left off in the flow stack:
302                info.setFlowStackIndex( flowStackIndex );
303            }
304            finally {
305                // Make the SlotTable fresh for the next interception point.
306                current.resetSlotTable( );
307            }
308        } // end enabled check
309    }
310
311    /**
312     * Invokes either receive_reply, receive_exception, or receive_other,
313     * depending on the value of info.getEndingPointCall()
314     */
315    void invokeClientInterceptorEndingPoint( ClientRequestInfoImpl info ) {
316        // If invocation is not yet enabled, don't do anything.
317        if( enabled ) {
318            try {
319                // NOTE: It is assumed someplace else prepared a
320                // fresh TSC slot table.
321
322                info.setCurrentExecutionPoint( info.EXECUTION_POINT_ENDING );
323
324                // Get all ClientRequestInterceptors:
325                ClientRequestInterceptor[] clientInterceptors =
326                    (ClientRequestInterceptor[])interceptorList.
327                    getInterceptors( InterceptorList.INTERCEPTOR_TYPE_CLIENT );
328                int flowStackIndex = info.getFlowStackIndex();
329
330                // Determine whether we are calling receive_reply,
331                // receive_exception, or receive_other:
332                int endingPointCall = info.getEndingPointCall();
333
334                // If we would be calling RECEIVE_REPLY, but this is a
335                // one-way call, override this and call receive_other:
336                if( ( endingPointCall ==
337                      ClientRequestInfoImpl.CALL_RECEIVE_REPLY ) &&
338                    info.getIsOneWay() )
339                {
340                    endingPointCall = ClientRequestInfoImpl.CALL_RECEIVE_OTHER;
341                    info.setEndingPointCall( endingPointCall );
342                }
343
344                // Only step through the interceptors whose starting points
345                // have successfully returned.
346                // Unlike the previous loop, this one counts backwards for a
347                // reason - we must execute these in the reverse order of the
348                // starting points.
349                for( int i = (flowStackIndex - 1); i >= 0; i-- ) {
350
351                    try {
352                        switch( endingPointCall ) {
353                        case ClientRequestInfoImpl.CALL_RECEIVE_REPLY:
354                            clientInterceptors[i].receive_reply( info );
355                            break;
356                        case ClientRequestInfoImpl.CALL_RECEIVE_EXCEPTION:
357                            clientInterceptors[i].receive_exception( info );
358                            break;
359                        case ClientRequestInfoImpl.CALL_RECEIVE_OTHER:
360                            clientInterceptors[i].receive_other( info );
361                            break;
362                        }
363                    }
364                    catch( ForwardRequest e ) {
365
366                        // as per PI spec (orbos/99-12-02 sec 5.2.1.), if
367                        // interception point throws a ForwardException,
368                        // ending point call changes to receive_other.
369                        endingPointCall =
370                            ClientRequestInfoImpl.CALL_RECEIVE_OTHER;
371                        info.setEndingPointCall( endingPointCall );
372                        info.setReplyStatus( LOCATION_FORWARD.value );
373                        info.setForwardRequest( e );
374                        updateClientRequestDispatcherForward( info );
375                    }
376                    catch( SystemException e ) {
377
378                        // as per PI spec (orbos/99-12-02 sec 5.2.1.), if
379                        // interception point throws a SystemException,
380                        // ending point call changes to receive_exception.
381                        endingPointCall =
382                            ClientRequestInfoImpl.CALL_RECEIVE_EXCEPTION;
383                        info.setEndingPointCall( endingPointCall );
384                        info.setReplyStatus( SYSTEM_EXCEPTION.value );
385                        info.setException( e );
386                    }
387                }
388            }
389            finally {
390                // See doc for setPICurrentPushed as to why this is necessary.
391                // Check info for null in case errors happen before initiate.
392                if (info != null && info.isPICurrentPushed()) {
393                    current.popSlotTable( );
394                    // After the pop, original client's TSC slot table
395                    // remains avaiable via PICurrent.
396                }
397            }
398        } // end enabled check
399    }
400
401    /*
402     **********************************************************************
403     * Server Interceptor invocation
404     **********************************************************************/
405
406    /**
407     * Invokes receive_request_service_context interception points.
408     */
409    void invokeServerInterceptorStartingPoint( ServerRequestInfoImpl info ) {
410        // If invocation is not yet enabled, don't do anything.
411        if( enabled ) {
412            try {
413                // Make a fresh slot table for RSC.
414                current.pushSlotTable();
415                info.setSlotTable(current.getSlotTable());
416
417                // Make a fresh slot table for TSC in case
418                // interceptors need to make out calls.
419                current.pushSlotTable( );
420
421                info.setCurrentExecutionPoint( info.EXECUTION_POINT_STARTING );
422
423                // Get all ServerRequestInterceptors:
424                ServerRequestInterceptor[] serverInterceptors =
425                    (ServerRequestInterceptor[])interceptorList.
426                    getInterceptors( InterceptorList.INTERCEPTOR_TYPE_SERVER );
427                int size = serverInterceptors.length;
428
429                // We will assume that all interceptors returned successfully,
430                // and adjust the flowStackIndex to the appropriate value if
431                // we later discover otherwise.
432                int flowStackIndex = size;
433                boolean continueProcessing = true;
434
435                // Currently, there is only one server-side starting point
436                // interceptor called receive_request_service_contexts.
437                for( int i = 0; continueProcessing && (i < size); i++ ) {
438
439                    try {
440                        serverInterceptors[i].
441                            receive_request_service_contexts( info );
442                    }
443                    catch( ForwardRequest e ) {
444                        // as per PI spec (orbos/99-12-02 sec 5.3.1.), if
445                        // interception point throws a ForwardRequest,
446                        // no other Interceptors' starting points are
447                        // called and send_other is called.
448                        flowStackIndex = i;
449                        info.setForwardRequest( e );
450                        info.setIntermediatePointCall(
451                            ServerRequestInfoImpl.CALL_INTERMEDIATE_NONE );
452                        info.setEndingPointCall(
453                            ServerRequestInfoImpl.CALL_SEND_OTHER );
454                        info.setReplyStatus( LOCATION_FORWARD.value );
455
456                        // For some reason, using break here causes the VM on
457                        // NT to lose track of the value of flowStackIndex
458                        // after exiting the for loop.  I changed this to
459                        // check a boolean value instead and it seems to work
460                        // fine.
461                        continueProcessing = false;
462                    }
463                    catch( SystemException e ) {
464
465                        // as per PI spec (orbos/99-12-02 sec 5.3.1.), if
466                        // interception point throws a SystemException,
467                        // no other Interceptors' starting points are
468                        // called.
469                        flowStackIndex = i;
470                        info.setException( e );
471                        info.setIntermediatePointCall(
472                            ServerRequestInfoImpl.CALL_INTERMEDIATE_NONE );
473                        info.setEndingPointCall(
474                            ServerRequestInfoImpl.CALL_SEND_EXCEPTION );
475                        info.setReplyStatus( SYSTEM_EXCEPTION.value );
476
477                        // For some reason, using break here causes the VM on
478                        // NT to lose track of the value of flowStackIndex
479                        // after exiting the for loop.  I changed this to
480                        // check a boolean value instead and it seems to
481                        // work fine.
482                        continueProcessing = false;
483                    }
484
485                }
486
487                // Remember where we left off in the flow stack:
488                info.setFlowStackIndex( flowStackIndex );
489            }
490            finally {
491                // The remaining points, ServantManager and Servant
492                // all run in the same logical thread.
493                current.popSlotTable( );
494                // Now TSC and RSC are equivalent.
495            }
496        } // end enabled check
497    }
498
499    /**
500     * Invokes receive_request interception points
501     */
502    void invokeServerInterceptorIntermediatePoint(
503        ServerRequestInfoImpl info )
504    {
505        int intermediatePointCall = info.getIntermediatePointCall();
506        // If invocation is not yet enabled, don't do anything.
507        if( enabled && ( intermediatePointCall !=
508                         ServerRequestInfoImpl.CALL_INTERMEDIATE_NONE ) )
509        {
510            // NOTE: do not touch the slotStack.  The RSC and TSC are
511            // equivalent at this point.
512
513            info.setCurrentExecutionPoint( info.EXECUTION_POINT_INTERMEDIATE );
514
515            // Get all ServerRequestInterceptors:
516            ServerRequestInterceptor[] serverInterceptors =
517                (ServerRequestInterceptor[])
518                interceptorList.getInterceptors(
519                InterceptorList.INTERCEPTOR_TYPE_SERVER );
520            int size = serverInterceptors.length;
521
522            // Currently, there is only one server-side intermediate point
523            // interceptor called receive_request.
524            for( int i = 0; i < size; i++ ) {
525
526                try {
527                    serverInterceptors[i].receive_request( info );
528                }
529                catch( ForwardRequest e ) {
530
531                    // as per PI spec (orbos/99-12-02 sec 5.3.1.), if
532                    // interception point throws a ForwardRequest,
533                    // no other Interceptors' intermediate points are
534                    // called and send_other is called.
535                    info.setForwardRequest( e );
536                    info.setEndingPointCall(
537                        ServerRequestInfoImpl.CALL_SEND_OTHER );
538                    info.setReplyStatus( LOCATION_FORWARD.value );
539                    break;
540                }
541                catch( SystemException e ) {
542
543                    // as per PI spec (orbos/99-12-02 sec 5.3.1.), if
544                    // interception point throws a SystemException,
545                    // no other Interceptors' starting points are
546                    // called.
547                    info.setException( e );
548                    info.setEndingPointCall(
549                        ServerRequestInfoImpl.CALL_SEND_EXCEPTION );
550                    info.setReplyStatus( SYSTEM_EXCEPTION.value );
551                    break;
552                }
553            }
554        } // end enabled check
555    }
556
557    /**
558     * Invokes either send_reply, send_exception, or send_other,
559     * depending on the value of info.getEndingPointCall()
560     */
561    void invokeServerInterceptorEndingPoint( ServerRequestInfoImpl info ) {
562        // If invocation is not yet enabled, don't do anything.
563        if( enabled ) {
564            try {
565                // NOTE: do not touch the slotStack.  The RSC and TSC are
566                // equivalent at this point.
567
568                // REVISIT: This is moved out to PIHandlerImpl until dispatch
569                // path is rearchitected.  It must be there so that
570                // it always gets executed so if an interceptor raises
571                // an exception any service contexts added in earlier points
572                // this point get put in the exception reply (via the SC Q).
573                //info.setCurrentExecutionPoint( info.EXECUTION_POINT_ENDING );
574
575                // Get all ServerRequestInterceptors:
576                ServerRequestInterceptor[] serverInterceptors =
577                    (ServerRequestInterceptor[])interceptorList.
578                    getInterceptors( InterceptorList.INTERCEPTOR_TYPE_SERVER );
579                int flowStackIndex = info.getFlowStackIndex();
580
581                // Determine whether we are calling
582                // send_exception, or send_other:
583                int endingPointCall = info.getEndingPointCall();
584
585                // Only step through the interceptors whose starting points
586                // have successfully returned.
587                for( int i = (flowStackIndex - 1); i >= 0; i-- ) {
588                    try {
589                        switch( endingPointCall ) {
590                        case ServerRequestInfoImpl.CALL_SEND_REPLY:
591                            serverInterceptors[i].send_reply( info );
592                            break;
593                        case ServerRequestInfoImpl.CALL_SEND_EXCEPTION:
594                            serverInterceptors[i].send_exception( info );
595                            break;
596                        case ServerRequestInfoImpl.CALL_SEND_OTHER:
597                            serverInterceptors[i].send_other( info );
598                            break;
599                        }
600                    }
601                    catch( ForwardRequest e ) {
602                        // as per PI spec (orbos/99-12-02 sec 5.3.1.), if
603                        // interception point throws a ForwardException,
604                        // ending point call changes to receive_other.
605                        endingPointCall =
606                            ServerRequestInfoImpl.CALL_SEND_OTHER;
607                        info.setEndingPointCall( endingPointCall );
608                        info.setForwardRequest( e );
609                        info.setReplyStatus( LOCATION_FORWARD.value );
610                        info.setForwardRequestRaisedInEnding();
611                    }
612                    catch( SystemException e ) {
613                        // as per PI spec (orbos/99-12-02 sec 5.3.1.), if
614                        // interception point throws a SystemException,
615                        // ending point call changes to send_exception.
616                        endingPointCall =
617                            ServerRequestInfoImpl.CALL_SEND_EXCEPTION;
618                        info.setEndingPointCall( endingPointCall );
619                        info.setException( e );
620                        info.setReplyStatus( SYSTEM_EXCEPTION.value );
621                    }
622                }
623
624                // Remember that all interceptors' starting and ending points
625                // have already been executed so we need not do anything.
626                info.setAlreadyExecuted( true );
627            }
628            finally {
629                // Get rid of the Server side RSC.
630                current.popSlotTable();
631            }
632        } // end enabled check
633    }
634
635    /*
636     **********************************************************************
637     * Private utility methods
638     **********************************************************************/
639
640    /**
641     * Update the client delegate in the event of a ForwardRequest, given the
642     * information in the passed-in info object.
643     */
644    private void updateClientRequestDispatcherForward(
645        ClientRequestInfoImpl info )
646    {
647        ForwardRequest forwardRequest = info.getForwardRequestException();
648
649        // ForwardRequest may be null if the forwarded IOR is set internal
650        // to the ClientRequestDispatcher rather than explicitly through Portable
651        // Interceptors.  In this case, we need not update the client
652        // delegate ForwardRequest object.
653        if( forwardRequest != null ) {
654            org.omg.CORBA.Object object = forwardRequest.forward;
655
656            // Convert the forward object into an IOR:
657            IOR ior = ORBUtility.getIOR( object ) ;
658            info.setLocatedIOR( ior );
659        }
660    }
661
662}
663