IIOPOutputStream.java revision 608:7e06bf1dcb09
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 int stackMark = classDescStack.size(); 563 try { 564 ObjectStreamClass next; 565 while ((next = currentClassDesc.getSuperclass()) != null) { 566 classDescStack.push(currentClassDesc); 567 currentClassDesc = next; 568 } 569 570 /* 571 * For currentClassDesc and all the pushed class descriptors 572 * If the class is writing its own data 573 * set blockData = true; call the class writeObject method 574 * If not 575 * invoke either the defaultWriteObject method. 576 */ 577 do { 578 579 WriteObjectState oldState = writeObjectState; 580 581 try { 582 583 setState(NOT_IN_WRITE_OBJECT); 584 585 if (currentClassDesc.hasWriteObject()) { 586 invokeObjectWriter(currentClassDesc, obj ); 587 } else { 588 defaultWriteObjectDelegate(); 589 } 590 } finally { 591 setState(oldState); 592 } 593 594 } while (classDescStack.size() > stackMark && 595 (currentClassDesc = (ObjectStreamClass)classDescStack.pop()) != null); 596 } finally { 597 classDescStack.setSize(stackMark); 598 } 599 } 600 } 601 602 /* 603 * Invoke writer. 604 * _REVISIT_ invokeObjectWriter and invokeObjectReader behave inconsistently with each other since 605 * the reader returns a boolean...fix later 606 */ 607 private void invokeObjectWriter(ObjectStreamClass osc, Object obj) 608 throws IOException 609 { 610 Class c = osc.forClass() ; 611 612 try { 613 614 // Write format version 615 orbStream.write_octet(streamFormatVersion); 616 617 writeObjectState.enterWriteObject(this); 618 619 // writeObject(obj, c, this); 620 osc.writeObjectMethod.invoke( obj, writeObjectArgList ) ; 621 622 writeObjectState.exitWriteObject(this); 623 624 } catch (InvocationTargetException e) { 625 Throwable t = e.getTargetException(); 626 if (t instanceof IOException) 627 throw (IOException)t; 628 else if (t instanceof RuntimeException) 629 throw (RuntimeException) t; 630 else if (t instanceof Error) 631 throw (Error) t; 632 else 633 // XXX I18N, Logging needed. 634 throw new Error("invokeObjectWriter internal error",e); 635 } catch (IllegalAccessException e) { 636 // cannot happen 637 } 638 } 639 640 void writeField(ObjectStreamField field, Object value) throws IOException { 641 switch (field.getTypeCode()) { 642 case 'B': 643 if (value == null) 644 orbStream.write_octet((byte)0); 645 else 646 orbStream.write_octet(((Byte)value).byteValue()); 647 break; 648 case 'C': 649 if (value == null) 650 orbStream.write_wchar((char)0); 651 else 652 orbStream.write_wchar(((Character)value).charValue()); 653 break; 654 case 'F': 655 if (value == null) 656 orbStream.write_float((float)0); 657 else 658 orbStream.write_float(((Float)value).floatValue()); 659 break; 660 case 'D': 661 if (value == null) 662 orbStream.write_double((double)0); 663 else 664 orbStream.write_double(((Double)value).doubleValue()); 665 break; 666 case 'I': 667 if (value == null) 668 orbStream.write_long((int)0); 669 else 670 orbStream.write_long(((Integer)value).intValue()); 671 break; 672 case 'J': 673 if (value == null) 674 orbStream.write_longlong((long)0); 675 else 676 orbStream.write_longlong(((Long)value).longValue()); 677 break; 678 case 'S': 679 if (value == null) 680 orbStream.write_short((short)0); 681 else 682 orbStream.write_short(((Short)value).shortValue()); 683 break; 684 case 'Z': 685 if (value == null) 686 orbStream.write_boolean(false); 687 else 688 orbStream.write_boolean(((Boolean)value).booleanValue()); 689 break; 690 case '[': 691 case 'L': 692 // What to do if it's null? 693 writeObjectField(field, value); 694 break; 695 default: 696 // XXX I18N, Logging needed. 697 throw new InvalidClassException(currentClassDesc.getName()); 698 } 699 } 700 701 private void writeObjectField(ObjectStreamField field, 702 Object objectValue) throws IOException { 703 704 if (ObjectStreamClassCorbaExt.isAny(field.getTypeString())) { 705 javax.rmi.CORBA.Util.writeAny(orbStream, objectValue); 706 } 707 else { 708 Class type = field.getType(); 709 int callType = ValueHandlerImpl.kValueType; 710 711 if (type.isInterface()) { 712 String className = type.getName(); 713 714 if (java.rmi.Remote.class.isAssignableFrom(type)) { 715 716 // RMI Object reference... 717 718 callType = ValueHandlerImpl.kRemoteType; 719 720 721 } else if (org.omg.CORBA.Object.class.isAssignableFrom(type)){ 722 723 // IDL Object reference... 724 callType = ValueHandlerImpl.kRemoteType; 725 726 } else if (RepositoryId.isAbstractBase(type)) { 727 // IDL Abstract Object reference... 728 callType = ValueHandlerImpl.kAbstractType; 729 } else if (ObjectStreamClassCorbaExt.isAbstractInterface(type)) { 730 callType = ValueHandlerImpl.kAbstractType; 731 } 732 } 733 734 switch (callType) { 735 case ValueHandlerImpl.kRemoteType: 736 Util.writeRemoteObject(orbStream, objectValue); 737 break; 738 case ValueHandlerImpl.kAbstractType: 739 Util.writeAbstractObject(orbStream, objectValue); 740 break; 741 case ValueHandlerImpl.kValueType: 742 try{ 743 orbStream.write_value((java.io.Serializable)objectValue, type); 744 } 745 catch(ClassCastException cce){ 746 if (objectValue instanceof java.io.Serializable) 747 throw cce; 748 else 749 Utility.throwNotSerializableForCorba(objectValue.getClass().getName()); 750 } 751 } 752 } 753 } 754 755 /* Write the fields of the specified class by invoking the appropriate 756 * write* method on this class. 757 */ 758 private void outputClassFields(Object o, Class cl, 759 ObjectStreamField[] fields) 760 throws IOException, InvalidClassException { 761 762 for (int i = 0; i < fields.length; i++) { 763 if (fields[i].getField() == null) 764 // XXX I18N, Logging needed. 765 throw new InvalidClassException(cl.getName(), 766 "Nonexistent field " + fields[i].getName()); 767 768 try { 769 switch (fields[i].getTypeCode()) { 770 case 'B': 771 byte byteValue = fields[i].getField().getByte( o ) ; 772 orbStream.write_octet(byteValue); 773 break; 774 case 'C': 775 char charValue = fields[i].getField().getChar( o ) ; 776 orbStream.write_wchar(charValue); 777 break; 778 case 'F': 779 float floatValue = fields[i].getField().getFloat( o ) ; 780 orbStream.write_float(floatValue); 781 break; 782 case 'D' : 783 double doubleValue = fields[i].getField().getDouble( o ) ; 784 orbStream.write_double(doubleValue); 785 break; 786 case 'I': 787 int intValue = fields[i].getField().getInt( o ) ; 788 orbStream.write_long(intValue); 789 break; 790 case 'J': 791 long longValue = fields[i].getField().getLong( o ) ; 792 orbStream.write_longlong(longValue); 793 break; 794 case 'S': 795 short shortValue = fields[i].getField().getShort( o ) ; 796 orbStream.write_short(shortValue); 797 break; 798 case 'Z': 799 boolean booleanValue = fields[i].getField().getBoolean( o ) ; 800 orbStream.write_boolean(booleanValue); 801 break; 802 case '[': 803 case 'L': 804 Object objectValue = fields[i].getField().get( o ) ; 805 writeObjectField(fields[i], objectValue); 806 break; 807 default: 808 // XXX I18N, Logging needed. 809 throw new InvalidClassException(cl.getName()); 810 } 811 } catch (IllegalAccessException exc) { 812 throw wrapper.illegalFieldAccess( exc, fields[i].getName() ) ; 813 } 814 } 815 } 816} 817