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