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 
  26 package com.sun.corba.se.spi.servicecontext;
  27 
  28 import java.lang.reflect.InvocationTargetException ;
  29 import java.lang.reflect.Modifier ;
  30 import java.lang.reflect.Field ;
  31 import java.lang.reflect.Constructor ;
  32 import java.util.*;
  33 
  34 import org.omg.CORBA.OctetSeqHelper;
  35 import org.omg.CORBA.SystemException;
  36 import org.omg.CORBA.INTERNAL;
  37 import org.omg.CORBA.CompletionStatus;
  38 import org.omg.CORBA_2_3.portable.OutputStream ;
  39 import org.omg.CORBA_2_3.portable.InputStream ;
  40 
  41 import com.sun.org.omg.SendingContext.CodeBase;
  42 
  43 import com.sun.corba.se.spi.ior.iiop.GIOPVersion;
  44 
  45 import com.sun.corba.se.spi.orb.ORB ;
  46 
  47 import com.sun.corba.se.spi.logging.CORBALogDomains;
  48 
  49 
  50 import com.sun.corba.se.spi.servicecontext.ServiceContext ;
  51 import com.sun.corba.se.spi.servicecontext.ServiceContextRegistry ;
  52 import com.sun.corba.se.spi.servicecontext.ServiceContextData ;
  53 import com.sun.corba.se.spi.servicecontext.UnknownServiceContext ;
  54 
  55 import com.sun.corba.se.impl.encoding.CDRInputStream;
  56 import com.sun.corba.se.impl.encoding.EncapsInputStream ;
  57 import com.sun.corba.se.impl.orbutil.ORBUtility ;
  58 import com.sun.corba.se.impl.util.Utility ;
  59 import com.sun.corba.se.impl.logging.ORBUtilSystemException ;
  60 
  61 import sun.corba.EncapsInputStreamFactory;
  62 
  63 
  64 public 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 }