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 gnu.java.lang.CPStringBuilder;
  51: 
  52: import org.omg.CORBA.MARSHAL;
  53: import org.omg.CORBA.portable.IDLEntity;
  54: 
  55: import java.io.ByteArrayOutputStream;
  56: import java.io.EOFException;
  57: import java.io.IOException;
  58: import java.io.InputStream;
  59: import java.io.OutputStream;
  60: import java.net.Socket;
  61: import java.util.Arrays;
  62: 
  63: /**
  64:  * The GIOP message header.
  65:  *
  66:  * @author Audrius Meskauskas (AudriusA@Bioinformatics.org)
  67:  */
  68: public class MessageHeader
  69:   implements IDLEntity
  70: {
  71:   /**
  72:    * Use serialVersionUID for interoperability.
  73:    */
  74:   private static final long serialVersionUID = 1;
  75: 
  76:   /**
  77:    * Request message.
  78:    */
  79:   public static final byte REQUEST = 0;
  80: 
  81:   /**
  82:    * Reply message
  83:    */
  84:   public static final byte REPLY = 1;
  85: 
  86:   /**
  87:    * Cancel request message.
  88:    */
  89:   public static final byte CANCEL_REQUEST = 2;
  90: 
  91:   /**
  92:    * Locate request message, used to check the server ability to process
  93:    * requests for the object reference. This message is also used to get the
  94:    * address where the object reference should be sent.
  95:    */
  96:   public static final byte LOCATE_REQUEST = 3;
  97: 
  98:   /**
  99:    * Locate reply message, sent in response to the {@link #LocateRequest}
 100:    * message.
 101:    */
 102:   public static final byte LOCATE_REPLY = 4;
 103: 
 104:   /**
 105:    * Instruction to close the connection.
 106:    */
 107:   public static final byte CLOSE_CONNECTION = 5;
 108: 
 109:   /**
 110:    * Error report.
 111:    */
 112:   public static final byte MESSAGE_ERROR = 6;
 113: 
 114:   /**
 115:    * The fragment messge, following the previous message that has more fragments
 116:    * flag set. Added in GIOP 1.1
 117:    */
 118:   public static final byte FRAGMENT = 7;
 119: 
 120:   /**
 121:    * This must always be "GIOP".
 122:    */
 123:   public static final byte[] MAGIC = new byte[] { 'G', 'I', 'O', 'P' };
 124: 
 125:   /**
 126:    * The message type names.
 127:    */
 128:   protected static String[] types = new String[] { "Request", "Reply",
 129:     "Cancel", "Locate request", "Locate reply", "Close connection", "Error",
 130:     "Fragment" };
 131: 
 132:   /**
 133:    * The GIOP version. Initialised to 1.0 .
 134:    */
 135:   public Version version;
 136: 
 137:   /**
 138:    * The flags field, introduced since GIOP 1.1.
 139:    */
 140:   public byte flags = 0;
 141: 
 142:   /**
 143:    * The message type.
 144:    */
 145:   public byte message_type = REQUEST;
 146: 
 147:   /**
 148:    * The message size, excluding the message header.
 149:    */
 150:   public int message_size = 0;
 151: 
 152:   /**
 153:    * Create an empty message header, corresponding version 1.0.
 154:    */
 155:   public MessageHeader()
 156:   {
 157:     version = new Version(1, 0);
 158:   }
 159: 
 160:   /**
 161:    * Create an empty message header, corresponding the given version.
 162:    *
 163:    * @param major the major message header version.
 164:    * @param minor the minot message header version.
 165:    */
 166:   public MessageHeader(int major, int minor)
 167:   {
 168:     version = new Version(major, minor);
 169:   }
 170: 
 171:   /**
 172:    * Checks if the message is encoded in the Big Endian, most significant byte
 173:    * first.
 174:    */
 175:   public boolean isBigEndian()
 176:   {
 177:     return (flags & 0x1) == 0;
 178:   }
 179: 
 180:   /**
 181:    * Checks if the message is partial, and more subsequent fragments follow.
 182:    */
 183:   public boolean moreFragmentsFollow()
 184:   {
 185:     return (flags & 0x2) != 0;
 186:   }
 187: 
 188:   /**
 189:    * Set the encoding to use.
 190:    *
 191:    * @param use_big_endian if true (default), the Big Endian encoding is used.
 192:    * If false, the Little Endian encoding is used.
 193:    */
 194:   public void setBigEndian(boolean use_big_endian)
 195:   {
 196:     if (use_big_endian)
 197:       flags = (byte) (flags & ~1);
 198:     else
 199:       flags = (byte) (flags | 1);
 200:   }
 201: 
 202:   /**
 203:    * Get the size of the message header itself. So far, it is always 12 bytes.
 204:    */
 205:   public int getHeaderSize()
 206:   {
 207:     return 12;
 208:   }
 209: 
 210:   /**
 211:    * Get the message type as string.
 212:    *
 213:    * @param type the message type as int (the field {@link message_type}).
 214:    *
 215:    * @return the message type as string.
 216:    */
 217:   public String getTypeString(int type)
 218:   {
 219:     try
 220:       {
 221:         return types[type];
 222:       }
 223:     catch (ArrayIndexOutOfBoundsException ex)
 224:       {
 225:         return "unknown type (" + type + ")";
 226:       }
 227:   }
 228: 
 229:   /**
 230:    * Creates reply header, matching the message header version number.
 231:    *
 232:    * @return one of {@link gnu.CORBA.GIOP.v1_0.ReplyHeader},
 233:    * {@link gnu.CORBA.GIOP.v1_2.ReplyHeader}, etc - depending on the version
 234:    * number in this header.
 235:    */
 236:   public ReplyHeader create_reply_header()
 237:   {
 238:     if (version.since_inclusive(1, 2))
 239:       return new gnu.CORBA.GIOP.v1_2.ReplyHeader();
 240:     else
 241:       return new gnu.CORBA.GIOP.v1_0.ReplyHeader();
 242:   }
 243: 
 244:   /**
 245:    * Creates request header, matching the message header version number.
 246:    *
 247:    * @return one of {@link gnu.CORBA.GIOP.v1_0.RequestHeader},
 248:    * {@link gnu.CORBA.GIOP.v1_2.RequestHeader}, etc - depending on the version
 249:    * number in this header.
 250:    */
 251:   public RequestHeader create_request_header()
 252:   {
 253:     if (version.since_inclusive(1, 2))
 254:       return new gnu.CORBA.GIOP.v1_2.RequestHeader();
 255:     else
 256:       return new gnu.CORBA.GIOP.v1_0.RequestHeader();
 257:   }
 258: 
 259:   /**
 260:    * Create the cancel header, matching the message header version number.
 261:    */
 262:   public CancelHeader create_cancel_header()
 263:   {
 264:     return new gnu.CORBA.GIOP.v1_0.CancelHeader();
 265:   }
 266: 
 267:   /**
 268:    * Create the error message.
 269:    */
 270:   public ErrorMessage create_error_message()
 271:   {
 272:     return new ErrorMessage(version);
 273:   }
 274: 
 275:   /**
 276:    * Read the header from the stream.
 277:    *
 278:    * @param istream a stream to read from.
 279:    * @throws MARSHAL if this is not a GIOP 1.0 header.
 280:    */
 281:   public void read(java.io.InputStream istream)
 282:     throws MARSHAL, EOFException
 283:   {
 284:     try
 285:       {
 286:         byte[] xMagic = new byte[MAGIC.length];
 287:         int r = istream.read(xMagic);
 288:         int minor;
 289:         if (! Arrays.equals(xMagic, MAGIC))
 290:           {
 291:             CPStringBuilder b = new CPStringBuilder();
 292:             if (r == - 1)
 293:               {
 294:                 b.append("Immediate EOF");
 295:                 minor = Minor.EOF;
 296:               }
 297:             else
 298:               {
 299:                 minor = Minor.Giop;
 300:                 b.append(r + " bytes: ");
 301:                 for (int i = 0; i < xMagic.length; i++)
 302:                   {
 303:                     b.append(Integer.toHexString(xMagic[i] & 0xFF));
 304:                     b.append(' ');
 305:                   }
 306:               }
 307:             MARSHAL m = new MARSHAL("Not a GIOP message: " + b);
 308:             m.minor = minor;
 309:             throw m;
 310:           }
 311: 
 312:         version = Version.read_version(istream);
 313: 
 314:         AbstractDataInput din;
 315: 
 316:         flags = (byte) istream.read();
 317: 
 318:         // This checks the bit in the byte we have just received.
 319:         if (isBigEndian())
 320:           din = new BigEndianInputStream(istream);
 321:         else
 322:           din = new LittleEndianInputStream(istream);
 323: 
 324:         message_type = (byte) din.read();
 325: 
 326:         message_size = din.readInt();
 327:       }
 328:     catch (IOException ex)
 329:       {
 330:         MARSHAL t = new MARSHAL();
 331:         t.minor = Minor.Header;
 332:         t.initCause(ex);
 333:         throw t;
 334:       }
 335:   }
 336: 
 337:   /**
 338:    * Get the short string summary of the message.
 339:    *
 340:    * @return a short message summary.
 341:    */
 342:   public String toString()
 343:   {
 344:     return "GIOP " + version + ", " + (isBigEndian() ? "Big" : "Little")
 345:       + " endian, " + getTypeString(message_type) + ", " + message_size
 346:       + " bytes. ";
 347:   }
 348: 
 349:   /**
 350:    * Write the header to stream.
 351:    *
 352:    * @param out a stream to write into.
 353:    */
 354:   public void write(java.io.OutputStream out)
 355:   {
 356:     try
 357:       {
 358:         AbstractDataOutput dout;
 359: 
 360:         if (isBigEndian())
 361:           dout = new BigEndianOutputStream(out);
 362:         else
 363:           dout = new LittleEndianOutputStream(out);
 364: 
 365:         // Write magic sequence.
 366:         dout.write(MAGIC);
 367: 
 368:         // Write version number.
 369:         version.write((OutputStream) dout);
 370:         dout.write(flags);
 371:         dout.write(message_type);
 372:         dout.writeInt(message_size);
 373:       }
 374:     catch (IOException ex)
 375:       {
 376:         MARSHAL t = new MARSHAL(ex.getMessage());
 377:         t.minor = Minor.Header;
 378:         t.initCause(ex);
 379:         throw t;
 380:       }
 381:   }
 382: 
 383:   /**
 384:    * Read data, followed by the message header. Handle fragmented messages.
 385:    *
 386:    * @param source the data source to read from.
 387:    * @param service the socket on that the time outs are set. Can be null (no
 388:    * timeouts are set).
 389:    * @param to_read the timeout while reading the message.
 390:    * @param to_pause the timeout for pauses between the message parts.
 391:    */
 392:   public byte[] readMessage(InputStream source, Socket service, int to_read,
 393:     int to_pause)
 394:   {
 395:     try
 396:       {
 397:         byte[] r = new byte[message_size];
 398: 
 399:         int n = 0;
 400:         if (service != null)
 401:           service.setSoTimeout(to_read);
 402: 
 403:         while (n < r.length)
 404:           {
 405:             n += source.read(r, n, r.length - n);
 406:           }
 407:         if (service != null)
 408:           service.setSoTimeout(to_pause);
 409: 
 410:         // Read the message remainder if the message is fragmented.
 411:         if (moreFragmentsFollow())
 412:           {
 413:             ByteArrayOutputStream buffer = new ByteArrayOutputStream(
 414:               2 * r.length);
 415:             buffer.write(r);
 416: 
 417:             if (r.length < 10)
 418:               // Increase the buffer size if the default value (size of the
 419:               // previous message) is really too small.
 420:               r = new byte[1024];
 421: 
 422:             MessageHeader h2 = new MessageHeader();
 423: 
 424:             do
 425:               {
 426:                 h2.read(source);
 427: 
 428:                 int dn;
 429: 
 430:                 n = 0;
 431:                 while (n < h2.message_size)
 432:                   {
 433:                     dn = source.read(r, 0, h2.message_size - n);
 434: 
 435:                     if (n == 0 && service != null)
 436:                       service.setSoTimeout(to_read);
 437: 
 438:                     if (n == 0 && version.since_inclusive(1, 2))
 439:                       {
 440:                         // Skip the four byte request id.
 441:                         buffer.write(r, 4, dn - 4);
 442:                       }
 443:                     else
 444:                       buffer.write(r, 0, dn);
 445:                     n = +dn;
 446:                   }
 447: 
 448:                 if (service != null)
 449:                   service.setSoTimeout(to_pause);
 450:               }
 451:             while (h2.moreFragmentsFollow());
 452:             return buffer.toByteArray();
 453:           }
 454:         else
 455:           return r;
 456:       }
 457:     catch (IOException ioex)
 458:       {
 459:         MARSHAL m = new MARSHAL("Unable to read the message continuation.");
 460:         m.minor = Minor.Header;
 461:         m.initCause(ioex);
 462:         throw m;
 463:       }
 464:   }
 465: }