Frames | No Frames |
1: /* UtilDelegateImpl.java -- 2: Copyright (C) 2002, 2005, 2006 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.javax.rmi.CORBA; 40: 41: import gnu.classpath.VMStackWalker; 42: 43: import gnu.CORBA.Minor; 44: import gnu.CORBA.ObjectCreator; 45: import gnu.CORBA.Poa.ORB_1_4; 46: import gnu.CORBA.Poa.AOM; 47: import gnu.CORBA.Poa.gnuPOA; 48: import gnu.CORBA.typecodes.GeneralTypeCode; 49: 50: import org.omg.CORBA.Any; 51: import org.omg.CORBA.BAD_PARAM; 52: import org.omg.CORBA.COMM_FAILURE; 53: import org.omg.CORBA.CompletionStatus; 54: import org.omg.CORBA.INVALID_TRANSACTION; 55: import org.omg.CORBA.INV_OBJREF; 56: import org.omg.CORBA.MARSHAL; 57: import org.omg.CORBA.NO_PERMISSION; 58: import org.omg.CORBA.OBJECT_NOT_EXIST; 59: import org.omg.CORBA.OMGVMCID; 60: import org.omg.CORBA.ORB; 61: import org.omg.CORBA.SystemException; 62: import org.omg.CORBA.TCKind; 63: import org.omg.CORBA.TRANSACTION_REQUIRED; 64: import org.omg.CORBA.TRANSACTION_ROLLEDBACK; 65: import org.omg.CORBA.TypeCode; 66: import org.omg.CORBA.UNKNOWN; 67: import org.omg.CORBA.portable.InputStream; 68: import org.omg.CORBA.portable.OutputStream; 69: 70: import java.io.ByteArrayInputStream; 71: import java.io.ByteArrayOutputStream; 72: import java.io.ObjectInputStream; 73: import java.io.ObjectOutputStream; 74: import java.io.Serializable; 75: import java.net.MalformedURLException; 76: import java.rmi.AccessException; 77: import java.rmi.MarshalException; 78: import java.rmi.NoSuchObjectException; 79: import java.rmi.Remote; 80: import java.rmi.RemoteException; 81: import java.rmi.ServerError; 82: import java.rmi.ServerException; 83: import java.rmi.UnexpectedException; 84: import java.rmi.server.RMIClassLoader; 85: import java.util.Hashtable; 86: 87: import javax.rmi.CORBA.Stub; 88: import javax.rmi.CORBA.Tie; 89: import javax.rmi.CORBA.Util; 90: import javax.rmi.CORBA.UtilDelegate; 91: import javax.rmi.CORBA.ValueHandler; 92: import javax.transaction.InvalidTransactionException; 93: import javax.transaction.TransactionRequiredException; 94: import javax.transaction.TransactionRolledbackException; 95: 96: /** 97: * The implementation of UtilDelegate. 98: * 99: * @author Wu Gansha (gansha.wu@intel.com) (stub) 100: * @author Audrius Meskauskas (AudriusA@Bioinformatics.org) (implementation) 101: */ 102: public class UtilDelegateImpl 103: extends RmiUtilities 104: implements UtilDelegate 105: { 106: /** 107: * The instance of the value handler, requested once. 108: */ 109: static ValueHandler m_ValueHandler; 110: 111: /** 112: * The global map of all ties to they records. 113: */ 114: static Hashtable m_Ties = new Hashtable(); 115: 116: /** 117: * The global map of all targets to they records. 118: */ 119: static Hashtable m_Targets = new Hashtable(); 120: 121: /** 122: * The standard package for that the exception names are omitted. 123: */ 124: static final String m_StandardPackage = "org.omg.CORBA."; 125: 126: /** 127: * Make a deep copy of the object. 128: */ 129: public Object copyObject(Object obj, ORB orb) 130: throws RemoteException 131: { 132: // Strings are immutable, can be shared. 133: if (obj instanceof String) 134: return obj; 135: else if (obj == null) 136: return null; 137: else if (obj instanceof String[] || obj instanceof String[][] 138: || obj instanceof String[][][]) 139: { 140: // String arrays can be just cloned. 141: return ((Object[]) obj).clone(); 142: } 143: else if (obj instanceof Serializable) 144: { 145: try 146: { 147: ByteArrayOutputStream a = new ByteArrayOutputStream(); 148: ObjectOutputStream ou = new ObjectOutputStream(a); 149: ou.writeObject(obj); 150: ou.close(); 151: ObjectInputStream input = new ObjectInputStream( 152: new ByteArrayInputStream(a.toByteArray())); 153: return input.readObject(); 154: } 155: catch (Exception ex) 156: { 157: RemoteException rex = new RemoteException("Cannot copy " + obj); 158: throw rex; 159: } 160: } 161: else 162: return obj; 163: } 164: 165: /** 166: * Make a deep copy of the object array. 167: */ 168: public Object[] copyObjects(Object[] obj, ORB orb) 169: throws RemoteException 170: { 171: return (Object[]) copyObject(obj, orb); 172: } 173: 174: public ValueHandler createValueHandler() 175: { 176: if (m_ValueHandler == null) 177: m_ValueHandler = (ValueHandler) DelegateFactory.getInstance(DelegateFactory.VALUEHANDLER); 178: return m_ValueHandler; 179: } 180: 181: /** 182: * Returns the codebase of the given class. 183: */ 184: public String getCodebase(Class clz) 185: { 186: return RMIClassLoader.getClassAnnotation(clz); 187: } 188: 189: /** 190: * Get the Tie that handles invocations on the given target. If the target/Tie 191: * pair has not been previously registered using {@link #registerTarget}, 192: * this method tries to locate a tie class by the name pattern. If this 193: * succeeds, the tie-target pair is also registered. 194: * 195: * @return the Tie. 196: */ 197: public Tie getTie(Remote target) 198: { 199: synchronized (m_Targets) 200: { 201: Tie tie; 202: TieTargetRecord r = ((TieTargetRecord) m_Targets.get(target)); 203: if (r == null) 204: { 205: if (target instanceof Stub) 206: { 207: tie = StubDelegateImpl.getTieFromStub(target); 208: registerTarget(tie, target); 209: } 210: else 211: { 212: // Treat this as implementation. 213: String tieClassName = getTieClassName(target.getClass().getName()); 214: try 215: { 216: Class tieClass = Util.loadClass(tieClassName, null, 217: target.getClass().getClassLoader()); 218: tie = (Tie) tieClass.newInstance(); 219: } 220: catch (Exception e) 221: { 222: MARSHAL m = new MARSHAL("Unable to instantiate " 223: + tieClassName); 224: m.minor = Minor.TargetConversion; 225: m.initCause(e); 226: throw m; 227: } 228: tie.setTarget(target); 229: registerTarget(tie, target); 230: } 231: } 232: else 233: tie = r.tie; 234: return tie; 235: } 236: } 237: 238: /** 239: * Get the Stub class name for the name, representing the given interface. 240: */ 241: private String getTieClassName(String interf) 242: { 243: String stubClassName; 244: int p = interf.lastIndexOf('.'); 245: 246: if (p < 0) 247: // The interface is defined in the default package. 248: stubClassName = "_" + interf + "_Tie"; 249: else 250: stubClassName = interf.substring(0, p + 1) + "_" 251: + interf.substring(p + 1) + "_Tie"; 252: return stubClassName; 253: } 254: 255: /** 256: * Register the Tie-target pair. As the Tie is a Servant, it can potentially 257: * be connected to several objects and hence may be registered with several 258: * targets. 259: */ 260: public void registerTarget(Tie tie, Remote target) 261: { 262: synchronized (m_Ties) 263: { 264: synchronized (m_Targets) 265: { 266: TieTargetRecord r = (TieTargetRecord) m_Ties.get(tie); 267: if (r == null) 268: { 269: // First registration for this Tie. 270: r = new TieTargetRecord(tie); 271: m_Ties.put(tie, r); 272: } 273: if (target != null) 274: { 275: r.add(target); 276: m_Targets.put(target, r); 277: } 278: } 279: } 280: } 281: 282: /** 283: * Deactivate the associated Tie, if it is found and is not connected to other 284: * registered targets. Independing from the POA policies, the transparent 285: * reactivation will not be possible. 286: */ 287: public void unexportObject(Remote target) 288: throws NoSuchObjectException 289: { 290: synchronized (m_Ties) 291: { 292: synchronized (m_Targets) 293: { 294: TieTargetRecord r = ((TieTargetRecord) m_Targets.get(target)); 295: if (r != null) 296: { 297: if (target instanceof org.omg.CORBA.Object) 298: r.tie.orb().disconnect((org.omg.CORBA.Object) target); 299: 300: if (r.unused()) 301: { 302: m_Targets.remove(target); 303: m_Ties.remove(r.tie); 304: r.tie.deactivate(); 305: 306: if (r.tie.orb() instanceof ORB_1_4) 307: { 308: // Standard case, when more deep cleanup is possible. 309: // Independing from the POA policies, the object will 310: // not be activable transparently. 311: ORB_1_4 orb = (ORB_1_4) r.tie.orb(); 312: 313: if (target instanceof org.omg.CORBA.Object) 314: { 315: AOM.Obj record = orb.rootPOA.findObject((org.omg.CORBA.Object) target); 316: 317: if (record != null && record.servant == r.tie 318: && record.poa instanceof gnuPOA) 319: { 320: ((gnuPOA) record.poa).aom.remove(record.key); 321: record.deactivated = true; 322: record.servant = null; 323: } 324: } 325: } 326: } 327: } 328: } 329: } 330: } 331: 332: /** 333: * Checks if the given stub is local. 334: * 335: * @param stub a stub to check. 336: * @return true if the stub is local, false otherwise. 337: */ 338: public boolean isLocal(Stub stub) 339: throws RemoteException 340: { 341: try 342: { 343: return stub._is_local(); 344: } 345: catch (SystemException e) 346: { 347: RemoteException rex = new RemoteException(); 348: rex.initCause(e); 349: throw rex; 350: } 351: } 352: 353: /** 354: * Load the class. The method uses class loaders from the call stact first. If 355: * this fails, the further behaviour depends on the System Property 356: * "java.rmi.server.useCodebaseOnly" with default value "false". 357: * 358: * <ul> 359: * <li>Try the current thread context class loader first.</li> 360: * <li>If remoteCodebase is non-null and useCodebaseOnly is "false" then call 361: * java.rmi.server.RMIClassLoader.loadClass (remoteCodebase, className)</li> 362: * <li> If remoteCodebase is null or useCodebaseOnly is true then call 363: * java.rmi.server.RMIClassLoader.loadClass(className)</li> 364: * <li>If a class is still not successfully loaded and the loader != null 365: * then try Class.forName(className, false, loader). </li> 366: * </ul> 367: * 368: * @param className the name of the class. 369: * @param remoteCodebase the codebase. 370: * @param loader the class loader. 371: * @return the loaded class. 372: * 373: * @throws ClassNotFoundException of the class cannot be loaded. 374: */ 375: public Class loadClass(String className, String remoteCodebase, 376: ClassLoader loader) 377: throws ClassNotFoundException 378: { 379: if (loader == null) 380: loader = VMStackWalker.firstNonNullClassLoader(); 381: 382: String p_useCodebaseOnly = System.getProperty("java.rmi.server.useCodebaseOnly"); 383: 384: boolean useCodebaseOnly = p_useCodebaseOnly != null 385: && p_useCodebaseOnly.trim().equalsIgnoreCase("true"); 386: 387: if (useCodebaseOnly) 388: remoteCodebase = null; 389: 390: try 391: { 392: return RMIClassLoader.loadClass(remoteCodebase, className, loader); 393: } 394: catch (MalformedURLException x) 395: { 396: throw new ClassNotFoundException(className, x); 397: } 398: } 399: 400: /** 401: * Converts CORBA {@link SystemException} into RMI {@link RemoteException}. 402: * The exception is converted as defined in the following table: 403: * <p> 404: * <table border = "1"> 405: * <tr> 406: * <th>CORBA Exception</th> 407: * <th>RMI Exception</th> 408: * </tr> 409: * <tr> 410: * <td>{@link COMM_FAILURE}</td> 411: * <td>{@link MarshalException}</td> 412: * </tr> 413: * <tr> 414: * <td>{@link INV_OBJREF}</td> 415: * <td>{@link NoSuchObjectException}</td> 416: * </tr> 417: * <tr> 418: * <td>{@link NO_PERMISSION}</td> 419: * <td>{@link AccessException}</td> 420: * </tr> 421: * <tr> 422: * <td>{@link MARSHAL}</td> 423: * <td>{@link MarshalException}</td> 424: * </tr> 425: * <tr> 426: * <td>{@link BAD_PARAM} (all other cases)</td> 427: * <td>{@link MarshalException}</td> 428: * </tr> 429: * <tr> 430: * <td>{@link OBJECT_NOT_EXIST}</td> 431: * <td>{@link NoSuchObjectException}</td> 432: * </tr> 433: * <tr> 434: * <td>{@link TRANSACTION_REQUIRED}</td> 435: * <td>{@link TransactionRequiredException}</td> 436: * </tr> 437: * <tr> 438: * <td>{@link TRANSACTION_ROLLEDBACK}</td> 439: * <td>{@link TransactionRolledbackException}</td> 440: * </tr> 441: * <tr> 442: * <td>{@link INVALID_TRANSACTION}</td> 443: * <td>{@link InvalidTransactionException}</td> 444: * </tr> 445: * <tr> 446: * <td bgcolor="lightgray">Any other {@link SystemException}</td> 447: * <td bgcolor="lightgray">{@link RemoteException}</td> 448: * </tr> 449: * </table> 450: * </p> 451: * <p> 452: * The exception detailed message always consists of 453: * <ol> 454: * <li>the string "CORBA "</li> 455: * <li>the CORBA name of the system exception</li> 456: * <li>single space</li> 457: * <li>the hexadecimal value of the system exception's minor code, preceeded 458: * by 0x (higher bits contain {@link OMGVMCID}).</li> 459: * <li>single space</li> 460: * <li>the {@link CompletionStatus} of the exception: "Yes", "No" or "Maybe".</li> 461: * </ol> 462: * <p> 463: * For instance, if the Internet connection was refused: 464: * </p> 465: * <p> 466: * <pre> 467: * <code>CORBA COMM_FAILURE 0x535500C9 No</code> 468: * </p> 469: * <p> 470: * The original CORBA exception is set as the cause of the RemoteException 471: * being created. 472: * </p> 473: */ 474: public RemoteException mapSystemException(SystemException ex) 475: { 476: RemoteException rex; 477: 478: String status; 479: 480: switch (ex.completed.value()) 481: { 482: case CompletionStatus._COMPLETED_MAYBE: 483: status = "Maybe"; 484: break; 485: 486: case CompletionStatus._COMPLETED_NO: 487: status = "No"; 488: break; 489: 490: case CompletionStatus._COMPLETED_YES: 491: status = "Yes"; 492: break; 493: 494: default: 495: status = "Unexpected completion status " + ex.completed.value(); 496: } 497: 498: String name = ex.getClass().getName(); 499: 500: if (name.startsWith(m_StandardPackage)) 501: name = name.substring(m_StandardPackage.length()); 502: 503: String message = "CORBA " + name + " 0x" + Integer.toHexString(ex.minor) 504: + " " + status; 505: 506: if (ex instanceof COMM_FAILURE) 507: rex = new MarshalException(message, ex); 508: else if (ex instanceof INV_OBJREF) 509: { 510: rex = new NoSuchObjectException(message); 511: rex.detail = ex; 512: } 513: else if (ex instanceof NO_PERMISSION) 514: rex = new AccessException(message, ex); 515: else if (ex instanceof MARSHAL) 516: rex = new MarshalException(message, ex); 517: else if (ex instanceof BAD_PARAM) 518: rex = new MarshalException(message, ex); 519: else if (ex instanceof OBJECT_NOT_EXIST) 520: { 521: rex = new NoSuchObjectException(message); 522: rex.detail = ex; 523: } 524: else if (ex instanceof TRANSACTION_REQUIRED) 525: { 526: rex = new TransactionRequiredException(message); 527: rex.detail = ex; 528: } 529: else if (ex instanceof TRANSACTION_ROLLEDBACK) 530: { 531: rex = new TransactionRolledbackException(message); 532: rex.detail = ex; 533: } 534: else if (ex instanceof INVALID_TRANSACTION) 535: { 536: rex = new InvalidTransactionException(message); 537: rex.detail = ex; 538: } 539: else if (ex instanceof UNKNOWN) 540: rex = wrapException(ex.getCause()); 541: else 542: rex = new RemoteException(message, ex); 543: 544: return rex; 545: } 546: 547: /** 548: * Converts the exception that was thrown by the implementation method on a 549: * server side into RemoteException that can be transferred and re-thrown on a 550: * client side. The method converts exceptions as defined in the following 551: * table: <table border = "1"> 552: * <tr> 553: * <th>Exception to map (or subclass)</th> 554: * <th>Maps into</th> 555: * </tr> 556: * <tr> 557: * <td>{@link Error}</td> 558: * <td>{@link ServerError}</td> 559: * </tr> 560: * <tr> 561: * <td>{@link RemoteException}</td> 562: * <td>{@link ServerException}</td> 563: * </tr> 564: * <tr> 565: * <td>{@link SystemException}</td> 566: * <td>wrapException({@link #mapSystemException})</td> 567: * </tr> 568: * <tr> 569: * <td>{@link RuntimeException}</td> 570: * <td><b>rethrows</b></td> 571: * </tr> 572: * <tr> 573: * <td>Any other exception</td> 574: * <td>{@link UnexpectedException}</td> 575: * </tr> 576: * </table> 577: * 578: * @param ex an exception that was thrown on a server side implementation. 579: * 580: * @return the corresponding RemoteException unless it is a RuntimeException. 581: * 582: * @throws RuntimeException the passed exception if it is an instance of 583: * RuntimeException. 584: * 585: * @specnote It is the same behavior, as in Suns implementations 1.4.0-1.5.0. 586: */ 587: public RemoteException wrapException(Throwable ex) 588: throws RuntimeException 589: { 590: if (ex instanceof RuntimeException) 591: throw (RuntimeException) ex; 592: else if (ex instanceof Error) 593: return new ServerError(ex.getMessage(), (Error) ex); 594: else if (ex instanceof RemoteException) 595: return new ServerException(ex.getMessage(), (Exception) ex); 596: else if (ex instanceof SystemException) 597: return wrapException(mapSystemException((SystemException) ex)); 598: else 599: return new UnexpectedException("Unexpected", (Exception) ex); 600: } 601: 602: /** 603: * Write abstract interface to the CORBA output stream. The write format is 604: * matching CORBA abstract interface. Remotes and CORBA objects are written as 605: * objects, other classes are supposed to be value types and are written as 606: * such. {@link Remote}s are processed as defined in 607: * {@link #writeRemoteObject}. The written data contains discriminator, 608: * defining, that was written. Another method that writes the same content is 609: * {@link org.omg.CORBA_2_3.portable.OutputStream#write_abstract_interface(java.lang.Object)}. 610: * 611: * @param output a stream to write to, must be 612: * {@link org.omg.CORBA_2_3.portable.OutputStream}. 613: * 614: * @param object an object to write, must be CORBA object, Remote 615: */ 616: public void writeAbstractObject(OutputStream output, Object object) 617: { 618: ((org.omg.CORBA_2_3.portable.OutputStream) output).write_abstract_interface(object); 619: } 620: 621: /** 622: * Write the passed java object to the output stream in the form of the CORBA 623: * {@link Any}. This includes creating an writing the object {@link TypeCode} 624: * first. Such Any can be later read by a non-RMI-IIOP CORBA implementation 625: * and manipulated, for instance, by means, provided in 626: * {@link org.omg.DynamicAny.DynAny}. Depending from the passed value, this 627: * method writes CORBA object, value type or value box. For value types Null 628: * is written with the abstract interface, its typecode having repository id 629: * "IDL:omg.org/CORBA/AbstractBase:1.0" and the empty string name. 630: * 631: * @param output the object to write. 632: * @param object the java object that must be written in the form of the CORBA 633: * {@link Any}. 634: */ 635: public void writeAny(OutputStream output, Object object) 636: { 637: Any any = output.orb().create_any(); 638: if (object == null) 639: { 640: GeneralTypeCode t = new GeneralTypeCode(TCKind.tk_abstract_interface); 641: t.setId("IDL:omg.org/CORBA/AbstractBase:1.0"); 642: t.setName(""); 643: any.type(t); 644: output.write_any(any); 645: return; 646: } 647: else if (object instanceof org.omg.CORBA.Object 648: && !(object instanceof Remote)) 649: { 650: // Write as value type. 651: boolean inserted = ObjectCreator.insertWithHelper(any, object); 652: if (inserted) 653: { 654: output.write_any(any); 655: return; 656: } 657: } 658: 659: if (object instanceof org.omg.CORBA.Object) 660: writeAnyAsRemote(output, object); 661: else if (object instanceof Serializable) 662: { 663: any.insert_Value((Serializable) object); 664: output.write_any(any); 665: } 666: else 667: { 668: MARSHAL m = new MARSHAL(object.getClass().getName() 669: + " must be CORBA Object, Remote or Serializable"); 670: m.minor = Minor.NonSerializable; 671: throw m; 672: } 673: } 674: 675: /** 676: * Write Any as for remote object. 677: */ 678: void writeAnyAsRemote(OutputStream output, Object object) 679: { 680: GeneralTypeCode t = new GeneralTypeCode(TCKind.tk_objref); 681: t.setId(m_ValueHandler.getRMIRepositoryID(object.getClass())); 682: t.setName(object.getClass().getName()); 683: 684: // Writing Any (typecode, followed by value). 685: output.write_TypeCode(t); 686: writeRemoteObject(output, object); 687: } 688: 689: /** 690: * Get the class name excluding the package name. 691: */ 692: String getName(String n) 693: { 694: int p = n.lastIndexOf('.'); 695: if (p < 0) 696: return n; 697: else 698: return n.substring(p + 1); 699: } 700: 701: /** 702: * Read Any from the input stream. 703: */ 704: public Object readAny(InputStream input) 705: { 706: return input.read_any(); 707: } 708: 709: /** 710: * Write the passed parameter to the output stream as CORBA object. If the 711: * parameter is an instance of Remote and not an instance of Stub, the method 712: * instantiates a suitable Tie, connects the parameter to this Tie and then 713: * connects that Tie to the ORB that is requested from the output stream. Then 714: * the object reference is written to the stream, making remote invocations 715: * possible. This method is used in write_value(..) method group in 716: * {@link org.omg.CORBA_2_3.portable.OutputStream} and also may be called 717: * directly from generated Stubs and Ties. 718: * 719: * @param output a stream to write to, must be 720: * org.omg.CORBA_2_3.portable.OutputStream 721: * @param object an object to write. 722: */ 723: public void writeRemoteObject(OutputStream an_output, Object object) 724: { 725: org.omg.CORBA_2_3.portable.OutputStream output = (org.omg.CORBA_2_3.portable.OutputStream) an_output; 726: if (object == null) 727: an_output.write_Object(null); 728: else if (isTieRequired(object)) 729: { 730: // Find the interface that is implemented by the object and extends 731: // Remote. 732: Class fc = getExportedInterface(object); 733: exportTie(output, object, fc); 734: } 735: else if (object instanceof org.omg.CORBA.Object) 736: { 737: ensureOrbRunning(output); 738: an_output.write_Object((org.omg.CORBA.Object) object); 739: } 740: else if (object != null && object instanceof Serializable) 741: writeFields(an_output, (Serializable) object); 742: } 743: 744: }