IIOPOutputStream.java revision 717:a88d571b42b6
1/* 2 * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25/* 26 * Licensed Materials - Property of IBM 27 * RMI-IIOP v1.0 28 * Copyright IBM Corp. 1998 1999 All Rights Reserved 29 * 30 */ 31 32package com.sun.corba.se.impl.io; 33 34import org.omg.CORBA.INTERNAL; 35import org.omg.CORBA.portable.OutputStream; 36 37import java.security.AccessController ; 38import java.security.PrivilegedAction ; 39 40import java.io.IOException; 41import java.io.DataOutputStream; 42import java.io.Serializable; 43import java.io.InvalidClassException; 44import java.io.StreamCorruptedException; 45import java.io.Externalizable; 46import java.io.ObjectStreamException; 47import java.io.NotSerializableException; 48import java.io.NotActiveException; 49 50import java.lang.reflect.InvocationTargetException; 51import java.lang.reflect.Field; 52 53import java.util.Stack; 54 55import javax.rmi.CORBA.Util; 56import javax.rmi.CORBA.ValueHandlerMultiFormat; 57 58import sun.corba.Bridge ; 59 60import com.sun.corba.se.impl.io.ObjectStreamClass; 61import com.sun.corba.se.impl.util.Utility; 62import com.sun.corba.se.impl.util.RepositoryId; 63 64import com.sun.corba.se.spi.logging.CORBALogDomains ; 65import com.sun.corba.se.impl.logging.UtilSystemException ; 66 67/** 68 * IIOPOutputStream is ... 69 * 70 * @author Stephen Lewallen 71 * @since JDK1.1.6 72 */ 73 74public class IIOPOutputStream 75 extends com.sun.corba.se.impl.io.OutputStreamHook 76{ 77 private UtilSystemException wrapper = UtilSystemException.get( 78 CORBALogDomains.RPC_ENCODING ) ; 79 80 private static Bridge bridge = 81 (Bridge)AccessController.doPrivileged( 82 new PrivilegedAction() { 83 public Object run() { 84 return Bridge.get() ; 85 } 86 } 87 ) ; 88 89 private org.omg.CORBA_2_3.portable.OutputStream orbStream; 90 91 private Object currentObject = null; 92 93 private ObjectStreamClass currentClassDesc = null; 94 95 private int recursionDepth = 0; 96 97 private int simpleWriteDepth = 0; 98 99 private IOException abortIOException = null; 100 101 private java.util.Stack classDescStack = new java.util.Stack(); 102 103 // Used when calling an object's writeObject method 104 private Object[] writeObjectArgList = {this}; 105 106 public IIOPOutputStream() 107 throws java.io.IOException 108 { 109 super(); 110 } 111 112 // If using RMI-IIOP stream format version 2, this tells 113 // the ORB stream (which must be a ValueOutputStream) to 114 // begin a new valuetype to contain the optional data 115 // of the writeObject method. 116 protected void beginOptionalCustomData() { 117 118 if (streamFormatVersion == 2) { 119 120 org.omg.CORBA.portable.ValueOutputStream vout 121 = (org.omg.CORBA.portable.ValueOutputStream)orbStream; 122 123 vout.start_value(currentClassDesc.getRMIIIOPOptionalDataRepId()); 124 } 125 } 126 127 final void setOrbStream(org.omg.CORBA_2_3.portable.OutputStream os) { 128 orbStream = os; 129 } 130 131 final org.omg.CORBA_2_3.portable.OutputStream getOrbStream() { 132 return orbStream; 133 } 134 135 final void increaseRecursionDepth(){ 136 recursionDepth++; 137 } 138 139 final int decreaseRecursionDepth(){ 140 return --recursionDepth; 141 } 142 143 /** 144 * Override the actions of the final method "writeObject()" 145 * in ObjectOutputStream. 146 * @since JDK1.1.6 147 */ 148 public final void writeObjectOverride(Object obj) 149 throws IOException 150 { 151 writeObjectState.writeData(this); 152 153 Util.writeAbstractObject((OutputStream)orbStream, obj); 154 } 155 156 /** 157 * Override the actions of the final method "writeObject()" 158 * in ObjectOutputStream. 159 * @since JDK1.1.6 160 */ 161 public final void simpleWriteObject(Object obj, byte formatVersion) 162 /* throws IOException */ 163 { 164 byte oldStreamFormatVersion = streamFormatVersion; 165 166 streamFormatVersion = formatVersion; 167 168 Object prevObject = currentObject; 169 ObjectStreamClass prevClassDesc = currentClassDesc; 170 simpleWriteDepth++; 171 172 try { 173 // if (!checkSpecialClasses(obj) && !checkSubstitutableSpecialClasses(obj)) 174 outputObject(obj); 175 176 } catch (IOException ee) { 177 if (abortIOException == null) 178 abortIOException = ee; 179 } finally { 180 /* Restore state of previous call incase this is a nested call */ 181 streamFormatVersion = oldStreamFormatVersion; 182 simpleWriteDepth--; 183 currentObject = prevObject; 184 currentClassDesc = prevClassDesc; 185 } 186 187 /* If the recursion depth is 0, test for and clear the pending exception. 188 * If there is a pending exception throw it. 189 */ 190 IOException pending = abortIOException; 191 if (simpleWriteDepth == 0) 192 abortIOException = null; 193 if (pending != null) { 194 bridge.throwException( pending ) ; 195 } 196 } 197 198 // Required by the superclass. 199 ObjectStreamField[] getFieldsNoCopy() { 200 return currentClassDesc.getFieldsNoCopy(); 201 } 202 203 /** 204 * Override the actions of the final method "defaultWriteObject()" 205 * in ObjectOutputStream. 206 * @since JDK1.1.6 207 */ 208 public final void defaultWriteObjectDelegate() 209 /* throws IOException */ 210 { 211 try { 212 if (currentObject == null || currentClassDesc == null) 213 // XXX I18N, Logging needed. 214 throw new NotActiveException("defaultWriteObjectDelegate"); 215 216 ObjectStreamField[] fields = 217 currentClassDesc.getFieldsNoCopy(); 218 if (fields.length > 0) { 219 outputClassFields(currentObject, currentClassDesc.forClass(), 220 fields); 221 } 222 } catch(IOException ioe) { 223 bridge.throwException(ioe); 224 } 225 } 226 227 /** 228 * Override the actions of the final method "enableReplaceObject()" 229 * in ObjectOutputStream. 230 * @since JDK1.1.6 231 */ 232 public final boolean enableReplaceObjectDelegate(boolean enable) 233 /* throws SecurityException */ 234 { 235 return false; 236 237 } 238 239 240 protected final void annotateClass(Class<?> cl) throws IOException{ 241 // XXX I18N, Logging needed. 242 throw new IOException("Method annotateClass not supported"); 243 } 244 245 public final void close() throws IOException{ 246 // no op 247 } 248 249 protected final void drain() throws IOException{ 250 // no op 251 } 252 253 public final void flush() throws IOException{ 254 try{ 255 orbStream.flush(); 256 } catch(Error e) { 257 IOException ioexc = new IOException(e.getMessage()); 258 ioexc.initCause(e) ; 259 throw ioexc ; 260 } 261 } 262 263 protected final Object replaceObject(Object obj) throws IOException{ 264 // XXX I18N, Logging needed. 265 throw new IOException("Method replaceObject not supported"); 266 } 267 268 /** 269 * Reset will disregard the state of any objects already written 270 * to the stream. The state is reset to be the same as a new 271 * ObjectOutputStream. The current point in the stream is marked 272 * as reset so the corresponding ObjectInputStream will be reset 273 * at the same point. Objects previously written to the stream 274 * will not be refered to as already being in the stream. They 275 * will be written to the stream again. 276 * @since JDK1.1 277 */ 278 public final void reset() throws IOException{ 279 try{ 280 //orbStream.reset(); 281 282 if (currentObject != null || currentClassDesc != null) 283 // XXX I18N, Logging needed. 284 throw new IOException("Illegal call to reset"); 285 286 abortIOException = null; 287 288 if (classDescStack == null) 289 classDescStack = new java.util.Stack(); 290 else 291 classDescStack.setSize(0); 292 293 } catch(Error e) { 294 IOException ioexc = new IOException(e.getMessage()); 295 ioexc.initCause(e) ; 296 throw ioexc ; 297 } 298 } 299 300 public final void write(byte b[]) throws IOException{ 301 try{ 302 writeObjectState.writeData(this); 303 304 orbStream.write_octet_array(b, 0, b.length); 305 } catch(Error e) { 306 IOException ioexc = new IOException(e.getMessage()); 307 ioexc.initCause(e) ; 308 throw ioexc ; 309 } 310 } 311 312 public final void write(byte b[], int off, int len) throws IOException{ 313 try{ 314 writeObjectState.writeData(this); 315 316 orbStream.write_octet_array(b, off, len); 317 } catch(Error e) { 318 IOException ioexc = new IOException(e.getMessage()); 319 ioexc.initCause(e) ; 320 throw ioexc ; 321 } 322 } 323 324 public final void write(int data) throws IOException{ 325 try{ 326 writeObjectState.writeData(this); 327 328 orbStream.write_octet((byte)(data & 0xFF)); 329 } catch(Error e) { 330 IOException ioexc = new IOException(e.getMessage()); 331 ioexc.initCause(e) ; 332 throw ioexc ; 333 } 334 } 335 336 public final void writeBoolean(boolean data) throws IOException{ 337 try{ 338 writeObjectState.writeData(this); 339 340 orbStream.write_boolean(data); 341 } catch(Error e) { 342 IOException ioexc = new IOException(e.getMessage()); 343 ioexc.initCause(e) ; 344 throw ioexc ; 345 } 346 } 347 348 public final void writeByte(int data) throws IOException{ 349 try{ 350 writeObjectState.writeData(this); 351 352 orbStream.write_octet((byte)data); 353 } catch(Error e) { 354 IOException ioexc = new IOException(e.getMessage()); 355 ioexc.initCause(e) ; 356 throw ioexc ; 357 } 358 } 359 360 public final void writeBytes(String data) throws IOException{ 361 try{ 362 writeObjectState.writeData(this); 363 364 byte buf[] = data.getBytes(); 365 orbStream.write_octet_array(buf, 0, buf.length); 366 } catch(Error e) { 367 IOException ioexc = new IOException(e.getMessage()); 368 ioexc.initCause(e) ; 369 throw ioexc ; 370 } 371 } 372 373 public final void writeChar(int data) throws IOException{ 374 try{ 375 writeObjectState.writeData(this); 376 377 orbStream.write_wchar((char)data); 378 } catch(Error e) { 379 IOException ioexc = new IOException(e.getMessage()); 380 ioexc.initCause(e) ; 381 throw ioexc ; 382 } 383 } 384 385 public final void writeChars(String data) throws IOException{ 386 try{ 387 writeObjectState.writeData(this); 388 389 char buf[] = data.toCharArray(); 390 orbStream.write_wchar_array(buf, 0, buf.length); 391 } catch(Error e) { 392 IOException ioexc = new IOException(e.getMessage()); 393 ioexc.initCause(e) ; 394 throw ioexc ; 395 } 396 } 397 398 public final void writeDouble(double data) throws IOException{ 399 try{ 400 writeObjectState.writeData(this); 401 402 orbStream.write_double(data); 403 } catch(Error e) { 404 IOException ioexc = new IOException(e.getMessage()); 405 ioexc.initCause(e) ; 406 throw ioexc ; 407 } 408 } 409 410 public final void writeFloat(float data) throws IOException{ 411 try{ 412 writeObjectState.writeData(this); 413 414 orbStream.write_float(data); 415 } catch(Error e) { 416 IOException ioexc = new IOException(e.getMessage()); 417 ioexc.initCause(e) ; 418 throw ioexc ; 419 } 420 } 421 422 public final void writeInt(int data) throws IOException{ 423 try{ 424 writeObjectState.writeData(this); 425 426 orbStream.write_long(data); 427 } catch(Error e) { 428 IOException ioexc = new IOException(e.getMessage()); 429 ioexc.initCause(e) ; 430 throw ioexc ; 431 } 432 } 433 434 public final void writeLong(long data) throws IOException{ 435 try{ 436 writeObjectState.writeData(this); 437 438 orbStream.write_longlong(data); 439 } catch(Error e) { 440 IOException ioexc = new IOException(e.getMessage()); 441 ioexc.initCause(e) ; 442 throw ioexc ; 443 } 444 } 445 446 public final void writeShort(int data) throws IOException{ 447 try{ 448 writeObjectState.writeData(this); 449 450 orbStream.write_short((short)data); 451 } catch(Error e) { 452 IOException ioexc = new IOException(e.getMessage()); 453 ioexc.initCause(e) ; 454 throw ioexc ; 455 } 456 } 457 458 protected final void writeStreamHeader() throws IOException{ 459 // no op 460 } 461 462 /** 463 * Helper method for correcting the Kestrel bug 4367783 (dealing 464 * with larger than 8-bit chars). The old behavior is preserved 465 * in orbutil.IIOPInputStream_1_3 in order to interoperate with 466 * our legacy ORBs. 467 */ 468 protected void internalWriteUTF(org.omg.CORBA.portable.OutputStream stream, 469 String data) 470 { 471 stream.write_wstring(data); 472 } 473 474 public final void writeUTF(String data) throws IOException{ 475 try{ 476 writeObjectState.writeData(this); 477 478 internalWriteUTF(orbStream, data); 479 } catch(Error e) { 480 IOException ioexc = new IOException(e.getMessage()); 481 ioexc.initCause(e) ; 482 throw ioexc ; 483 } 484 } 485 486 // INTERNAL UTILITY METHODS 487 /* 488 * Check for special cases of serializing objects. 489 * These objects are not subject to replacement. 490 */ 491 private boolean checkSpecialClasses(Object obj) throws IOException { 492 493 /* 494 * If this is a class, don't allow substitution 495 */ 496 //if (obj instanceof Class) { 497 // throw new IOException("Serialization of Class not supported"); 498 //} 499 500 if (obj instanceof ObjectStreamClass) { 501 // XXX I18N, Logging needed. 502 throw new IOException("Serialization of ObjectStreamClass not supported"); 503 } 504 505 return false; 506 } 507 508 /* 509 * Check for special cases of substitutable serializing objects. 510 * These classes are replaceable. 511 */ 512 private boolean checkSubstitutableSpecialClasses(Object obj) 513 throws IOException 514 { 515 if (obj instanceof String) { 516 orbStream.write_value((java.io.Serializable)obj); 517 return true; 518 } 519 520 //if (obj.getClass().isArray()) { 521 // outputArray(obj); 522 // return true; 523 //} 524 525 return false; 526 } 527 528 /* 529 * Write out the object 530 */ 531 private void outputObject(final Object obj) throws IOException{ 532 533 currentObject = obj; 534 Class currclass = obj.getClass(); 535 536 /* Get the Class descriptor for this class, 537 * Throw a NotSerializableException if there is none. 538 */ 539 currentClassDesc = ObjectStreamClass.lookup(currclass); 540 if (currentClassDesc == null) { 541 // XXX I18N, Logging needed. 542 throw new NotSerializableException(currclass.getName()); 543 } 544 545 /* If the object is externalizable, 546 * call writeExternal. 547 * else do Serializable processing. 548 */ 549 if (currentClassDesc.isExternalizable()) { 550 // Write format version 551 orbStream.write_octet(streamFormatVersion); 552 553 Externalizable ext = (Externalizable)obj; 554 ext.writeExternal(this); 555 556 } else { 557 558 /* The object's classes should be processed from supertype to subtype 559 * Push all the clases of the current object onto a stack. 560 * Remember the stack pointer where this set of classes is being pushed. 561 */ 562 if (currentClassDesc.forClass().getName().equals("java.lang.String")) { 563 this.writeUTF((String)obj); 564 return; 565 } 566 int stackMark = classDescStack.size(); 567 try { 568 ObjectStreamClass next; 569 while ((next = currentClassDesc.getSuperclass()) != null) { 570 classDescStack.push(currentClassDesc); 571 currentClassDesc = next; 572 } 573 574 /* 575 * For currentClassDesc and all the pushed class descriptors 576 * If the class is writing its own data 577 * set blockData = true; call the class writeObject method 578 * If not 579 * invoke either the defaultWriteObject method. 580 */ 581 do { 582 583 WriteObjectState oldState = writeObjectState; 584 585 try { 586 587 setState(NOT_IN_WRITE_OBJECT); 588 589 if (currentClassDesc.hasWriteObject()) { 590 invokeObjectWriter(currentClassDesc, obj ); 591 } else { 592 defaultWriteObjectDelegate(); 593 } 594 } finally { 595 setState(oldState); 596 } 597 598 } while (classDescStack.size() > stackMark && 599 (currentClassDesc = (ObjectStreamClass)classDescStack.pop()) != null); 600 } finally { 601 classDescStack.setSize(stackMark); 602 } 603 } 604 } 605 606 /* 607 * Invoke writer. 608 * _REVISIT_ invokeObjectWriter and invokeObjectReader behave inconsistently with each other since 609 * the reader returns a boolean...fix later 610 */ 611 private void invokeObjectWriter(ObjectStreamClass osc, Object obj) 612 throws IOException 613 { 614 Class c = osc.forClass() ; 615 616 try { 617 618 // Write format version 619 orbStream.write_octet(streamFormatVersion); 620 621 writeObjectState.enterWriteObject(this); 622 623 // writeObject(obj, c, this); 624 osc.writeObjectMethod.invoke( obj, writeObjectArgList ) ; 625 626 writeObjectState.exitWriteObject(this); 627 628 } catch (InvocationTargetException e) { 629 Throwable t = e.getTargetException(); 630 if (t instanceof IOException) 631 throw (IOException)t; 632 else if (t instanceof RuntimeException) 633 throw (RuntimeException) t; 634 else if (t instanceof Error) 635 throw (Error) t; 636 else 637 // XXX I18N, Logging needed. 638 throw new Error("invokeObjectWriter internal error",e); 639 } catch (IllegalAccessException e) { 640 // cannot happen 641 } 642 } 643 644 void writeField(ObjectStreamField field, Object value) throws IOException { 645 switch (field.getTypeCode()) { 646 case 'B': 647 if (value == null) 648 orbStream.write_octet((byte)0); 649 else 650 orbStream.write_octet(((Byte)value).byteValue()); 651 break; 652 case 'C': 653 if (value == null) 654 orbStream.write_wchar((char)0); 655 else 656 orbStream.write_wchar(((Character)value).charValue()); 657 break; 658 case 'F': 659 if (value == null) 660 orbStream.write_float((float)0); 661 else 662 orbStream.write_float(((Float)value).floatValue()); 663 break; 664 case 'D': 665 if (value == null) 666 orbStream.write_double((double)0); 667 else 668 orbStream.write_double(((Double)value).doubleValue()); 669 break; 670 case 'I': 671 if (value == null) 672 orbStream.write_long((int)0); 673 else 674 orbStream.write_long(((Integer)value).intValue()); 675 break; 676 case 'J': 677 if (value == null) 678 orbStream.write_longlong((long)0); 679 else 680 orbStream.write_longlong(((Long)value).longValue()); 681 break; 682 case 'S': 683 if (value == null) 684 orbStream.write_short((short)0); 685 else 686 orbStream.write_short(((Short)value).shortValue()); 687 break; 688 case 'Z': 689 if (value == null) 690 orbStream.write_boolean(false); 691 else 692 orbStream.write_boolean(((Boolean)value).booleanValue()); 693 break; 694 case '[': 695 case 'L': 696 // What to do if it's null? 697 writeObjectField(field, value); 698 break; 699 default: 700 // XXX I18N, Logging needed. 701 throw new InvalidClassException(currentClassDesc.getName()); 702 } 703 } 704 705 private void writeObjectField(ObjectStreamField field, 706 Object objectValue) throws IOException { 707 708 if (ObjectStreamClassCorbaExt.isAny(field.getTypeString())) { 709 javax.rmi.CORBA.Util.writeAny(orbStream, objectValue); 710 } 711 else { 712 Class type = field.getType(); 713 int callType = ValueHandlerImpl.kValueType; 714 715 if (type.isInterface()) { 716 String className = type.getName(); 717 718 if (java.rmi.Remote.class.isAssignableFrom(type)) { 719 720 // RMI Object reference... 721 722 callType = ValueHandlerImpl.kRemoteType; 723 724 725 } else if (org.omg.CORBA.Object.class.isAssignableFrom(type)){ 726 727 // IDL Object reference... 728 callType = ValueHandlerImpl.kRemoteType; 729 730 } else if (RepositoryId.isAbstractBase(type)) { 731 // IDL Abstract Object reference... 732 callType = ValueHandlerImpl.kAbstractType; 733 } else if (ObjectStreamClassCorbaExt.isAbstractInterface(type)) { 734 callType = ValueHandlerImpl.kAbstractType; 735 } 736 } 737 738 switch (callType) { 739 case ValueHandlerImpl.kRemoteType: 740 Util.writeRemoteObject(orbStream, objectValue); 741 break; 742 case ValueHandlerImpl.kAbstractType: 743 Util.writeAbstractObject(orbStream, objectValue); 744 break; 745 case ValueHandlerImpl.kValueType: 746 try{ 747 orbStream.write_value((java.io.Serializable)objectValue, type); 748 } 749 catch(ClassCastException cce){ 750 if (objectValue instanceof java.io.Serializable) 751 throw cce; 752 else 753 Utility.throwNotSerializableForCorba(objectValue.getClass().getName()); 754 } 755 } 756 } 757 } 758 759 /* Write the fields of the specified class by invoking the appropriate 760 * write* method on this class. 761 */ 762 private void outputClassFields(Object o, Class cl, 763 ObjectStreamField[] fields) 764 throws IOException, InvalidClassException { 765 766 for (int i = 0; i < fields.length; i++) { 767 if (fields[i].getField() == null) 768 // XXX I18N, Logging needed. 769 throw new InvalidClassException(cl.getName(), 770 "Nonexistent field " + fields[i].getName()); 771 772 try { 773 switch (fields[i].getTypeCode()) { 774 case 'B': 775 byte byteValue = fields[i].getField().getByte( o ) ; 776 orbStream.write_octet(byteValue); 777 break; 778 case 'C': 779 char charValue = fields[i].getField().getChar( o ) ; 780 orbStream.write_wchar(charValue); 781 break; 782 case 'F': 783 float floatValue = fields[i].getField().getFloat( o ) ; 784 orbStream.write_float(floatValue); 785 break; 786 case 'D' : 787 double doubleValue = fields[i].getField().getDouble( o ) ; 788 orbStream.write_double(doubleValue); 789 break; 790 case 'I': 791 int intValue = fields[i].getField().getInt( o ) ; 792 orbStream.write_long(intValue); 793 break; 794 case 'J': 795 long longValue = fields[i].getField().getLong( o ) ; 796 orbStream.write_longlong(longValue); 797 break; 798 case 'S': 799 short shortValue = fields[i].getField().getShort( o ) ; 800 orbStream.write_short(shortValue); 801 break; 802 case 'Z': 803 boolean booleanValue = fields[i].getField().getBoolean( o ) ; 804 orbStream.write_boolean(booleanValue); 805 break; 806 case '[': 807 case 'L': 808 Object objectValue = fields[i].getField().get( o ) ; 809 writeObjectField(fields[i], objectValue); 810 break; 811 default: 812 // XXX I18N, Logging needed. 813 throw new InvalidClassException(cl.getName()); 814 } 815 } catch (IllegalAccessException exc) { 816 throw wrapper.illegalFieldAccess( exc, fields[i].getName() ) ; 817 } 818 } 819 } 820} 821