1 /* Vio.java -- Value type IO operations. 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.CDR; 40 41 import gnu.CORBA.Minor; 42 import gnu.CORBA.ObjectCreator; 43 44 import org.omg.CORBA.CustomMarshal; 45 import org.omg.CORBA.DataInputStream; 46 import org.omg.CORBA.DataOutputStream; 47 import org.omg.CORBA.MARSHAL; 48 import org.omg.CORBA.NO_IMPLEMENT; 49 import org.omg.CORBA.StringSeqHelper; 50 import org.omg.CORBA.StringValueHelper; 51 import org.omg.CORBA.SystemException; 52 import org.omg.CORBA.WStringValueHelper; 53 import org.omg.CORBA.portable.BoxedValueHelper; 54 import org.omg.CORBA.portable.InputStream; 55 import org.omg.CORBA.portable.OutputStream; 56 import org.omg.CORBA.portable.Streamable; 57 import org.omg.CORBA.portable.ValueFactory; 58 59 import java.io.IOException; 60 import java.io.Serializable; 61 import java.lang.reflect.Constructor; 62 import java.lang.reflect.Modifier; 63 import java.util.StringTokenizer; 64 65 import javax.rmi.CORBA.Util; 66 import javax.rmi.CORBA.ValueHandler; 67 68 /** 69 * A specialised class for reading and writing the value types. 70 * 71 * @author Audrius Meskauskas (AudriusA@Bioinformatics.org) 72 */ 73 public abstract class Vio 74 { 75 /** 76 * If true, wrap value type data into chunks. This decrease the performance, 77 * and is not required for interoperability with jdk 1.5, but is left in the 78 * implementation as the optional mode for solving possible interoperability 79 * problems with non-Sun CORBA implementations. 80 * 81 * The current implementation would accept both single chunk or multiple 82 * chunks, but will always send a single chunk (if true) or unchunked data (if 83 * false). 84 */ 85 public static boolean USE_CHUNKING = false; 86 87 /** 88 * The first field in the value record. The last octet may contain additional 89 * flags (vf_CODEBASE, vf_ID and vf_MULTIPLE_IDS). The tag value is different 90 * for the indirections (vt_INDIRECTION) and nulls (vt_NULL). 91 */ 92 public static final int vt_VALUE_TAG = 0x7fffff00; 93 94 /** 95 * The value tag flag, indicating that the codebase URL is present in the 96 * value tag record. 97 */ 98 public static final int vf_CODEBASE = 0x1; 99 100 /** 101 * The value tag flag, indicating that a single repository id is present in 102 * the value tag record. 103 */ 104 public static final int vf_ID = 0x2; 105 106 /** 107 * The value tag flag, indicating, that there are multiple repository ids 108 * present in the record. If this flag is set, the flag vf_ID must also be 109 * set, resulting the value of the least significant byte 0x6. 110 */ 111 public static final int vf_MULTIPLE_IDS = 0x4; 112 113 /** 114 * The value tag flag, indicating the presence of chunking. Each chunk is 115 * preceeded by a positive int, indicating the number of bytes in the chunk. A 116 * sequence of chunks is terminated by a non positive int. 117 */ 118 public static final int vf_CHUNKING = 0x8; 119 120 /** 121 * The indirection tag value. Such tag must be followed by the CORBA long, 122 * indicating the offset in the CORBA message, where the indirected 123 * information is present. This offset is assumed zero at the position where 124 * the mentioned CORBA long starts and can refer both forward (positive 125 * values) and backward (negative values). 126 */ 127 public static final int vt_INDIRECTION = 0xffffffff; 128 129 /** 130 * This tag value means that the value object being transferred is equal to 131 * null. 132 */ 133 public static final int vt_NULL = 0x0; 134 135 /** 136 * The size of CORBA long (java int). 137 */ 138 static final int INT_SIZE = 4; 139 140 /** 141 * The String value helper (one instance is sufficient). 142 */ 143 public static final WStringValueHelper m_StringValueHelper = new WStringValueHelper(); 144 145 /** 146 * An instance of the value handler. 147 */ 148 static ValueHandler handler = Util.createValueHandler(); 149 150 /** 151 * Read the value base from the given input stream. Determines the required 152 * class from the repository id. This includes operations that are not 153 * required when an unitialised instance or at least class of the value type 154 * is known. Hence it may be faster to use the alternative methods, 155 * read(InputStream, Class) or read(InputStream, Serializable). 156 * 157 * @param input a stream to read from. 158 * @param repository_id a repository id of the object being read, may be null. 159 * 160 * @return the loaded value. 161 * 162 * @throws MARSHAL if the reading has failed due any reason. 163 */ read(InputStream input)164 public static Serializable read(InputStream input) 165 { 166 return read(input, (String) null); 167 } 168 169 /** 170 * Read the value base from the given input stream. Determines the required 171 * class from the repository id. This includes operations that are not 172 * required when an unitialised instance or at least class of the value type 173 * is known. Hence it may be faster to use the alternative methods, 174 * read(InputStream, Class) or read(InputStream, Serializable). 175 * 176 * @param an_input a stream to read from. 177 * @param repository_id a repository id of the object being read, may be null. 178 * 179 * @return the loaded value. 180 * 181 * @throws MARSHAL if the reading has failed due any reason. 182 */ read(InputStream input, String repository_id)183 public static Serializable read(InputStream input, String repository_id) 184 { 185 try 186 { 187 final int position = getCurrentPosition(input); 188 // We may need to jump back if the value is read via value factory. 189 input.mark(512); 190 191 int value_tag = input.read_long(); 192 checkTag(value_tag); 193 194 String codebase = null; 195 String[] ids = null; 196 String id = repository_id; 197 198 // Check for the agreed null value. 199 if (value_tag == vt_NULL) 200 return null; 201 else if (value_tag == vt_INDIRECTION) 202 return readIndirection(input); 203 else 204 { 205 // Read the value. 206 if ((value_tag & vf_CODEBASE) != 0) 207 { 208 // The codebase is present. The codebase is a space 209 // separated list of URLs from where the implementing 210 // code can be downloaded. 211 codebase = read_string(input); 212 } 213 214 if ((value_tag & vf_MULTIPLE_IDS) != 0) 215 { 216 // Multiple supported repository ids are present. 217 ids = read_string_array(input); 218 } 219 else if ((value_tag & vf_ID) != 0) 220 { 221 // Single supported repository id is present. 222 id = read_string(input); 223 } 224 } 225 226 BoxedValueHelper helper = getHelper(null, id); 227 // The existing implementing object. 228 java.lang.Object ox = null; 229 230 if (helper != null) 231 ox = null; // Helper will care about the instantiating. 232 else if (id.equals(WStringValueHelper.id())) 233 helper = m_StringValueHelper; 234 else 235 ox = createInstance(id, ids, codebase); 236 return (Serializable) read_instance(input, position, ox, value_tag, 237 helper, id, ids, codebase); 238 } 239 catch (Exception ex) 240 { 241 MARSHAL m = new MARSHAL(); 242 m.minor = Minor.Value; 243 m.initCause(ex); 244 throw m; 245 } 246 } 247 248 /** 249 * Read the value base from the given input stream when the value base class 250 * is available. Hence there is no need to guess it from the repository id. 251 * 252 * @param input a stream to read from. 253 * @param value_class the class of the value being read. 254 * 255 * @return the loaded value. 256 * 257 * @throws MARSHAL if the reading has failed due any reason. 258 */ read(InputStream input, Class value_class)259 public static Serializable read(InputStream input, Class value_class) 260 { 261 final int position = getCurrentPosition(input); 262 263 String id = null; 264 String[] ids = null; 265 String codebase = null; 266 267 try 268 { 269 int value_tag = input.read_long(); 270 checkTag(value_tag); 271 272 // Check for the agreed null value. 273 if (value_tag == vt_NULL) 274 return null; 275 else if (value_tag == vt_INDIRECTION) 276 return readIndirection(input); 277 else 278 { 279 // Read the value. 280 if ((value_tag & vf_CODEBASE) != 0) 281 { 282 // The codebase is present. 283 codebase = read_string(input); 284 } 285 286 if ((value_tag & vf_MULTIPLE_IDS) != 0) 287 { 288 // Multiple supported repository ids are present. 289 ids = read_string_array(input); 290 } 291 else if ((value_tag & vf_ID) != 0) 292 { 293 // Single supported repository id is present. 294 id = read_string(input); 295 } 296 } 297 298 BoxedValueHelper vHelper = id != null ? getHelper(value_class, id) 299 : getHelper(value_class, ids); 300 301 java.lang.Object ox; 302 303 if (vHelper == null) 304 { 305 try 306 { 307 ox = createInstance(id, ids, codebase); 308 } 309 catch (Exception e) 310 { 311 ox = null; 312 } 313 314 if (ox != null) 315 { 316 if (value_class != null 317 && !value_class.isAssignableFrom(ox.getClass())) 318 { 319 MARSHAL m = new MARSHAL(ox.getClass() + " is not a " 320 + value_class.getName()); 321 m.minor = Minor.ClassCast; 322 throw m; 323 } 324 } 325 } 326 else 327 ox = null; 328 329 ox = read_instance(input, position, ox, value_tag, vHelper, id, ids, 330 codebase); 331 return (Serializable) ox; 332 } 333 catch (MARSHAL m) 334 { 335 throw m; 336 } 337 catch (SystemException sysEx) 338 { 339 // OK. 340 throw sysEx; 341 } 342 catch (Exception ex) 343 { 344 MARSHAL m = new MARSHAL("Cant read " + value_class); 345 m.minor = Minor.Value; 346 m.initCause(ex); 347 throw m; 348 } 349 } 350 351 /** 352 * Read the value base from the given input stream when the unitialised 353 * instance is available. Hence there is no need to guess the class from the 354 * repository id and then to instantiate an instance. 355 * 356 * @param input a stream to read from. 357 * 358 * @param value_instance an pre-created instance of the value. If the helper 359 * is not null, this parameter is ignored an should be null. 360 * 361 * @param helper a helper to create an instance and read the object- specific 362 * part of the record. If the value_instance is used instead, this parameter 363 * should be null. 364 * 365 * @return the loaded value. 366 * 367 * @throws MARSHAL if the reading has failed due any reason. 368 */ read(InputStream input, Object value_instance, BoxedValueHelper helper)369 public static Object read(InputStream input, Object value_instance, 370 BoxedValueHelper helper) 371 { 372 final int position = getCurrentPosition(input); 373 374 String id = null; 375 String[] ids = null; 376 String codebase = null; 377 378 try 379 { 380 int value_tag = input.read_long(); 381 checkTag(value_tag); 382 383 // Check for the agreed null value. 384 if (value_tag == vt_NULL) 385 return null; 386 else if (value_tag == vt_INDIRECTION) 387 return readIndirection(input); 388 else 389 { 390 // Read the value. 391 if ((value_tag & vf_CODEBASE) != 0) 392 { 393 // The codebase is present. 394 codebase = read_string(input); 395 } 396 397 if ((value_tag & vf_MULTIPLE_IDS) != 0) 398 { 399 // Multiple supported repository ids are present. 400 ids = read_string_array(input); 401 } 402 else if ((value_tag & vf_ID) != 0) 403 { 404 // Single supported repository id is present. 405 id = read_string(input); 406 } 407 } 408 409 Class value_class = value_instance == null ? null 410 : value_instance.getClass(); 411 412 if (helper == null) 413 helper = id != null ? getHelper(value_class, id) : getHelper( 414 value_class, ids); 415 416 value_instance = read_instance(input, position, value_instance, 417 value_tag, helper, id, ids, codebase); 418 return value_instance; 419 } 420 catch (Exception ex) 421 { 422 MARSHAL m = new MARSHAL(); 423 m.minor = Minor.Value; 424 m.initCause(ex); 425 throw m; 426 } 427 } 428 429 /** 430 * Read using provided boxed value helper. This method expects the full value 431 * type header, followed by contents, that are delegated to the provided 432 * helper. It handles null. 433 * 434 * @param input the stream to read from. 435 * @param helper the helper that reads the type-specific part of the content. 436 * 437 * @return the value, created by the helper, or null if the header indicates 438 * that null was previously written. 439 */ read(InputStream input, BoxedValueHelper helper)440 public static Serializable read(InputStream input, BoxedValueHelper helper) 441 { 442 return (Serializable) read(input, null, helper); 443 } 444 445 /** 446 * Fill in the instance fields by the data from the input stream. The method 447 * assumes that the value header, if any, is already behind. The information 448 * from the stream is stored into the passed ox parameter. 449 * 450 * @param input an input stream to read from. 451 * 452 * @param value a pre-instantiated value type object, must be either 453 * Streamable or CustomMarshal. If the helper is used, this parameter is 454 * ignored and should be null. 455 * 456 * @param value_tag the tag that must be read previously. 457 * @param helper the helper for read object specific part; may be null to read 458 * in using other methods. 459 * 460 * @return the value that was read. 461 */ read_instance(InputStream input, final int position, Object value, int value_tag, BoxedValueHelper helper, String id, String[] ids, String codebase)462 static Object read_instance(InputStream input, final int position, 463 Object value, int value_tag, BoxedValueHelper helper, String id, 464 String[] ids, String codebase) 465 { 466 if (helper != m_StringValueHelper && id != null) 467 if (id.equals(StringValueHelper.id())) 468 { 469 value = null; 470 helper = m_StringValueHelper; 471 } 472 473 try 474 { 475 if ((value_tag & vf_CHUNKING) != 0) 476 { 477 BufferedCdrOutput output = createBuffer(input, 1024); 478 // Read the current (not a nested one) value in this spec case. 479 readNestedValue(value_tag, input, output, -1); 480 BufferredCdrInput ci = new BufferredCdrInput(output.buffer.getBuffer()); 481 ci.setRunTime(output.getRunTime()); 482 483 input = new HeadlessInput(ci, input); 484 } 485 else 486 { 487 if (input instanceof BufferredCdrInput) 488 { 489 // Highly probable case. 490 input = new HeadlessInput((BufferredCdrInput) input, null); 491 } 492 else if (input instanceof HeadlessInput) 493 { 494 // There is no need to instantiate one more HeadlessInput 495 // as we can just reset. 496 ((HeadlessInput) input).subsequentCalls = false; 497 } 498 else 499 { 500 BufferedCdrOutput bout = new BufferedCdrOutput(); 501 int c; 502 while ((c = input.read()) >= 0) 503 bout.write((byte) c); 504 input = new HeadlessInput( 505 (BufferredCdrInput) bout.create_input_stream(), input); 506 } 507 } 508 } 509 catch (IOException ex) 510 { 511 MARSHAL m = new MARSHAL("Unable to read chunks"); 512 m.minor = Minor.Value; 513 m.initCause(ex); 514 throw m; 515 } 516 517 return readValue(input, position, value, helper, id, ids, codebase); 518 } 519 520 /** 521 * Create a buffer, inheriting critical settings from the passed input stream. 522 */ createBuffer(InputStream input, int proposed_size)523 private static BufferedCdrOutput createBuffer(InputStream input, int proposed_size) 524 { 525 BufferedCdrOutput bout; 526 bout = new BufferedCdrOutput(2 * proposed_size + 256); 527 528 if (input instanceof BufferredCdrInput) 529 { 530 BufferredCdrInput in = (BufferredCdrInput) input; 531 bout.setBigEndian(in.isBigEndian()); 532 } 533 534 if (input instanceof gnuValueStream) 535 bout.setRunTime(((gnuValueStream) input).getRunTime()); 536 else 537 bout.setRunTime(new gnuRuntime(null, null)); 538 return bout; 539 } 540 541 /** 542 * Read the chunked nested value from the given input stream, transferring the 543 * contents to the given output stream. 544 * 545 * @param value_tag the value tag of the value being read. 546 * @param input the input stream from where the remainder of the nested value 547 * must be read. 548 * @param output the output stream where the unchunked nested value must be 549 * copied. 550 * 551 * @return the tag that ended the nested value. 552 */ readNestedValue(int value_tag, InputStream input, BufferedCdrOutput output, int level)553 public static int readNestedValue(int value_tag, InputStream input, 554 BufferedCdrOutput output, int level) 555 throws IOException 556 { 557 String id = null; 558 if (level < -1) 559 { 560 // For the first level, this information is already behind. 561 output.write_long(value_tag - vf_CHUNKING); 562 563 // The nested value should be aways chunked. 564 if ((value_tag & vf_CHUNKING) == 0) 565 { 566 MARSHAL m = new MARSHAL("readNestedValue: must be chunked"); 567 m.minor = Minor.Chunks; 568 throw m; 569 } 570 else if (value_tag == vt_NULL) 571 { 572 MARSHAL m = new MARSHAL("readNestedValue: nul"); 573 m.minor = Minor.Chunks; 574 throw m; 575 } 576 else if (value_tag == vt_INDIRECTION) 577 { 578 MARSHAL m = new MARSHAL("readNestedValue: indirection"); 579 m.minor = Minor.Chunks; 580 throw m; 581 } 582 else 583 { 584 // Read the value. 585 if ((value_tag & vf_CODEBASE) != 0) 586 { 587 String codebase = read_string(input); 588 write_string(output, codebase); 589 } 590 591 if ((value_tag & vf_MULTIPLE_IDS) != 0) 592 { 593 // Multiple supported repository ids are present. 594 String[] ids = read_string_array(input); 595 id = ids[0]; 596 write_string_array(output, ids); 597 } 598 else if ((value_tag & vf_ID) != 0) 599 { 600 id = read_string(input); 601 write_string(output, id); 602 } 603 } 604 } 605 606 int n = -1; 607 608 // Read all chunks. 609 int chunk_size; 610 611 byte[] r = null; 612 613 while (true) 614 { 615 // Read the size of the next chunk or it may also be the 616 // header of the nested value. 617 chunk_size = input.read_long(); 618 619 // End of chunk terminator. 620 if (chunk_size < 0 && chunk_size >= level) 621 return chunk_size; 622 else if (chunk_size >= 0x7FFFFF00) 623 { 624 int onInput = getCurrentPosition(input) - 4; 625 int onOutput = output.getPosition(); 626 output.getRunTime().redirect(onInput, onOutput); 627 // Value over 0x7FFFFF00 indicates that the nested value 628 // starts here. Read the nested value, storing it into the output. 629 // First parameter is actually the value tag. 630 chunk_size = readNestedValue(chunk_size, input, output, level - 1); 631 if (chunk_size < 0 && chunk_size >= level) 632 return chunk_size; 633 } 634 else 635 { 636 // The chunk follows. 637 if (r == null || r.length < chunk_size) 638 r = new byte[chunk_size + 256]; 639 640 n = 0; 641 reading: while (n < chunk_size) 642 n += input.read(r, n, chunk_size - n); 643 output.write(r, 0, n); 644 } 645 } 646 } 647 648 /** 649 * Read the value (the header must be behind). 650 */ readValue(InputStream input, final int position, Object value, BoxedValueHelper helper, String id, String[] ids, String codebase)651 public static Serializable readValue(InputStream input, final int position, 652 Object value, BoxedValueHelper helper, String id, String[] ids, 653 String codebase) 654 { 655 gnuRuntime g; 656 gnuValueStream c = ((gnuValueStream) input); 657 if (c.getRunTime() == null) 658 { 659 g = new gnuRuntime(codebase, value); 660 c.setRunTime(g); 661 } 662 else 663 { 664 g = c.getRunTime(); 665 g.addCodeBase(codebase); 666 g.target = (Serializable) value; 667 } 668 if (value != null) 669 g.objectWritten(value, position); 670 671 if (input instanceof HeadlessInput) 672 ((HeadlessInput) input).subsequentCalls = false; 673 674 boolean ok = true; 675 676 // The user-defined io operations are implemented. 677 if (value instanceof CustomMarshal) 678 { 679 CustomMarshal marsh = (CustomMarshal) value; 680 marsh.unmarshal((DataInputStream) input); 681 } 682 else 683 // The IDL-generated io operations are implemented. 684 if (value instanceof Streamable) 685 { 686 ((Streamable) value)._read(input); 687 } 688 else if (helper != null) 689 { 690 // If helper is non-null the value should normally be null. 691 value = helper.read_value(input); 692 g.objectWritten(value, position); 693 } 694 else 695 { 696 ok = false; 697 ValueFactory factory = null; 698 org.omg.CORBA_2_3.ORB orb = (org.omg.CORBA_2_3.ORB) input.orb(); 699 700 if (id != null) 701 factory = orb.lookup_value_factory(id); 702 703 if (factory == null && ids != null) 704 { 705 for (int i = 0; i < ids.length && factory == null; i++) 706 { 707 factory = orb.lookup_value_factory(ids[i]); 708 } 709 } 710 711 if (factory != null) 712 { 713 value = factory.read_value((org.omg.CORBA_2_3.portable.InputStream) input); 714 ok = true; 715 } 716 } 717 718 if (!ok && value instanceof Serializable) 719 // Delegate to ValueHandler 720 { 721 if (ids != null && ids.length > 0) 722 id = ids[0]; 723 724 value = handler.readValue(input, position, value.getClass(), id, g); 725 ok = true; 726 } 727 728 if (!ok) 729 { 730 if (value != null) 731 { 732 MARSHAL m = new MARSHAL(value.getClass().getName() 733 + " must be Streamable, CustomMarshal or Serializable"); 734 m.minor = Minor.UnsupportedValue; 735 throw m; 736 } 737 else 738 { 739 MARSHAL m = new MARSHAL("Unable to instantiate " + id + ":" + list(ids) 740 + " helper " + helper); 741 m.minor = Minor.UnsupportedValue; 742 throw m; 743 } 744 } 745 else 746 return (Serializable) value; 747 } 748 749 /** 750 * Conveniency method to list ids in exception reports. 751 */ list(String[] s)752 static String list(String[] s) 753 { 754 if (s == null) 755 return "null"; 756 else 757 { 758 StringBuffer b = new StringBuffer("{"); 759 for (int i = 0; i < s.length; i++) 760 { 761 b.append(s[i]); 762 b.append(" "); 763 } 764 b.append("}"); 765 return b.toString(); 766 } 767 } 768 769 /** 770 * Write the value base into the given stream. 771 * 772 * @param output a stream to write to. 773 * 774 * @param value a value type object, must be either Streamable or 775 * CustomMarshal. 776 * 777 * @throws MARSHAL if the writing failed due any reason. 778 */ write(OutputStream output, Serializable value)779 public static void write(OutputStream output, Serializable value) 780 { 781 // Write null if this is a null value. 782 if (value == null) 783 output.write_long(vt_NULL); 784 else if (value instanceof String) 785 write(output, value, m_StringValueHelper); 786 else 787 write(output, value, value.getClass()); 788 } 789 790 /** 791 * Write the value base into the given stream, stating that it is an instance 792 * of the given class. 793 * 794 * @param output a stream to write to. 795 * 796 * @param value a value to write. 797 * 798 * @throws MARSHAL if the writing failed due any reason. 799 */ write(OutputStream output, Serializable value, Class substitute)800 public static void write(OutputStream output, Serializable value, 801 Class substitute) 802 { 803 // Write null if this is a null value. 804 if (value == null) 805 output.write_long(vt_NULL); 806 else if (value instanceof String || substitute == String.class) 807 writeString(output, value); 808 else 809 { 810 String vId = ObjectCreator.getRepositoryId(value.getClass()); 811 if (substitute == null || value.getClass().equals(substitute)) 812 write_instance(output, value, vId, getHelper(value.getClass(), vId)); 813 else 814 { 815 String vC = ObjectCreator.getRepositoryId(substitute); 816 String[] ids = new String[] { vId, vC }; 817 BoxedValueHelper h = getHelper(substitute.getClass(), ids); 818 // If the helper is available, it is also responsible for 819 // providing the repository Id. Otherwise, write both 820 // ids. 821 if (h == null) 822 write_instance(output, value, ids, null); 823 else 824 write_instance(output, value, h.get_id(), null); 825 } 826 } 827 } 828 829 /** 830 * Write the value base into the given stream, supplementing it with an array 831 * of the provided repository ids plus the repository id, derived from the 832 * passed value. 833 * 834 * @param output a stream to write to. 835 * 836 * @param value a value to write. 837 * 838 * @throws MARSHAL if the writing failed due any reason. 839 */ write(OutputStream output, Serializable value, String[] multiple_ids)840 public static void write(OutputStream output, Serializable value, 841 String[] multiple_ids) 842 { 843 // Write null if this is a null value. 844 if (value == null) 845 output.write_long(vt_NULL); 846 else 847 { 848 String[] ids = new String[multiple_ids.length + 1]; 849 ids[0] = ObjectCreator.getRepositoryId(value.getClass()); 850 System.arraycopy(multiple_ids, 0, ids, 1, multiple_ids.length); 851 BoxedValueHelper h = getHelper(value.getClass(), ids); 852 write_instance(output, value, ids, h); 853 } 854 } 855 856 /** 857 * Write value when its repository Id is explicitly given. Only this Id is 858 * written, the type of value is not taken into consideration. 859 * 860 * @param output an output stream to write into. 861 * @param value a value to write. 862 * @param id a value repository id. 863 */ write(OutputStream output, Serializable value, String id)864 public static void write(OutputStream output, Serializable value, String id) 865 { 866 if (value == null) 867 output.write_long(vt_NULL); 868 else 869 write_instance(output, value, id, getHelper(value.getClass(), id)); 870 } 871 872 /** 873 * Write standard value type header, followed by contents, produced by the 874 * boxed value helper. 875 * 876 * @param output the stream to write to. 877 * @param value the value to write, can be null. 878 * @param helper the helper that writes the value content if it is not null 879 * (must be provided for this method). 880 */ write(OutputStream output, Serializable value, BoxedValueHelper helper)881 public static void write(OutputStream output, Serializable value, 882 BoxedValueHelper helper) 883 { 884 if (helper == null) 885 throw new AssertionError("Helper must be provided"); 886 if (value == null) 887 output.write_long(vt_NULL); 888 else 889 write_instance(output, value, helper.get_id(), helper); 890 } 891 892 /** 893 * Write the parameter that is surely a string and not null. 894 */ writeString(OutputStream output, Serializable string)895 private static void writeString(OutputStream output, Serializable string) 896 { 897 write_instance(output, string, m_StringValueHelper.get_id(), 898 m_StringValueHelper); 899 } 900 901 /** 902 * Write value when its repository Id is explicitly given. Does not handle 903 * null. 904 * 905 * @param output an output stream to write into. 906 * @param value a value to write. 907 * @param id a value repository id (can be either single string or string 908 * array). 909 * @param helper a helper, writing object - specifical part. Can be null if 910 * the value should be written using other methods. 911 */ write_instance(OutputStream output, Serializable value, Object ids, BoxedValueHelper helper)912 static void write_instance(OutputStream output, Serializable value, 913 Object ids, BoxedValueHelper helper) 914 { 915 gnuValueStream rout = null; 916 gnuRuntime runtime = null; 917 918 try 919 { 920 if (output instanceof gnuValueStream) 921 { 922 int position; 923 rout = (gnuValueStream) output; 924 runtime = rout.getRunTime(); 925 926 if (runtime == null) 927 { 928 runtime = new gnuRuntime(null, value); 929 rout.setRunTime(runtime); 930 rout.getRunTime().objectWritten(value, 931 position = rout.getPosition()); 932 } 933 else if (runtime.target == value) 934 { 935 if (!writeSelf(output, value)) 936 throw new InternalError("Recursive helper call for " 937 + value.getClass().getName()); 938 return; 939 } 940 else 941 { 942 position = runtime.isWrittenAt(value); 943 if (position >= 0) 944 { 945 // The object was already written. 946 output.write_long(vt_INDIRECTION); 947 output.write_long(position - rout.getPosition()); 948 // Replacing object write data by indirection reference. 949 return; 950 } 951 else 952 { 953 runtime.objectWritten(value, position = rout.getPosition()); 954 } 955 } 956 } 957 958 int value_tag = vt_VALUE_TAG; 959 960 if (ids instanceof String) 961 value_tag |= vf_ID; 962 else if (ids instanceof String[]) 963 // OMG standard requires to set both flags. 964 value_tag |= vf_MULTIPLE_IDS | vf_ID; 965 966 int chunkSizeLocation; 967 968 OutputStream outObj; 969 970 if (USE_CHUNKING) 971 { 972 // Wrap the value being written into one chunk (makes sense only for 973 // compatibility reasons). 974 outObj = output; 975 value_tag |= vf_CHUNKING; 976 } 977 else 978 outObj = output; 979 980 output.write_long(value_tag); 981 982 if ((value_tag & vf_MULTIPLE_IDS) != 0) 983 write_string_array(output, (String[]) ids); 984 else if ((value_tag & vf_ID) != 0) 985 write_string(output, (String) ids); 986 987 if (USE_CHUNKING) 988 { 989 // So far, write 0x55555555 instead of the chunk size (alignment may 990 // take place). 991 output.write_long(0x55555555); 992 // If the chunking is involved, the chunk size must be written here. 993 chunkSizeLocation = rout.getPosition() - INT_SIZE; 994 } 995 else 996 // Not in use for this case. 997 chunkSizeLocation = -1; 998 999 writeValue(outObj, value, helper); 1000 1001 if (USE_CHUNKING) 1002 { 1003 // Write the chunk size where the place for it was reserved. 1004 int chunkSize = rout.getPosition() - chunkSizeLocation - INT_SIZE; 1005 int current = rout.getPosition(); 1006 rout.seek(chunkSizeLocation); 1007 output.write_long(chunkSize); 1008 rout.seek(current); 1009 1010 // The end of record marker. 1011 output.write_long(-1); 1012 } 1013 } 1014 finally 1015 { 1016 if (runtime != null) 1017 runtime.target = null; 1018 } 1019 } 1020 1021 /** 1022 * Write value (after header). 1023 */ writeValue(OutputStream output, Serializable value, BoxedValueHelper helper)1024 static void writeValue(OutputStream output, Serializable value, 1025 BoxedValueHelper helper) 1026 { 1027 ((gnuValueStream) output).getRunTime().target = value; 1028 if (helper != null) 1029 helper.write_value(output, value); 1030 else if (!writeSelf(output, value)) 1031 { 1032 // Try to find helper via class loader. 1033 boolean ok = false; 1034 1035 if (!ok) 1036 { 1037 if (output instanceof BufferedCdrOutput) 1038 { 1039 BufferedCdrOutput b = (BufferedCdrOutput) output; 1040 if (b.runtime == null) 1041 b.runtime = new gnuRuntime(null, value); 1042 } 1043 1044 handler.writeValue(output, value); 1045 } 1046 } 1047 } 1048 1049 /** 1050 * Try to write value supposing that it implements self-streamable interfaces. 1051 * Return false if it does not or true on success. 1052 */ writeSelf(OutputStream output, Serializable value)1053 static boolean writeSelf(OutputStream output, Serializable value) 1054 { 1055 // User defined write method is present. 1056 if (value instanceof CustomMarshal) 1057 { 1058 ((CustomMarshal) value).marshal((DataOutputStream) output); 1059 return true; 1060 } 1061 else if (value instanceof Streamable) 1062 { 1063 ((Streamable) value)._write(output); 1064 return true; 1065 } 1066 return false; 1067 } 1068 1069 /** 1070 * Read the indirection data and return the object that was already written to 1071 * this stream. 1072 * 1073 * @param an_input the input stream, must be BufferredCdrInput. 1074 */ readIndirection(InputStream an_input)1075 static Serializable readIndirection(InputStream an_input) 1076 { 1077 if (!(an_input instanceof gnuValueStream)) 1078 throw new NO_IMPLEMENT(gnuValueStream.class.getName() 1079 + " expected as parameter"); 1080 1081 gnuValueStream in = (gnuValueStream) an_input; 1082 1083 int current_pos = in.getPosition(); 1084 1085 int offset = an_input.read_long(); 1086 if (offset > -INT_SIZE) 1087 { 1088 MARSHAL m = new MARSHAL("Indirection tag refers to " + offset 1089 + " (must be less than -" + INT_SIZE + ")"); 1090 m.minor = Minor.Offset; 1091 throw m; 1092 } 1093 1094 int stored_at = current_pos + offset; 1095 1096 if (in.getRunTime() == null) 1097 { 1098 MARSHAL m = new MARSHAL(stored_at + " offset " + offset + ": not written"); 1099 m.minor = Minor.Value; 1100 throw m; 1101 } 1102 1103 return (Serializable) in.getRunTime().isObjectWrittenAt(stored_at, offset); 1104 } 1105 1106 /** 1107 * Check the passed value tag for correctness. 1108 * 1109 * @param value_tag a tag to check, must be between 0x7fffff00 and 0x7fffffff 1110 * 1111 * @throws MARSHAL if the tag is outside this interval. 1112 */ checkTag(int value_tag)1113 static void checkTag(int value_tag) 1114 { 1115 if ((value_tag < 0x7fffff00 || value_tag > 0x7fffffff) 1116 && value_tag != vt_NULL && value_tag != vt_INDIRECTION) 1117 { 1118 MARSHAL m = new MARSHAL("Invalid value record, unsupported header tag: " 1119 + value_tag + " (0x" + Integer.toHexString(value_tag) + ")"); 1120 m.minor = Minor.ValueHeaderTag; 1121 throw m; 1122 } 1123 1124 if ((value_tag & vf_MULTIPLE_IDS) != 0 && (value_tag & vf_ID) == 0) 1125 { 1126 MARSHAL m = new MARSHAL("Invalid value record header flag combination (0x" 1127 + Integer.toHexString(value_tag) + ")"); 1128 m.minor = Minor.ValueHeaderFlags; 1129 throw m; 1130 } 1131 } 1132 1133 /** 1134 * Throw MARSHAL. 1135 */ throwIt(String msg, String id1, String id2, Throwable e)1136 static void throwIt(String msg, String id1, String id2, Throwable e) 1137 throws MARSHAL 1138 { 1139 MARSHAL m = new MARSHAL(msg + ":'" + id1 + "' versus '" + id2 + "'"); 1140 if (e != null) 1141 m.initCause(e); 1142 m.minor = Minor.Value; 1143 throw m; 1144 } 1145 1146 /** 1147 * Load class by name and create the instance. 1148 */ createInstance(String id, String[] ids, String codebase)1149 static Object createInstance(String id, String[] ids, String codebase) 1150 { 1151 Object o = null; 1152 1153 if (id != null) 1154 o = _createInstance(id, codebase); 1155 1156 if (ids != null) 1157 for (int i = 0; i < ids.length && o == null; i++) 1158 o = _createInstance(ids[i], codebase); 1159 return o; 1160 } 1161 _createInstance(String id, String codebase)1162 static Object _createInstance(String id, String codebase) 1163 { 1164 if (id == null) 1165 return null; 1166 if (id.equals(StringValueHelper.id())) 1167 return ""; 1168 StringTokenizer st = new StringTokenizer(id, ":"); 1169 1170 String prefix = st.nextToken(); 1171 if (prefix.equalsIgnoreCase("IDL")) 1172 return ObjectCreator.Idl2Object(id); 1173 else if (prefix.equalsIgnoreCase("RMI")) 1174 { 1175 String className = st.nextToken(); 1176 String hashCode = st.nextToken(); 1177 String sid = null; 1178 if (st.hasMoreElements()) 1179 sid = st.nextToken(); 1180 1181 try 1182 { 1183 Class objectClass = Util.loadClass(className, codebase, 1184 Vio.class.getClassLoader()); 1185 1186 String rid = ObjectCreator.getRepositoryId(objectClass); 1187 1188 if (!rid.equals(id)) 1189 { 1190 // If direct string comparison fails, compare by meaning. 1191 StringTokenizer st2 = new StringTokenizer(rid, ":"); 1192 if (!st2.nextToken().equals("RMI")) 1193 throw new InternalError("RMI format expected: '" + rid + "'"); 1194 if (!st2.nextToken().equals(className)) 1195 throwIt("Class name mismatch", id, rid, null); 1196 1197 try 1198 { 1199 long h1 = Long.parseLong(hashCode, 16); 1200 long h2 = Long.parseLong(st2.nextToken(), 16); 1201 if (h1 != h2) 1202 throwIt("Hashcode mismatch", id, rid, null); 1203 1204 if (sid != null && st2.hasMoreTokens()) 1205 { 1206 long s1 = Long.parseLong(hashCode, 16); 1207 long s2 = Long.parseLong(st2.nextToken(), 16); 1208 if (s1 != s2) 1209 throwIt("serialVersionUID mismatch", id, rid, null); 1210 } 1211 } 1212 catch (NumberFormatException e) 1213 { 1214 throwIt("Invalid hashcode or svuid format: ", id, rid, e); 1215 } 1216 } 1217 1218 // Low - level instantiation required here. 1219 return instantiateAnyWay(objectClass); 1220 } 1221 catch (Exception ex) 1222 { 1223 MARSHAL m = new MARSHAL("Unable to instantiate " + id); 1224 m.minor = Minor.Instantiation; 1225 m.initCause(ex); 1226 throw m; 1227 } 1228 } 1229 else 1230 throw new NO_IMPLEMENT("Unsupported prefix " + prefix + ":"); 1231 } 1232 1233 /** 1234 * Read string, expecting the probable indirection. 1235 */ read_string(InputStream input)1236 static String read_string(InputStream input) 1237 { 1238 gnuValueStream g = (gnuValueStream) input; 1239 int previous = g.getPosition(); 1240 int l = input.read_long(); 1241 if (l != vt_INDIRECTION) 1242 { 1243 g.seek(previous); 1244 String s = input.read_string(); 1245 if (g.getRunTime() == null) 1246 g.setRunTime(new gnuRuntime(null, null)); 1247 g.getRunTime().singleIdWritten(s, previous); 1248 return s; 1249 } 1250 else 1251 { 1252 gnuRuntime r = g.getRunTime(); 1253 int base = g.getPosition(); 1254 int delta = input.read_long(); 1255 if (r == null) 1256 { 1257 previous = g.getPosition(); 1258 g.seek(base + delta); 1259 String indir = input.read_string(); 1260 g.seek(previous); 1261 return indir; 1262 } 1263 else 1264 { 1265 return (String) r.isObjectWrittenAt(base + delta, delta); 1266 } 1267 } 1268 } 1269 1270 /** 1271 * Read string array, expecting the probable indirection. 1272 */ read_string_array(InputStream input)1273 static String[] read_string_array(InputStream input) 1274 { 1275 gnuValueStream g = (gnuValueStream) input; 1276 int previous = g.getPosition(); 1277 int l = input.read_long(); 1278 if (l != vt_INDIRECTION) 1279 { 1280 g.seek(previous); 1281 String[] s = StringSeqHelper.read(input); 1282 if (g.getRunTime() == null) 1283 g.setRunTime(new gnuRuntime(null, null)); 1284 g.getRunTime().objectWritten(s, previous); 1285 return s; 1286 } 1287 else 1288 { 1289 gnuRuntime r = g.getRunTime(); 1290 int base = g.getPosition(); 1291 int delta = input.read_long(); 1292 if (r == null) 1293 { 1294 previous = g.getPosition(); 1295 g.seek(base + delta); 1296 String[] indir = StringSeqHelper.read(input); 1297 g.seek(previous); 1298 return indir; 1299 } 1300 else 1301 { 1302 return (String[]) r.isObjectWrittenAt(base + delta, delta); 1303 } 1304 } 1305 } 1306 1307 /** 1308 * Write repository Id, probably shared. 1309 */ write_string(OutputStream output, String id)1310 static void write_string(OutputStream output, String id) 1311 { 1312 if (output instanceof gnuValueStream) 1313 { 1314 gnuValueStream b = (gnuValueStream) output; 1315 if (b != null) 1316 { 1317 int written = b.getRunTime().idWrittenAt(id); 1318 if (written >= 0) 1319 { 1320 // Reuse existing id record. 1321 output.write_long(vt_INDIRECTION); 1322 int p = b.getPosition(); 1323 output.write_long(written - p); 1324 } 1325 else 1326 { 1327 b.getRunTime().singleIdWritten(id, b.getPosition()); 1328 output.write_string(id); 1329 } 1330 } 1331 } 1332 else 1333 output.write_string(id); 1334 } 1335 1336 /** 1337 * Write repository Id, probably shared. 1338 */ write_string_array(OutputStream output, String[] ids)1339 static void write_string_array(OutputStream output, String[] ids) 1340 { 1341 if (output instanceof gnuValueStream) 1342 { 1343 gnuValueStream b = (gnuValueStream) output; 1344 if (b != null) 1345 { 1346 int written = b.getRunTime().idWrittenAt(ids); 1347 if (written >= 0) 1348 { 1349 // Reuse existing id record. 1350 output.write_long(vt_INDIRECTION); 1351 int p = b.getPosition(); 1352 output.write_long(written - p); 1353 } 1354 else 1355 { 1356 b.getRunTime().multipleIdsWritten(ids, b.getPosition()); 1357 StringSeqHelper.write(output, ids); 1358 } 1359 } 1360 } 1361 else 1362 StringSeqHelper.write(output, ids); 1363 } 1364 1365 /** 1366 * Get the helper that could write the given object, or null if no pre-defined 1367 * helper available for this object. 1368 */ getHelper(Class x, Object ids)1369 public static BoxedValueHelper getHelper(Class x, Object ids) 1370 { 1371 if (x != null && x.equals(String.class)) 1372 return m_StringValueHelper; 1373 else if (x != null && x.isArray()) 1374 return new ArrayValueHelper(x); 1375 else if (ids instanceof String) 1376 return locateHelper((String) ids); 1377 else if (ids instanceof String[]) 1378 { 1379 String[] ia = (String[]) ids; 1380 BoxedValueHelper h; 1381 for (int i = 0; i < ia.length; i++) 1382 { 1383 h = locateHelper(ia[i]); 1384 if (h != null) 1385 return h; 1386 } 1387 return null; 1388 } 1389 else 1390 return null; 1391 } 1392 1393 /** 1394 * Get the helper that could write the given object, or null if no pre-defined 1395 * helper available for this object. 1396 */ getHelper(Class x, String id)1397 public static BoxedValueHelper getHelper(Class x, String id) 1398 { 1399 if (x != null && x.equals(String.class)) 1400 return m_StringValueHelper; 1401 else if (x != null && x.isArray()) 1402 return new ArrayValueHelper(x); 1403 else 1404 return locateHelper(id); 1405 } 1406 1407 /** 1408 * Try to locate helper from the repository id. 1409 */ locateHelper(String id)1410 static BoxedValueHelper locateHelper(String id) 1411 { 1412 if (id != null) 1413 { 1414 if (id.equals(m_StringValueHelper.get_id())) 1415 return m_StringValueHelper; 1416 else 1417 // Try to locate helper for IDL type. 1418 if (id.startsWith("IDL:")) 1419 { 1420 try 1421 { 1422 Class helperClass = ObjectCreator.findHelper(id); 1423 if (BoxedValueHelper.class.isAssignableFrom(helperClass)) 1424 return (BoxedValueHelper) helperClass.newInstance(); 1425 else if (helperClass != null) 1426 return new IDLTypeHelper(helperClass); 1427 else 1428 return null; 1429 } 1430 catch (Exception ex) 1431 { 1432 return null; 1433 } 1434 } 1435 } 1436 return null; 1437 } 1438 1439 /** 1440 * Get the current position. 1441 */ getCurrentPosition(InputStream x)1442 static int getCurrentPosition(InputStream x) 1443 { 1444 if (x instanceof gnuValueStream) 1445 return ((gnuValueStream) x).getPosition(); 1446 else 1447 return 0; 1448 } 1449 1450 /** 1451 * Instantiate an instance of this class anyway; also in the case when it has 1452 * no parameterless or any other constructor. The fields will be assigned 1453 * while reading the class from the stream. 1454 * 1455 * @param clazz a class for that the instance should be instantiated. 1456 */ instantiateAnyWay(Class clazz)1457 public static Object instantiateAnyWay(Class clazz) 1458 throws Exception 1459 { 1460 Class first_nonserial = clazz; 1461 1462 while (Serializable.class.isAssignableFrom(first_nonserial) 1463 || Modifier.isAbstract(first_nonserial.getModifiers())) 1464 first_nonserial = first_nonserial.getSuperclass(); 1465 1466 final Class local_constructor_class = first_nonserial; 1467 1468 Constructor constructor = local_constructor_class.getDeclaredConstructor(new Class[0]); 1469 1470 return VMVio.allocateObject(clazz, constructor.getDeclaringClass(), 1471 constructor); 1472 } 1473 }