OSDN Git Service

libjava/ChangeLog:
[pf3gnuchains/gcc-fork.git] / libjava / classpath / gnu / CORBA / GIOP / MessageHeader.java
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 }