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