ServiceContexts.java revision 608:7e06bf1dcb09
1/*
2 * Copyright (c) 1999, 2013, 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 */
25
26package com.sun.corba.se.spi.servicecontext;
27
28import java.lang.reflect.InvocationTargetException ;
29import java.lang.reflect.Modifier ;
30import java.lang.reflect.Field ;
31import java.lang.reflect.Constructor ;
32import java.util.*;
33
34import org.omg.CORBA.OctetSeqHelper;
35import org.omg.CORBA.SystemException;
36import org.omg.CORBA.INTERNAL;
37import org.omg.CORBA.CompletionStatus;
38import org.omg.CORBA_2_3.portable.OutputStream ;
39import org.omg.CORBA_2_3.portable.InputStream ;
40
41import com.sun.org.omg.SendingContext.CodeBase;
42
43import com.sun.corba.se.spi.ior.iiop.GIOPVersion;
44
45import com.sun.corba.se.spi.orb.ORB ;
46
47import com.sun.corba.se.spi.logging.CORBALogDomains;
48
49
50import com.sun.corba.se.spi.servicecontext.ServiceContext ;
51import com.sun.corba.se.spi.servicecontext.ServiceContextRegistry ;
52import com.sun.corba.se.spi.servicecontext.ServiceContextData ;
53import com.sun.corba.se.spi.servicecontext.UnknownServiceContext ;
54
55import com.sun.corba.se.impl.encoding.CDRInputStream;
56import com.sun.corba.se.impl.encoding.EncapsInputStream ;
57import com.sun.corba.se.impl.orbutil.ORBUtility ;
58import com.sun.corba.se.impl.util.Utility ;
59import com.sun.corba.se.impl.logging.ORBUtilSystemException ;
60
61import sun.corba.EncapsInputStreamFactory;
62
63
64public class ServiceContexts {
65    private static boolean isDebugging( OutputStream os )
66    {
67        ORB orb = (ORB)(os.orb()) ;
68        if (orb==null)
69            return false ;
70        return orb.serviceContextDebugFlag ;
71    }
72
73    private static boolean isDebugging( InputStream is )
74    {
75        ORB orb = (ORB)(is.orb()) ;
76        if (orb==null)
77            return false ;
78        return orb.serviceContextDebugFlag ;
79    }
80
81    private void dprint( String msg )
82    {
83        ORBUtility.dprint( this, msg ) ;
84    }
85
86    public static void writeNullServiceContext( OutputStream os )
87    {
88        if (isDebugging(os))
89            ORBUtility.dprint( "ServiceContexts", "Writing null service context" ) ;
90        os.write_long( 0 ) ;
91    }
92
93    /**
94     * Given the input stream, this fills our service
95     * context map.  See the definition of scMap for
96     * details.  Creates a HashMap.
97     *
98     * Note that we don't actually unmarshal the
99     * bytes of the service contexts here.  That is
100     * done when they are actually requested via
101     * get(int).
102     */
103    private void createMapFromInputStream(InputStream is)
104    {
105        orb = (ORB)(is.orb()) ;
106        if (orb.serviceContextDebugFlag)
107            dprint( "Constructing ServiceContexts from input stream" ) ;
108
109        int numValid = is.read_long() ;
110
111        if (orb.serviceContextDebugFlag)
112            dprint("Number of service contexts = " + numValid);
113
114        for (int ctr = 0; ctr < numValid; ctr++) {
115            int scId = is.read_long();
116
117            if (orb.serviceContextDebugFlag)
118                dprint("Reading service context id " + scId);
119
120            byte[] data = OctetSeqHelper.read(is);
121
122            if (orb.serviceContextDebugFlag)
123                dprint("Service context" + scId + " length: " + data.length);
124
125            scMap.put(new Integer(scId), data);
126        }
127    }
128
129    public ServiceContexts( ORB orb )
130    {
131        this.orb = orb ;
132        wrapper = ORBUtilSystemException.get( orb,
133            CORBALogDomains.RPC_PROTOCOL ) ;
134
135        addAlignmentOnWrite = false ;
136
137        scMap = new HashMap();
138
139        // Use the GIOP version of the ORB.  Should
140        // be specified in ServiceContext.
141        // See REVISIT below concerning giopVersion.
142        giopVersion = orb.getORBData().getGIOPVersion();
143        codeBase = null ;
144    }
145
146    /**
147     * Read the Service contexts from the input stream.
148     */
149    public ServiceContexts(InputStream s)
150    {
151        this( (ORB)(s.orb()) ) ;
152
153        // We need to store this so that we can have access
154        // to the CodeBase for unmarshaling possible
155        // RMI-IIOP valuetype data within an encapsulation.
156        // (Known case: UnknownExceptionInfo)
157        codeBase = ((CDRInputStream)s).getCodeBase();
158
159        createMapFromInputStream(s);
160
161        // Fix for bug 4904723
162        giopVersion = ((CDRInputStream)s).getGIOPVersion();
163    }
164
165    /**
166     * Find the ServiceContextData for a given scId and unmarshal
167     * the bytes.
168     */
169    private ServiceContext unmarshal(Integer scId, byte[] data) {
170
171        ServiceContextRegistry scr = orb.getServiceContextRegistry();
172
173        ServiceContextData scd = scr.findServiceContextData(scId.intValue());
174        ServiceContext sc = null;
175
176        if (scd == null) {
177            if (orb.serviceContextDebugFlag) {
178                dprint("Could not find ServiceContextData for "
179                       + scId
180                       + " using UnknownServiceContext");
181            }
182
183            sc = new UnknownServiceContext(scId.intValue(), data);
184
185        } else {
186
187            if (orb.serviceContextDebugFlag) {
188                dprint("Found " + scd);
189            }
190
191            // REVISIT.  GIOP version should be specified as
192            // part of a service context's definition, so should
193            // be accessible from ServiceContextData via
194            // its ServiceContext implementation class.
195            //
196            // Since we don't have that, yet, I'm using the GIOP
197            // version of the input stream, presuming that someone
198            // can't send a service context of a later GIOP
199            // version than its stream version.
200            //
201            // Note:  As of Jan 2001, no standard OMG or Sun service contexts
202            // ship wchar data or are defined as using anything but GIOP 1.0 CDR.
203            EncapsInputStream eis
204                = EncapsInputStreamFactory.newEncapsInputStream(orb,
205                                    data,
206                                    data.length,
207                                    giopVersion,
208                                    codeBase);
209            eis.consumeEndian();
210
211            // Now the input stream passed to a ServiceContext
212            // constructor is already the encapsulation input
213            // stream with the endianness read off, so the
214            // service context should just unmarshal its own
215            // data.
216            sc = scd.makeServiceContext(eis, giopVersion);
217            if (sc == null)
218                throw wrapper.svcctxUnmarshalError(
219                    CompletionStatus.COMPLETED_MAYBE);
220        }
221
222        return sc;
223    }
224
225    public void addAlignmentPadding()
226    {
227        // Make service context 12 bytes longer by adding
228        // JAVAIDL_ALIGN_SERVICE_ID service context at end.
229        // The exact length
230        // must be >8 (minimum service context size) and
231        // =4 mod 8, so 12 is the minimum.
232        addAlignmentOnWrite = true ;
233    }
234
235    /**
236     * Hopefully unused scid:  This should be changed to a proper
237     * VMCID aligned value.  REVISIT!
238     */
239    private static final int JAVAIDL_ALIGN_SERVICE_ID = 0xbe1345cd ;
240
241    /**
242     * Write the service contexts to the output stream.
243     *
244     * If they haven't been unmarshaled, we don't have to
245     * unmarshal them.
246     */
247    public void write(OutputStream os, GIOPVersion gv)
248    {
249        if (isDebugging(os)) {
250            dprint( "Writing service contexts to output stream" ) ;
251            Utility.printStackTrace() ;
252        }
253
254        int numsc = scMap.size();
255
256        if (addAlignmentOnWrite) {
257            if (isDebugging(os))
258                dprint( "Adding alignment padding" ) ;
259
260            numsc++ ;
261        }
262
263        if (isDebugging(os))
264            dprint( "Service context has " + numsc + " components"  ) ;
265
266        os.write_long( numsc ) ;
267
268        writeServiceContextsInOrder(os, gv);
269
270        if (addAlignmentOnWrite) {
271            if (isDebugging(os))
272                dprint( "Writing alignment padding" ) ;
273
274            os.write_long( JAVAIDL_ALIGN_SERVICE_ID ) ;
275            os.write_long( 4 ) ;
276            os.write_octet( (byte)0 ) ;
277            os.write_octet( (byte)0 ) ;
278            os.write_octet( (byte)0 ) ;
279            os.write_octet( (byte)0 ) ;
280        }
281
282        if (isDebugging(os))
283            dprint( "Service context writing complete" ) ;
284    }
285
286    /**
287     * Write the service contexts in scMap in a desired order.
288     * Right now, the only special case we have is UnknownExceptionInfo,
289     * so I'm merely writing it last if present.
290     */
291    private void writeServiceContextsInOrder(OutputStream os, GIOPVersion gv) {
292
293        // Temporarily remove this rather than check it per iteration
294        Integer ueInfoId
295            = new Integer(UEInfoServiceContext.SERVICE_CONTEXT_ID);
296
297        Object unknownExceptionInfo = scMap.remove(ueInfoId);
298
299        Iterator iter = scMap.keySet().iterator();
300
301        while (iter.hasNext()) {
302            Integer id = (Integer)iter.next();
303
304            writeMapEntry(os, id, scMap.get(id), gv);
305        }
306
307        // Write the UnknownExceptionInfo service context last
308        // (so it will be after the CodeBase) and restore it in
309        // the map.
310        if (unknownExceptionInfo != null) {
311            writeMapEntry(os, ueInfoId, unknownExceptionInfo, gv);
312
313            scMap.put(ueInfoId, unknownExceptionInfo);
314        }
315    }
316
317    /**
318     * Write the given entry from the scMap to the OutputStream.
319     * See note on giopVersion.  The service context should
320     * know the GIOP version it is meant for.
321     */
322    private void writeMapEntry(OutputStream os, Integer id, Object scObj, GIOPVersion gv) {
323
324        // If it's still in byte[] form, we don't need to
325        // unmarshal it here, just copy the bytes into
326        // the new stream.
327
328        if (scObj instanceof byte[]) {
329            if (isDebugging(os))
330                dprint( "Writing service context bytes for id " + id);
331
332            OctetSeqHelper.write(os, (byte[])scObj);
333
334        } else {
335
336            // We actually unmarshaled it into a ServiceContext
337            // at some point.
338            ServiceContext sc = (ServiceContext)scObj;
339
340            if (isDebugging(os))
341                dprint( "Writing service context " + sc ) ;
342
343            sc.write(os, gv);
344        }
345    }
346
347    /** Add a service context to the stream, if there is not already
348     * a service context in this object with the same id as sc.
349     */
350    public void put( ServiceContext sc )
351    {
352        Integer id = new Integer(sc.getId());
353        scMap.put(id, sc);
354    }
355
356    public void delete( int scId ) {
357        this.delete(new Integer(scId));
358    }
359
360    public void delete(Integer id)
361    {
362        scMap.remove(id)  ;
363    }
364
365    public ServiceContext get(int scId) {
366        return this.get(new Integer(scId));
367    }
368
369    public ServiceContext get(Integer id)
370    {
371        Object result = scMap.get(id);
372        if (result == null)
373            return null ;
374
375        // Lazy unmarshaling on first use.
376        if (result instanceof byte[]) {
377
378            ServiceContext sc = unmarshal(id, (byte[])result);
379
380            scMap.put(id, sc);
381
382            return sc;
383        } else {
384            return (ServiceContext)result;
385        }
386    }
387
388    private ORB orb ;
389
390    /**
391     * Map of all ServiceContext objects in this container.
392     *
393     * Keys are java.lang.Integers for service context IDs.
394     * Values are either instances of ServiceContext or the
395     * unmarshaled byte arrays (unmarshaled on first use).
396     *
397     * This provides a mild optimization if we don't happen to
398     * use a given service context, but it's main advantage is
399     * that it allows us to change the order in which we
400     * unmarshal them.  We need to do the UnknownExceptionInfo service
401     * context after the SendingContextRunTime service context so that we can
402     * get the CodeBase if necessary.
403     */
404    private Map scMap;
405
406    /**
407     * If true, write out a special alignment service context to force the
408     * correct alignment on re-marshalling.
409     */
410    private boolean addAlignmentOnWrite ;
411
412    private CodeBase codeBase;
413    private GIOPVersion giopVersion;
414    private ORBUtilSystemException wrapper ;
415}
416