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