1 /* ObjectCreator.java -- 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; 40 41 import gnu.CORBA.CDR.UnknownExceptionCtxHandler; 42 import gnu.CORBA.CDR.BufferredCdrInput; 43 import gnu.CORBA.CDR.BufferedCdrOutput; 44 import gnu.CORBA.CDR.AbstractCdrInput; 45 import gnu.CORBA.GIOP.ServiceContext; 46 import gnu.CORBA.typecodes.RecordTypeCode; 47 import gnu.classpath.VMStackWalker; 48 49 import org.omg.CORBA.Any; 50 import org.omg.CORBA.CompletionStatus; 51 import org.omg.CORBA.CompletionStatusHelper; 52 import org.omg.CORBA.MARSHAL; 53 import org.omg.CORBA.SystemException; 54 import org.omg.CORBA.TCKind; 55 import org.omg.CORBA.UNKNOWN; 56 import org.omg.CORBA.UserException; 57 import org.omg.CORBA.portable.IDLEntity; 58 import org.omg.CORBA.portable.InputStream; 59 import org.omg.CORBA.portable.OutputStream; 60 import org.omg.CORBA.portable.ValueBase; 61 62 import java.lang.reflect.Method; 63 import java.util.Map; 64 import java.util.WeakHashMap; 65 66 import javax.rmi.CORBA.Util; 67 68 /** 69 * Creates java objects from the agreed IDL names for the simple case when the 70 * CORBA object is directly mapped into the locally defined java class. 71 * 72 * @author Audrius Meskauskas (AudriusA@Bioinformatics.org) 73 */ 74 public class ObjectCreator 75 { 76 /** 77 * The standard OMG prefix. 78 */ 79 public static final String OMG_PREFIX = "omg.org/"; 80 81 /** 82 * The standard java prefix. 83 */ 84 public static final String JAVA_PREFIX = "org.omg."; 85 86 /** 87 * The prefix for classes that are placed instide the gnu.CORBA namespace. 88 */ 89 public static final String CLASSPATH_PREFIX = "gnu.CORBA."; 90 91 /** 92 * Maps classes to they IDL or RMI names. Computing RMI name is an expensive 93 * operations, so frequently used RMI keys are reused. The map must be weak to 94 * ensure that the class can be unloaded, when applicable. 95 */ 96 public static Map m_names = new WeakHashMap(); 97 98 /** 99 * Maps IDL strings into known classes. The map must be weak to ensure that 100 * the class can be unloaded, when applicable. 101 */ 102 public static Map m_classes = new WeakHashMap(); 103 104 /** 105 * Maps IDL types to they helpers. 106 */ 107 public static Map m_helpers = new WeakHashMap(); 108 109 /** 110 * Try to instantiate an object with the given IDL name. The object must be 111 * mapped to the local java class. The omg.org domain must be mapped into the 112 * object in either org/omg or gnu/CORBA namespace. 113 * 114 * @param idl name 115 * @return instantiated object instance or null if no such available. 116 */ createObject(String idl, String suffix)117 public static java.lang.Object createObject(String idl, String suffix) 118 { 119 synchronized (m_classes) 120 { 121 Class known = (Class) (suffix == null ? m_classes.get(idl) 122 : m_classes.get(idl + 0xff + suffix)); 123 Object object; 124 125 if (known != null) 126 { 127 try 128 { 129 return known.newInstance(); 130 } 131 catch (Exception ex) 132 { 133 RuntimeException rex = new RuntimeException(idl + " suffix " 134 + suffix, ex); 135 throw rex; 136 } 137 } 138 else 139 { 140 if (suffix == null) 141 suffix = ""; 142 try 143 { 144 known = forName(toClassName(JAVA_PREFIX, idl) + suffix); 145 object = known.newInstance(); 146 } 147 catch (Exception ex) 148 { 149 try 150 { 151 known = forName(toClassName(CLASSPATH_PREFIX, idl) 152 + suffix); 153 object = known.newInstance(); 154 } 155 catch (Exception exex) 156 { 157 return null; 158 } 159 } 160 m_classes.put(idl + 0xff + suffix, known); 161 return object; 162 } 163 } 164 } 165 166 /** 167 * Read the system exception from the given stream. 168 * 169 * @param input the CDR stream to read from. 170 * @param contexts the service contexts in request/reply header/ 171 * 172 * @return the exception that has been stored in the stream (IDL name, minor 173 * code and completion status). 174 */ readSystemException(InputStream input, ServiceContext[] contexts)175 public static SystemException readSystemException(InputStream input, 176 ServiceContext[] contexts) 177 { 178 SystemException exception; 179 180 String idl = input.read_string(); 181 int minor = input.read_ulong(); 182 CompletionStatus completed = CompletionStatusHelper.read(input); 183 184 try 185 { 186 exception = (SystemException) createObject(idl, null); 187 exception.minor = minor; 188 exception.completed = completed; 189 } 190 catch (Exception ex) 191 { 192 UNKNOWN u = new UNKNOWN("Unsupported system exception " + idl, minor, 193 completed); 194 u.initCause(ex); 195 throw u; 196 } 197 198 try 199 { 200 // If UnknownExceptionInfo is present in the contexts, read it and 201 // set as a cause of this exception. 202 ServiceContext uEx = ServiceContext.find( 203 ServiceContext.UnknownExceptionInfo, contexts); 204 205 if (uEx != null) 206 { 207 BufferredCdrInput in = new BufferredCdrInput(uEx.context_data); 208 in.setOrb(in.orb()); 209 if (input instanceof AbstractCdrInput) 210 { 211 ((AbstractCdrInput) input).cloneSettings(in); 212 } 213 214 Throwable t = UnknownExceptionCtxHandler.read(in, contexts); 215 exception.initCause(t); 216 } 217 } 218 catch (Exception ex) 219 { 220 // Unsupported context format. Do not terminate as the user program may 221 // not need it. 222 } 223 224 return exception; 225 } 226 227 /** 228 * Reads the user exception, having the given Id, from the input stream. The 229 * id is expected to be in the form like 230 * 'IDL:test/org/omg/CORBA/ORB/communication/ourUserException:1.0' 231 * 232 * @param idl the exception idl name. 233 * @param input the stream to read from. 234 * 235 * @return the loaded exception. 236 * @return null if the helper class cannot be found. 237 */ readUserException(String idl, InputStream input)238 public static UserException readUserException(String idl, InputStream input) 239 { 240 try 241 { 242 Class helperClass = findHelper(idl); 243 244 Method read = helperClass.getMethod("read", 245 new Class[] { org.omg.CORBA.portable.InputStream.class }); 246 247 return (UserException) read.invoke(null, new Object[] { input }); 248 } 249 catch (MARSHAL mex) 250 { 251 // This one is ok to throw 252 throw mex; 253 } 254 catch (Exception ex) 255 { 256 ex.printStackTrace(); 257 return null; 258 } 259 } 260 261 /** 262 * Gets the helper class name from the string like 263 * 'IDL:test/org/omg/CORBA/ORB/communication/ourUserException:1.0' 264 * 265 * @param IDL the idl name. 266 */ toHelperName(String IDL)267 public static String toHelperName(String IDL) 268 { 269 String s = IDL; 270 int a = s.indexOf(':') + 1; 271 int b = s.lastIndexOf(':'); 272 273 s = IDL.substring(a, b); 274 275 if (s.startsWith(OMG_PREFIX)) 276 s = JAVA_PREFIX + s.substring(OMG_PREFIX.length()); 277 278 return s.replace('/', '.') + "Helper"; 279 } 280 281 /** 282 * Writes the system exception data to CDR output stream. 283 * 284 * @param output a stream to write data to. 285 * @param ex an exception to write. 286 */ writeSystemException(OutputStream output, SystemException ex)287 public static void writeSystemException(OutputStream output, 288 SystemException ex) 289 { 290 String exIDL = getRepositoryId(ex.getClass()); 291 output.write_string(exIDL); 292 output.write_ulong(ex.minor); 293 CompletionStatusHelper.write(output, ex.completed); 294 } 295 296 /** 297 * Converts the given IDL name to class name. 298 * 299 * @param IDL the idl name. 300 * 301 */ toClassName(String prefix, String IDL)302 protected static String toClassName(String prefix, String IDL) 303 { 304 String s = IDL; 305 int a = s.indexOf(':') + 1; 306 int b = s.lastIndexOf(':'); 307 308 s = IDL.substring(a, b); 309 310 if (s.startsWith(OMG_PREFIX)) 311 s = prefix + s.substring(OMG_PREFIX.length()); 312 313 return s.replace('/', '.'); 314 } 315 316 /** 317 * Converts the given IDL name to class name and tries to load the matching 318 * class. The OMG prefix (omg.org) is replaced by the java prefix org.omg. No 319 * other prefixes are added. 320 * 321 * @param IDL the idl name. 322 * 323 * @return the matching class or null if no such is available. 324 */ Idl2class(String IDL)325 public static Class Idl2class(String IDL) 326 { 327 synchronized (m_classes) 328 { 329 Class c = (Class) m_classes.get(IDL); 330 331 if (c != null) 332 return c; 333 else 334 { 335 String s = IDL; 336 int a = s.indexOf(':') + 1; 337 int b = s.lastIndexOf(':'); 338 339 s = IDL.substring(a, b); 340 341 if (s.startsWith(OMG_PREFIX)) 342 s = JAVA_PREFIX + s.substring(OMG_PREFIX.length()); 343 344 String cn = s.replace('/', '.'); 345 346 try 347 { 348 c = forName(cn); 349 m_classes.put(IDL, c); 350 return c; 351 } 352 catch (ClassNotFoundException ex) 353 { 354 return null; 355 } 356 } 357 } 358 } 359 360 /** 361 * Converts the given IDL name to class name, tries to load the matching class 362 * and create an object instance with parameterless constructor. The OMG 363 * prefix (omg.org) is replaced by the java prefix org.omg. No other prefixes 364 * are added. 365 * 366 * @param IDL the idl name. 367 * 368 * @return instantiated object instance or null if such attempt was not 369 * successful. 370 */ Idl2Object(String IDL)371 public static java.lang.Object Idl2Object(String IDL) 372 { 373 Class cx = Idl2class(IDL); 374 375 try 376 { 377 if (cx != null) 378 return cx.newInstance(); 379 else 380 return null; 381 } 382 catch (Exception ex) 383 { 384 return null; 385 } 386 } 387 388 /** 389 * Convert the class name to IDL or RMI name (repository id). If the class 390 * inherits from IDLEntity, ValueBase or SystemException, returns repository 391 * Id in the IDL:(..) form. If it does not, returns repository Id in the 392 * RMI:(..) form. 393 * 394 * @param cx the class for that the name must be computed. 395 * 396 * @return the idl or rmi name. 397 */ getRepositoryId(Class cx)398 public static synchronized String getRepositoryId(Class cx) 399 { 400 String name = (String) m_names.get(cx); 401 if (name != null) 402 return name; 403 404 String cn = cx.getName(); 405 if (!(IDLEntity.class.isAssignableFrom(cx) 406 || ValueBase.class.isAssignableFrom(cx) || SystemException.class.isAssignableFrom(cx))) 407 { 408 // Not an IDL entity. 409 name = Util.createValueHandler().getRMIRepositoryID(cx); 410 } 411 else 412 { 413 if (cn.startsWith(JAVA_PREFIX)) 414 cn = OMG_PREFIX 415 + cn.substring(JAVA_PREFIX.length()).replace('.', '/'); 416 else if (cn.startsWith(CLASSPATH_PREFIX)) 417 cn = OMG_PREFIX 418 + cn.substring(CLASSPATH_PREFIX.length()).replace('.', '/'); 419 420 name = "IDL:" + cn + ":1.0"; 421 } 422 m_names.put(cx, name); 423 return name; 424 } 425 426 /** 427 * Insert the passed parameter into the given Any, assuming that the helper 428 * class is available. The helper class must have the "Helper" suffix and be 429 * in the same package as the class of the object being inserted. 430 * 431 * @param into the target to insert. 432 * 433 * @param object the object to insert. It can be any object as far as the 434 * corresponding helper is provided. 435 * 436 * @return true on success, false otherwise. 437 */ insertWithHelper(Any into, Object object)438 public static boolean insertWithHelper(Any into, Object object) 439 { 440 try 441 { 442 String helperClassName = object.getClass().getName() + "Helper"; 443 Class helperClass = forName(helperClassName); 444 445 Method insert = helperClass.getMethod("insert", new Class[] { 446 Any.class, object.getClass() }); 447 448 insert.invoke(null, new Object[] { into, object }); 449 450 return true; 451 } 452 catch (Exception exc) 453 { 454 // Failed due some reason. 455 return false; 456 } 457 } 458 459 /** 460 * Insert the system exception into the given Any. 461 */ insertSysException(Any into, SystemException exception)462 public static boolean insertSysException(Any into, SystemException exception) 463 { 464 try 465 { 466 BufferedCdrOutput output = new BufferedCdrOutput(); 467 468 String m_exception_id = getRepositoryId(exception.getClass()); 469 output.write_string(m_exception_id); 470 output.write_ulong(exception.minor); 471 CompletionStatusHelper.write(output, exception.completed); 472 473 String name = getDefaultName(m_exception_id); 474 475 GeneralHolder h = new GeneralHolder(output); 476 477 into.insert_Streamable(h); 478 479 RecordTypeCode r = new RecordTypeCode(TCKind.tk_except); 480 r.setId(m_exception_id); 481 r.setName(name); 482 into.type(r); 483 484 return true; 485 } 486 catch (Exception ex) 487 { 488 ex.printStackTrace(); 489 return false; 490 } 491 } 492 493 /** 494 * Get the type name from the IDL string. 495 */ getDefaultName(String idl)496 public static String getDefaultName(String idl) 497 { 498 int f1 = idl.lastIndexOf("/"); 499 int p1 = (f1 < 0) ? 0 : f1; 500 int p2 = idl.indexOf(":", p1); 501 if (p2 < 0) 502 p2 = idl.length(); 503 504 String name = idl.substring(f1 + 1, p2); 505 return name; 506 } 507 508 /** 509 * Insert this exception into the given Any. On failure, insert the UNKNOWN 510 * exception. 511 */ insertException(Any into, Throwable exception)512 public static void insertException(Any into, Throwable exception) 513 { 514 boolean ok = false; 515 if (exception instanceof SystemException) 516 ok = insertSysException(into, (SystemException) exception); 517 else if (exception instanceof UserException) 518 ok = insertWithHelper(into, exception); 519 520 if (!ok) 521 ok = insertSysException(into, new UNKNOWN()); 522 if (!ok) 523 throw new InternalError("Exception wrapping broken"); 524 } 525 526 /** 527 * Find helper for the class with the given name. 528 */ findHelper(String idl)529 public static Class findHelper(String idl) 530 { 531 synchronized (m_helpers) 532 { 533 Class c = (Class) m_helpers.get(idl); 534 if (c != null) 535 return c; 536 try 537 { 538 String helper = toHelperName(idl); 539 c = forName(helper); 540 541 m_helpers.put(idl, c); 542 return c; 543 } 544 catch (Exception ex) 545 { 546 return null; 547 } 548 } 549 } 550 551 /** 552 * Load the class with the given name. This method tries to use the context 553 * class loader first. If this fails, it searches for the suitable class 554 * loader in the caller stack trace. This method is a central point where all 555 * requests to find a class by name are delegated. 556 */ forName(String className)557 public static Class forName(String className) throws ClassNotFoundException 558 { 559 try 560 { 561 return Class.forName(className, true, 562 Thread.currentThread().getContextClassLoader()); 563 } 564 catch (ClassNotFoundException nex) 565 { 566 /** 567 * Returns the first user defined class loader on the call stack, or 568 * null when no non-null class loader was found. 569 */ 570 Class[] ctx = VMStackWalker.getClassContext(); 571 for (int i = 0; i < ctx.length; i++) 572 { 573 // Since we live in a class loaded by the bootstrap 574 // class loader, getClassLoader is safe to call without 575 // needing to be wrapped in a privileged action. 576 ClassLoader cl = ctx[i].getClassLoader(); 577 try 578 { 579 if (cl != null) 580 return Class.forName(className, true, cl); 581 } 582 catch (ClassNotFoundException nex2) 583 { 584 // Try next. 585 } 586 } 587 } 588 throw new ClassNotFoundException(className); 589 } 590 } 591