Source for gnu.CORBA.GIOP.MessageHeader

   1: /* MessageHeader.java -- GIOP message header.
   2:    Copyright (C) 2005 Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package gnu.CORBA.GIOP;
  40: 
  41: import gnu.CORBA.Minor;
  42: import gnu.CORBA.Version;
  43: import gnu.CORBA.CDR.BigEndianInputStream;
  44: import gnu.CORBA.CDR.BigEndianOutputStream;
  45: import gnu.CORBA.CDR.LittleEndianInputStream;
  46: import gnu.CORBA.CDR.LittleEndianOutputStream;
  47: import gnu.CORBA.CDR.AbstractDataInput;
  48: import gnu.CORBA.CDR.AbstractDataOutput;
  49: 
  50: import org.omg.CORBA.MARSHAL;
  51: import org.omg.CORBA.portable.IDLEntity;
  52: 
  53: import java.io.ByteArrayOutputStream;
  54: import java.io.IOException;
  55: import java.io.InputStream;
  56: import java.io.OutputStream;
  57: import java.net.Socket;
  58: import java.util.Arrays;
  59: 
  60: /**
  61:  * The GIOP message header.
  62:  * 
  63:  * @author Audrius Meskauskas (AudriusA@Bioinformatics.org)
  64:  */
  65: public class MessageHeader
  66:   implements IDLEntity
  67: {
  68:   /**
  69:    * Use serialVersionUID for interoperability.
  70:    */
  71:   private static final long serialVersionUID = 1;
  72: 
  73:   /**
  74:    * Request message.
  75:    */
  76:   public static final byte REQUEST = 0;
  77: 
  78:   /**
  79:    * Reply message
  80:    */
  81:   public static final byte REPLY = 1;
  82: 
  83:   /**
  84:    * Cancel request message.
  85:    */
  86:   public static final byte CANCEL_REQUEST = 2;
  87: 
  88:   /**
  89:    * Locate request message, used to check the server ability to process
  90:    * requests for the object reference. This message is also used to get the
  91:    * address where the object reference should be sent.
  92:    */
  93:   public static final byte LOCATE_REQUEST = 3;
  94: 
  95:   /**
  96:    * Locate reply message, sent in response to the {@link #LocateRequest}
  97:    * message.
  98:    */
  99:   public static final byte LOCATE_REPLY = 4;
 100: 
 101:   /**
 102:    * Instruction to close the connection.
 103:    */
 104:   public static final byte CLOSE_CONNECTION = 5;
 105: 
 106:   /**
 107:    * Error report.
 108:    */
 109:   public static final byte MESSAGE_ERROR = 6;
 110: 
 111:   /**
 112:    * The fragment messge, following the previous message that has more fragments
 113:    * flag set. Added in GIOP 1.1
 114:    */
 115:   public static final byte FRAGMENT = 7;
 116: 
 117:   /**
 118:    * This must always be "GIOP".
 119:    */
 120:   public static final byte[] MAGIC = new byte[] { 'G', 'I', 'O', 'P' };
 121: 
 122:   /**
 123:    * The message type names.
 124:    */
 125:   protected static String[] types = new String[] { "Request", "Reply",
 126:     "Cancel", "Locate request", "Locate reply", "Close connection", "Error",
 127:     "Fragment" };
 128: 
 129:   /**
 130:    * The GIOP version. Initialised to 1.0 .
 131:    */
 132:   public Version version;
 133: 
 134:   /**
 135:    * The flags field, introduced since GIOP 1.1.
 136:    */
 137:   public byte flags = 0;
 138: 
 139:   /**
 140:    * The message type.
 141:    */
 142:   public byte message_type = REQUEST;
 143: 
 144:   /**
 145:    * The message size, excluding the message header.
 146:    */
 147:   public int message_size = 0;
 148: 
 149:   /**
 150:    * Create an empty message header, corresponding version 1.0.
 151:    */
 152:   public MessageHeader()
 153:   {
 154:     version = new Version(1, 0);
 155:   }
 156: 
 157:   /**
 158:    * Create an empty message header, corresponding the given version.
 159:    * 
 160:    * @param major the major message header version.
 161:    * @param minor the minot message header version.
 162:    */
 163:   public MessageHeader(int major, int minor)
 164:   {
 165:     version = new Version(major, minor);
 166:   }
 167: 
 168:   /**
 169:    * Checks if the message is encoded in the Big Endian, most significant byte
 170:    * first.
 171:    */
 172:   public boolean isBigEndian()
 173:   {
 174:     return (flags & 0x1) == 0;
 175:   }
 176: 
 177:   /**
 178:    * Checks if the message is partial, and more subsequent fragments follow.
 179:    */
 180:   public boolean moreFragmentsFollow()
 181:   {
 182:     return (flags & 0x2) != 0;
 183:   }
 184: 
 185:   /**
 186:    * Set the encoding to use.
 187:    * 
 188:    * @param use_big_endian if true (default), the Big Endian encoding is used.
 189:    * If false, the Little Endian encoding is used.
 190:    */
 191:   public void setBigEndian(boolean use_big_endian)
 192:   {
 193:     if (use_big_endian)
 194:       flags = (byte) (flags & ~1);
 195:     else
 196:       flags = (byte) (flags | 1);
 197:   }
 198: 
 199:   /**
 200:    * Get the size of the message header itself. So far, it is always 12 bytes.
 201:    */
 202:   public int getHeaderSize()
 203:   {
 204:     return 12;
 205:   }
 206: 
 207:   /**
 208:    * Get the message type as string.
 209:    * 
 210:    * @param type the message type as int (the field {@link message_type}).
 211:    * 
 212:    * @return the message type as string.
 213:    */
 214:   public String getTypeString(int type)
 215:   {
 216:     try
 217:       {
 218:         return types[type];
 219:       }
 220:     catch (ArrayIndexOutOfBoundsException ex)
 221:       {
 222:         return "unknown type (" + type + ")";
 223:       }
 224:   }
 225: 
 226:   /**
 227:    * Creates reply header, matching the message header version number.
 228:    * 
 229:    * @return one of {@link gnu.CORBA.GIOP.v1_0.ReplyHeader},
 230:    * {@link gnu.CORBA.GIOP.v1_2.ReplyHeader}, etc - depending on the version
 231:    * number in this header.
 232:    */
 233:   public ReplyHeader create_reply_header()
 234:   {
 235:     if (version.since_inclusive(1, 2))
 236:       return new gnu.CORBA.GIOP.v1_2.ReplyHeader();
 237:     else
 238:       return new gnu.CORBA.GIOP.v1_0.ReplyHeader();
 239:   }
 240: 
 241:   /**
 242:    * Creates request header, matching the message header version number.
 243:    * 
 244:    * @return one of {@link gnu.CORBA.GIOP.v1_0.RequestHeader},
 245:    * {@link gnu.CORBA.GIOP.v1_2.RequestHeader}, etc - depending on the version
 246:    * number in this header.
 247:    */
 248:   public RequestHeader create_request_header()
 249:   {
 250:     if (version.since_inclusive(1, 2))
 251:       return new gnu.CORBA.GIOP.v1_2.RequestHeader();
 252:     else
 253:       return new gnu.CORBA.GIOP.v1_0.RequestHeader();
 254:   }
 255: 
 256:   /**
 257:    * Create the cancel header, matching the message header version number.
 258:    */
 259:   public CancelHeader create_cancel_header()
 260:   {
 261:     return new gnu.CORBA.GIOP.v1_0.CancelHeader();
 262:   }
 263: 
 264:   /**
 265:    * Create the error message.
 266:    */
 267:   public ErrorMessage create_error_message()
 268:   {
 269:     return new ErrorMessage(version);
 270:   }
 271: 
 272:   /**
 273:    * Read the header from the stream.
 274:    * 
 275:    * @param istream a stream to read from.
 276:    * 
 277:    * @throws MARSHAL if this is not a GIOP 1.0 header.
 278:    */
 279:   public void read(java.io.InputStream istream)
 280:     throws MARSHAL
 281:   {
 282:     try
 283:       {
 284:         byte[] xMagic = new byte[MAGIC.length];
 285:         istream.read(xMagic);
 286:         if (!Arrays.equals(xMagic, MAGIC))
 287:           {
 288:             MARSHAL m = new MARSHAL("Not a GIOP message");
 289:             m.minor = Minor.Giop;
 290:             throw m;
 291:           }
 292: 
 293:         version = Version.read_version(istream);
 294: 
 295:         AbstractDataInput din;
 296: 
 297:         flags = (byte) istream.read();
 298: 
 299:         // This checks the bit in the byte we have just received.
 300:         if (isBigEndian())
 301:           din = new BigEndianInputStream(istream);
 302:         else
 303:           din = new LittleEndianInputStream(istream);
 304: 
 305:         message_type = (byte) din.read();
 306: 
 307:         message_size = din.readInt();
 308:       }
 309:     catch (IOException ex)
 310:       {
 311:         MARSHAL t = new MARSHAL();
 312:         t.minor = Minor.Header;
 313:         t.initCause(ex);
 314:         throw t;
 315:       }
 316:   }
 317: 
 318:   /**
 319:    * Get the short string summary of the message.
 320:    * 
 321:    * @return a short message summary.
 322:    */
 323:   public String toString()
 324:   {
 325:     return "GIOP " + version + ", " + (isBigEndian() ? "Big" : "Little")
 326:       + " endian, " + getTypeString(message_type) + ", " + message_size
 327:       + " bytes. ";
 328:   }
 329: 
 330:   /**
 331:    * Write the header to stream.
 332:    * 
 333:    * @param out a stream to write into.
 334:    */
 335:   public void write(java.io.OutputStream out)
 336:   {
 337:     try
 338:       {
 339:         AbstractDataOutput dout;
 340: 
 341:         if (isBigEndian())
 342:           dout = new BigEndianOutputStream(out);
 343:         else
 344:           dout = new LittleEndianOutputStream(out);
 345: 
 346:         // Write magic sequence.
 347:         dout.write(MAGIC);
 348: 
 349:         // Write version number.
 350:         version.write((OutputStream) dout);
 351:         dout.write(flags);
 352:         dout.write(message_type);
 353:         dout.writeInt(message_size);
 354:       }
 355:     catch (IOException ex)
 356:       {
 357:         MARSHAL t = new MARSHAL();
 358:         t.minor = Minor.Header;
 359:         t.initCause(ex);
 360:         throw t;
 361:       }
 362:   }
 363: 
 364:   /**
 365:    * Read data, followed by the message header. Handle fragmented messages.
 366:    * 
 367:    * @param source the data source to read from.
 368:    * @param service the socket on that the time outs are set. Can be null (no
 369:    * timeouts are set).
 370:    * @param to_read the timeout while reading the message.
 371:    * @param to_pause the timeout for pauses between the message parts.
 372:    */
 373:   public byte[] readMessage(InputStream source, Socket service, int to_read,
 374:     int to_pause)
 375:   {
 376:     try
 377:       {
 378:         byte[] r = new byte[message_size];
 379: 
 380:         int n = 0;
 381:         if (service != null)
 382:           service.setSoTimeout(to_read);
 383: 
 384:         reading: while (n < r.length)
 385:           {
 386:             n += source.read(r, n, r.length - n);
 387:           }
 388:         if (service != null)
 389:           service.setSoTimeout(to_pause);
 390: 
 391:         // Read the message remainder if the message is fragmented.
 392:         if (moreFragmentsFollow())
 393:           {
 394:             ByteArrayOutputStream buffer = new ByteArrayOutputStream(
 395:               2 * r.length);
 396:             buffer.write(r);
 397: 
 398:             if (r.length < 10)
 399:               // Increase the buffer size if the default value (size of the
 400:               // previous message) is really too small.
 401:               r = new byte[1024];
 402: 
 403:             MessageHeader h2 = new MessageHeader();
 404: 
 405:             do
 406:               {
 407:                 h2.read(source);
 408: 
 409:                 int dn;
 410: 
 411:                 n = 0;
 412:                 reading: while (n < h2.message_size)
 413:                   {
 414:                     dn = source.read(r, 0, h2.message_size - n);
 415: 
 416:                     if (n == 0 && service != null)
 417:                       service.setSoTimeout(to_read);
 418: 
 419:                     if (n == 0 && version.since_inclusive(1, 2))
 420:                       {
 421:                         // Skip the four byte request id.
 422:                         buffer.write(r, 4, dn - 4);
 423:                       }
 424:                     else
 425:                       buffer.write(r, 0, dn);
 426:                     n = +dn;
 427:                   }
 428: 
 429:                 if (service != null)
 430:                   service.setSoTimeout(to_pause);
 431:               }
 432:             while (h2.moreFragmentsFollow());
 433:             return buffer.toByteArray();
 434:           }
 435:         else
 436:           return r;
 437:       }
 438:     catch (IOException ioex)
 439:       {
 440:         MARSHAL m = new MARSHAL("Unable to read the message continuation.");
 441:         m.minor = Minor.Header;
 442:         m.initCause(ioex);
 443:         throw m;
 444:       }
 445:   }