KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectweb > fractal > rmi > stub > RmiStubFactory


1 /***
2  * Fractal RMI: a binder for remote method calls between Fractal components.
3  * Copyright (C) 2003 France Telecom R&D
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18  *
19  * Contact: Eric.Bruneton@rd.francetelecom.com
20  *
21  * Author: Eric Bruneton
22  *
23  * with some comments copied from Jonathan:
24  * org.objectweb.jonathan.apis.stub_factories.StubFactory (author: B. Dumant)
25  */

26
27 package org.objectweb.fractal.rmi.stub;
28
29 import org.objectweb.fractal.api.Interface;
30 import org.objectweb.fractal.api.control.BindingController;
31
32 import org.objectweb.asm.ClassVisitor;
33 import org.objectweb.asm.ClassWriter;
34 import org.objectweb.asm.CodeVisitor;
35 import org.objectweb.asm.Constants;
36 import org.objectweb.asm.Label;
37 import org.objectweb.asm.Type;
38
39 import org.objectweb.jonathan.apis.binding.ExportException;
40 import org.objectweb.jonathan.apis.binding.Identifier;
41 import org.objectweb.jonathan.apis.kernel.Context;
42 import org.objectweb.jonathan.apis.kernel.JonathanException;
43 import org.objectweb.jonathan.apis.presentation.MarshallerFactory;
44 import org.objectweb.jonathan.apis.protocols.RequestSession;
45 import org.objectweb.jonathan.apis.protocols.SessionIdentifier;
46 import org.objectweb.jonathan.apis.stub_factories.StubFactory;
47
48 import org.objectweb.util.monolog.api.BasicLevel;
49 import org.objectweb.util.monolog.api.Logger;
50 import org.objectweb.util.monolog.api.LoggerFactory;
51
52 import java.lang.reflect.Method JavaDoc;
53 import java.util.HashMap JavaDoc;
54
55 /**
56  * A stub and skeleton factory based on <a
57  * HREF="http://www.objectweb.org/asm">ASM</a>. This factory generates the stub
58  * and skeleton classes dynamically, when they are needed. It is therefore not
59  * necessary for the user to statically generate these classes with a tool such
60  * as <tt>rmic</tt> or <tt>idlc</tt>.
61  * <br>
62  * The stubs and skeletons created by this factory marshall and unmarshall the
63  * method invocations by using the following format: a method invocation message
64  * contains a four bytes method index, which uniquely and unambiguously
65  * identifies a method among the methods provided by a Java interface, followed
66  * by the method's arguments, marshalled in the order of their declaration in
67  * the method's signature.
68  */

69
70 public class RmiStubFactory
71   implements Constants, StubFactory, SkeletonFactory, BindingController
72 {
73
74   /**
75    * A map associating {@link Loader loaders} to their parent class loader.
76    */

77
78   private final static HashMap JavaDoc LOADERS = new HashMap JavaDoc();
79
80   /**
81    * The marshaller factory to be used by the stubs created by this factory.
82    */

83
84   protected MarshallerFactory marshallerFactory;
85
86   /**
87    * The optional logger factory used to get a logger for this component.
88    */

89
90   protected LoggerFactory loggerFactory;
91
92   /**
93    * The logger used to log messages. May be <tt>null</tt>.
94    */

95
96   protected Logger logger;
97
98   /**
99    * Constructs a new {@link RmiStubFactory}.
100    */

101
102   public RmiStubFactory () {
103   }
104
105   // --------------------------------------------------------------------------
106
// Implementation of the BindingController interface
107
// --------------------------------------------------------------------------
108

109   public String JavaDoc[] listFc () {
110     return new String JavaDoc[] { "marshaller-factory", "logger-factory" };
111   }
112
113   public Object JavaDoc lookupFc (final String JavaDoc clientItfName) {
114     if (clientItfName.equals("marshaller-factory")) {
115       return marshallerFactory;
116     } else if (clientItfName.equals("logger-factory")) {
117       return loggerFactory;
118     }
119     return null;
120   }
121
122   public void bindFc (final String JavaDoc clientItfName, final Object JavaDoc serverItf) {
123     if (clientItfName.equals("marshaller-factory")) {
124       marshallerFactory = (MarshallerFactory)serverItf;
125     } else if (clientItfName.equals("logger-factory")) {
126       loggerFactory = (LoggerFactory)serverItf;
127       logger = loggerFactory.getLogger(getClass().getName());
128     }
129   }
130
131   public void unbindFc (final String JavaDoc clientItfName) {
132     if (clientItfName.equals("marshaller-factory")) {
133       marshallerFactory = null;
134     } else if (clientItfName.equals("logger-factory")) {
135       loggerFactory = null;
136       logger = null;
137     }
138   }
139
140   // --------------------------------------------------------------------------
141
// Implementation of the StubFactory interface
142
// --------------------------------------------------------------------------
143

144   /**
145    * Creates a new stub. A stub plays two roles:
146    * <ul>
147    * <li>It is the local representative of a (remote) object, and thus should
148    * bear a set of identifiers for that remote object;</li>
149    * <li>It is part of the binding between the client and the server, and is
150    * thus related to a given protocol. The session identifier provided as an
151    * argument is the manifestation of this relation. It can be used to
152    * obtain a session allowing the stub to send data to the remote object.</li>
153    * </ul>
154    *
155    * @param sessionId a session identifier, to be used to send marshalled
156    * data to the object represented by the stub;
157    * @param ids the set of identifiers of the stub;
158    * @param hints other data possibly used to create the stub. This method
159    * requires the fully qualified name of the Java interface to which the
160    * stub will gives access. This name must be associated to the
161    * "interface_type" key.
162    * @return an instance of a sub class of the {@link Stub} class.
163    * @throws JonathanException if something goes wrong.
164    */

165
166   public Object JavaDoc newStub (
167     final SessionIdentifier sessionId,
168     final Identifier[] ids,
169     final Context hints) throws JonathanException
170   {
171     try {
172       String JavaDoc implClassName = (String JavaDoc)hints.getValue("interface_type", (char)0);
173       Class JavaDoc implClass =
174         Thread.currentThread().getContextClassLoader().loadClass(implClassName);
175       Class JavaDoc stubClass =
176         getLoader(implClass).loadClass(getStubClassName(implClass.getName()));
177       if (ids.length != 1) {
178         throw new JonathanException();
179       }
180       Stub stub = (Stub)stubClass.newInstance();
181       stub.id = ids[0];
182       stub.sessionId = sessionId;
183       stub.marshallerFactory = marshallerFactory;
184       if (logger != null && logger.isLoggable(BasicLevel.INFO)) {
185         logger.log(BasicLevel.INFO, "Stub created for id " + ids[0]);
186       }
187       return stub;
188     } catch (Exception JavaDoc e) {
189       throw new JonathanException(e);
190     }
191   }
192
193   // --------------------------------------------------------------------------
194
// Implementation of the StubFactory interface
195
// --------------------------------------------------------------------------
196

197   public RequestSession newSkeleton (final Object JavaDoc target)
198     throws JonathanException
199   {
200     try {
201       if (!(target instanceof Interface)) {
202         throw new Exception JavaDoc("target object must implement Interface");
203       }
204       Class JavaDoc implClass = target.getClass();
205       Class JavaDoc[] itfs = implClass.getInterfaces();
206       if (itfs.length == 0) {
207         throw new Exception JavaDoc("target object must implement an interface");
208       }
209       Class JavaDoc skelClass =
210         getLoader(implClass).loadClass(getSkelClassName(itfs[0].getName()));
211       Skeleton skel = (Skeleton)skelClass.newInstance();
212       skel.target = target;
213       if (logger != null && logger.isLoggable(BasicLevel.INFO)) {
214         logger.log(BasicLevel.INFO, "Skeleton created for target " + target);
215       }
216       return skel;
217     } catch (Exception JavaDoc e) {
218       throw new ExportException(e);
219     }
220   }
221
222   // --------------------------------------------------------------------------
223
// Class loading
224
// --------------------------------------------------------------------------
225

226   /**
227    * Returns the {@link Loader} whose parent class loader is equal to the
228    * given one.
229    *
230    * @param context the parent class loader. More precisely a class, from which
231    * the parent class loader is computed (with the getClassLoader method).
232    * @return the {@link Loader} whose parent class loader is equal to the
233    * given one.
234    */

235
236   private Loader getLoader (final Class JavaDoc context) {
237     ClassLoader JavaDoc parent = context.getClassLoader();
238     Loader instance = (Loader)LOADERS.get(parent);
239     if (instance == null) {
240       instance = new Loader(parent);
241       LOADERS.put(parent, instance);
242     }
243     return instance;
244   }
245
246   /**
247    * A class loader that can generate stub and skeleton classes on the fly.
248    */

249
250   class Loader extends ClassLoader JavaDoc {
251
252     /**
253      * Constructs a {@link Loader} with the given parent class loader.
254      *
255      * @param parent the parent class loader.
256      */

257
258     public Loader (final ClassLoader JavaDoc parent) {
259       super(parent);
260     }
261
262     /**
263      * Finds the class whose name is given. If the given name is of the form
264      * <i>prefix</i><tt>_Stub</tt> then this method calls the {@link
265      * #generateStubClass generateStubClass} method with the <i>prefix</i> class
266      * as parameter. If the given name is of the form
267      * <i>prefix</i><tt>_Skel</tt> then this method calls the
268      * {@link #generateSkeletonClass generateSkeletonClass} method with the
269      * <i>prefix</i> class as parameter. In all other cases this method throws
270      * an exception.
271      *
272      * @param name the name of the class to be found.
273      * @return the specified class.
274      * @throws ClassNotFoundException if the class cannot be found.
275      */

276
277     protected Class JavaDoc findClass (final String JavaDoc name)
278       throws ClassNotFoundException JavaDoc
279     {
280       if (isStubClassName(name)) {
281         Class JavaDoc itf = loadClass(resolveStubClassName(name));
282         ClassWriter cw = new ClassWriter(true);
283         generateStubClass(cw, itf);
284         byte[] b = cw.toByteArray();
285         if (logger != null && logger.isLoggable(BasicLevel.INFO)) {
286           logger.log(BasicLevel.INFO, "Stub class generated: " + name);
287         }
288         /*
289         try {
290           java.io.OutputStream os;
291           os = new java.io.FileOutputStream(name + ".class");
292           os.write(b);
293           os.close();
294         } catch (Exception e) {
295         }*/

296         return defineClass(name, b, 0, b.length);
297       } else if (isSkelClassName(name)) {
298         Class JavaDoc itf = loadClass(resolveSkelClassName(name));
299         ClassWriter cw = new ClassWriter(true);
300         generateSkeletonClass(cw, itf);
301         byte[] b = cw.toByteArray();
302         if (logger != null && logger.isLoggable(BasicLevel.INFO)) {
303           logger.log(BasicLevel.INFO, "Skeleton class generated: " + name);
304         }
305         /*
306         try {
307           java.io.OutputStream os;
308           os = new java.io.FileOutputStream(name + ".class");
309           os.write(b);
310           os.close();
311         } catch (Exception e) {
312         }*/

313         return defineClass(name, b, 0, b.length);
314       } else {
315         throw new ClassNotFoundException JavaDoc(name);
316       }
317     }
318   }
319
320   // --------------------------------------------------------------------------
321
// Utility methods: class generation
322
// --------------------------------------------------------------------------
323

324   /**
325    * Generates a stub class for the given Java interface. This method generates
326    * a sub class of the {@link Stub} class that implements the given
327    * interface, by using the given class visitor.
328    *
329    * @param cv the class visitor to be used to generate the stub class.
330    * @param itf the Java interface that the stub class must implement.
331    */

332
333   protected void generateStubClass (final ClassVisitor cv, final Class JavaDoc itf) {
334     int access = ACC_PUBLIC;
335     String JavaDoc name = getStubClassName(itf.getName()).replace('.', '/');
336     String JavaDoc superClass = "org/objectweb/fractal/rmi/stub/Stub";
337     String JavaDoc[] itfs = new String JavaDoc[] { Type.getInternalName(itf) };
338     cv.visit(V1_1, access, name, superClass, itfs, null);
339     generateConstructor(cv, superClass);
340     Method JavaDoc[] meths = itf.getMethods();
341     sort(meths);
342     for (int i = 0; i < meths.length; ++i) {
343       Method JavaDoc meth = meths[i];
344       generateStubMethod(cv, name, meth, i);
345     }
346   }
347
348   /**
349    * Generates a skeleton class for the given Java interface. This method
350    * generates a sub class of the {@link Skeleton} class whose {@link
351    * Skeleton#target target} is an object implementing the given interface,
352    * by using the given class visitor.
353    *
354    * @param cv the class visitor to be used to generate the stub class.
355    * @param itf the Java interface implemented by the skeleton's target.
356    */

357
358   protected void generateSkeletonClass (final ClassVisitor cv, final Class JavaDoc itf) {
359     int access = ACC_PUBLIC;
360     String JavaDoc name = getSkelClassName(itf.getName()).replace('.', '/');
361     String JavaDoc superClass = "org/objectweb/fractal/rmi/stub/Skeleton";
362     cv.visit(V1_1, access, name, superClass, null, null);
363     generateConstructor(cv, superClass);
364     generateSkeletonMethod(cv, name, itf);
365   }
366
367   /**
368    * Generates an empty constructor for the given class.
369    *
370    * @param cv the class visitor to be used to generate the constructor.
371    * @param superClass the internal name of the super class of the generated
372    * class. This name is used to generate a call to the super constructor.
373    */

374
375   protected void generateConstructor (
376     final ClassVisitor cv,
377     final String JavaDoc superClass)
378   {
379     CodeVisitor mv = cv.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
380     // generates the bytecode corresponding to 'super(...);'
381
mv.visitVarInsn(ALOAD, 0);
382     mv.visitMethodInsn(INVOKESPECIAL, superClass, "<init>", "()V");
383     mv.visitInsn(RETURN);
384     mv.visitMaxs(1, 1);
385   }
386
387   /**
388    * Generates a stub method. A stub method is of the following form:
389    * <p>
390    * <pre>
391    * public <i>T</i> m (<i>T0 arg0</i>, ...) throws <i>E0</i>, ... {
392    * try {
393    * Marshaller marshaller = request();
394    * ReplyInterface reply = prepareInvocation(marshaller);
395    * marshaller.writeInt(<i>methodIndex</i>);
396    * marshaller.write<i>XXX</i>(<i>arg0</i>);
397    * ...
398    * invoke(marshaller);
399    * UnMarshaller unmarshaller = reply.listen();
400    * <i>T</i> result = (<i>T</i>)unmarshaller.read<i>XXX</i>();
401    * unmarshaller.close();
402    * return result;
403    * } catch (Exception e) {
404    * e = handleException(e);
405    * if (e instanceof <i>E0</i>) throw (<i>E0</i>)e;
406    * ...
407    * if (e instanceof RuntimeException) throw (RuntimeException)e;
408    * throw new RemoteException("server side exception", e);
409    * }
410    * }
411    * </pre>
412    *
413    * @param cv the class visitor to be used to generate the stub method.
414    * @param name the internal name of the generated stub class.
415    * @param m the signature of the method that must be generated.
416    * @param index the index of this method. This index will be marshalled by the
417    * generated stub method in each invocation message, to specify the
418    * method that must be called on the server side. This index is the index
419    * of the method in the {@link #sort sorted} array of all the
420    * methods implemented by the stub.
421    */

422
423   protected void generateStubMethod (
424     final ClassVisitor cv,
425     final String JavaDoc name,
426     final Method JavaDoc m,
427     final int index)
428   {
429     // generate the method header
430
String JavaDoc mName = m.getName();
431     String JavaDoc mDesc = Type.getMethodDescriptor(m);
432     Class JavaDoc[] params = m.getParameterTypes();
433     int locals = 1;
434     for (int i = 0; i < params.length; ++i) {
435       locals += getSize(params[i]);
436     }
437     Class JavaDoc result = m.getReturnType();
438     Class JavaDoc[] exceptions = m.getExceptionTypes();
439     String JavaDoc[] excepts = new String JavaDoc[exceptions.length];
440     for (int i = 0; i < exceptions.length; ++i) {
441       excepts[i] = Type.getInternalName(exceptions[i]);
442     }
443     CodeVisitor mv = cv.visitMethod(ACC_PUBLIC, mName, mDesc, excepts, null);
444
445     Label beginL = new Label();
446     Label endL = new Label();
447
448     mv.visitLabel(beginL);
449     // generate "Marshaller marshaller = request();"
450
mv.visitVarInsn(ALOAD, 0);
451     mv.visitMethodInsn(
452       INVOKEVIRTUAL,
453       name,
454       "request",
455       "()Lorg/objectweb/jonathan/apis/presentation/Marshaller;");
456     mv.visitVarInsn(ASTORE, locals);
457
458     // generate "ReplyInterface reply = prepareInvocation(marshaller);"
459
mv.visitVarInsn(ALOAD, 0);
460     mv.visitVarInsn(ALOAD, locals);
461     mv.visitMethodInsn(
462       INVOKEVIRTUAL,
463       name,
464       "prepareInvocation",
465       "(Lorg/objectweb/jonathan/apis/presentation/Marshaller;)Lorg/objectweb/jonathan/apis/protocols/ReplyInterface;");
466     mv.visitVarInsn(ASTORE, locals + 1);
467
468     // generate "marshaller.writeInt(<index>);"
469
mv.visitVarInsn(ALOAD, locals);
470     mv.visitIntInsn(SIPUSH, index);
471     mv.visitMethodInsn(
472       INVOKEINTERFACE,
473       "org/objectweb/jonathan/apis/presentation/Marshaller",
474       "writeInt",
475       "(I)V");
476
477     // parameter marshalling
478
int offset = 1;
479     for (int i = 0; i < params.length; ++i) {
480       mv.visitVarInsn(ALOAD, locals);
481       if (params[i].isPrimitive()) {
482         mv.visitVarInsn(ILOAD + getOpcodeOffset(params[i]), offset);
483         mv.visitMethodInsn(
484           INVOKEINTERFACE,
485           "org/objectweb/jonathan/apis/presentation/Marshaller",
486           "write" + getMarshallerMethod(params[i]),
487           "(" + Type.getDescriptor(params[i]) + ")V");
488       } else {
489         if (isClassParameter(m, i)) {
490           mv.visitVarInsn(ALOAD, 0);
491           mv.visitVarInsn(ILOAD + getOpcodeOffset(params[i]), offset);
492           mv.visitMethodInsn(
493             INVOKEVIRTUAL,
494             "org/objectweb/fractal/rmi/stub/Stub",
495             "replaceClassName",
496             "(Ljava/lang/Object;)Ljava/lang/Object;");
497         } else {
498           mv.visitVarInsn(ILOAD + getOpcodeOffset(params[i]), offset);
499         }
500         mv.visitMethodInsn(
501           INVOKEINTERFACE,
502           "org/objectweb/jonathan/apis/presentation/Marshaller",
503           "writeValue",
504           "(Ljava/lang/Object;)V");
505       }
506       offset += getSize(params[i]);
507     }
508
509     // generate "invoke(marshaller);"
510
mv.visitVarInsn(ALOAD, 0);
511     mv.visitVarInsn(ALOAD, locals);
512     mv.visitMethodInsn(
513       INVOKEVIRTUAL,
514       name,
515       "invoke",
516       "(Lorg/objectweb/jonathan/apis/presentation/Marshaller;)V");
517
518     // generate "UnMarshaller unmarshaller = reply.listen();"
519
mv.visitVarInsn(ALOAD, locals + 1);
520     mv.visitMethodInsn(
521       INVOKEINTERFACE,
522       "org/objectweb/jonathan/apis/protocols/ReplyInterface",
523       "listen",
524       "()Lorg/objectweb/jonathan/apis/presentation/UnMarshaller;");
525     mv.visitVarInsn(ASTORE, locals + 2);
526
527     // return value unmarshalling
528
if (result != Void.TYPE) {
529       if (result.isPrimitive()) {
530         mv.visitVarInsn(ALOAD, locals + 2);
531         mv.visitMethodInsn(
532           INVOKEINTERFACE,
533           "org/objectweb/jonathan/apis/presentation/UnMarshaller",
534           "read" + getMarshallerMethod(result),
535           "()" + Type.getDescriptor(result));
536       } else {
537         if (isClassParameter(m, -1)) {
538           mv.visitVarInsn(ALOAD, 0);
539           mv.visitVarInsn(ALOAD, locals + 2);
540           mv.visitMethodInsn(
541             INVOKEINTERFACE,
542             "org/objectweb/jonathan/apis/presentation/UnMarshaller",
543             "readValue",
544             "()Ljava/lang/Object;");
545           mv.visitMethodInsn(
546             INVOKEVIRTUAL,
547             "org/objectweb/fractal/rmi/stub/Stub",
548             "replaceClassValue",
549             "(Ljava/lang/Object;)Ljava/lang/Object;");
550         } else {
551           mv.visitVarInsn(ALOAD, locals + 2);
552           mv.visitMethodInsn(
553             INVOKEINTERFACE,
554             "org/objectweb/jonathan/apis/presentation/UnMarshaller",
555             "readValue",
556             "()Ljava/lang/Object;");
557         }
558         mv.visitTypeInsn(CHECKCAST, Type.getInternalName(result));
559       }
560       mv.visitVarInsn(ISTORE + getOpcodeOffset(result), locals + 3);
561     }
562
563     // generate "unmarshaller.close();"
564
mv.visitVarInsn(ALOAD, locals + 2);
565     mv.visitMethodInsn(
566       INVOKEINTERFACE,
567       "org/objectweb/jonathan/apis/presentation/UnMarshaller",
568       "close",
569       "()V");
570
571     // generate "return result";
572
if (result != Void.TYPE) {
573       mv.visitVarInsn(ILOAD + getOpcodeOffset(result), locals + 3);
574       mv.visitInsn(IRETURN + getOpcodeOffset(result));
575     } else {
576       mv.visitInsn(RETURN);
577     }
578     mv.visitLabel(endL);
579
580     mv.visitVarInsn(ASTORE, locals);
581
582     // generate "e = handleException(e);"
583
mv.visitVarInsn(ALOAD, 0);
584     mv.visitVarInsn(ALOAD, locals);
585     mv.visitMethodInsn(
586       INVOKEVIRTUAL,
587       name,
588       "handleException",
589       "(Ljava/lang/Exception;)Ljava/lang/Exception;");
590     mv.visitVarInsn(ASTORE, locals);
591
592     // generate "if (e instanceof <type>) throw (<type>)e;"
593
for (int i = 0; i < exceptions.length; ++i) {
594       String JavaDoc type = Type.getInternalName(exceptions[i]);
595       mv.visitVarInsn(ALOAD, locals);
596       mv.visitTypeInsn(INSTANCEOF, type);
597       Label next = new Label();
598       mv.visitJumpInsn(IFEQ, next);
599       mv.visitVarInsn(ALOAD, locals);
600       mv.visitTypeInsn(CHECKCAST, type);
601       mv.visitInsn(ATHROW);
602       mv.visitLabel(next);
603     }
604
605     // generate "if (e instanceof RuntimeException) throw (RuntimeException)e;"
606
mv.visitVarInsn(ALOAD, locals);
607     mv.visitTypeInsn(INSTANCEOF, "java/lang/RuntimeException");
608     Label next = new Label();
609     mv.visitJumpInsn(IFEQ, next);
610     mv.visitVarInsn(ALOAD, locals);
611     mv.visitTypeInsn(CHECKCAST, "java/lang/RuntimeException");
612     mv.visitInsn(ATHROW);
613     mv.visitLabel(next);
614
615     // generate "throw new RemoteException("server side exception", e);"
616
mv.visitTypeInsn(NEW, "org/objectweb/fractal/rmi/RemoteException");
617     mv.visitInsn(DUP);
618     mv.visitLdcInsn("server side exception");
619     mv.visitVarInsn(ALOAD, locals);
620     mv.visitMethodInsn(
621       INVOKESPECIAL,
622       "org/objectweb/fractal/rmi/RemoteException",
623       "<init>",
624       "(Ljava/lang/String;Ljava/lang/Exception;)V");
625     mv.visitInsn(ATHROW);
626
627     // end method body
628
mv.visitTryCatchBlock(beginL, endL, endL, "java/lang/Exception");
629     mv.visitMaxs(0, 0);
630   }
631
632   /**
633    * Generates a skeleton's {@link Skeleton#send send} method. The generated
634    * method is of the following form:
635    * <p>
636    * <pre>
637    * public void send (UnMarshaller unmarshaller, ReplySession session)
638    * throws JonathanException
639    * {
640    * int methodIndex = unmarshaller.readInt();
641    * try {
642    * switch (methodIndex) {
643    * case 0:
644    * <i>T0 arg0</i> = unmarshaller.read<i>XXX</i>();
645    * ...
646    * unmarshaller.close();
647    * <i>T</i> result = ((<i>I</i>)target).<i>m0</i>(<i>arg0</i>, ... );
648    * Marshaller marshaller = session.prepareReply();
649    * marshaller.write<i>XXX</i>(result);
650    * session.send(marshaller);
651    * session.close();
652    * return;
653    * ...
654    * default:
655    * handleInterfaceMethods(unmarshaller, session, methodIndex);
656    * }
657    * } catch (Exception e) {
658    * handleException(e, session);
659    * }
660    * }
661    * </pre>
662    *
663    * @param cv the class visitor to be used to generate the stub method.
664    * @param name the internal name of the generated stub class.
665    * @param itf the target object's interface that must be exported by the
666    * skeleton.
667    */

668
669   protected void generateSkeletonMethod (
670     final ClassVisitor cv,
671     final String JavaDoc name,
672     final Class JavaDoc itf)
673   {
674     CodeVisitor mv = cv.visitMethod(
675       ACC_PUBLIC,
676       "send",
677       "(Lorg/objectweb/jonathan/apis/presentation/UnMarshaller;Lorg/objectweb/jonathan/apis/protocols/ReplySession;)V",
678       new String JavaDoc[] { "org/objectweb/jonathan/apis/kernel/JonathanException" },
679       null);
680
681     Label beginL = new Label();
682     Label endL = new Label();
683     Label defaultL = new Label();
684
685     // generate "int methodIndex = unmarshaller.readInt();"
686
mv.visitLabel(beginL);
687     mv.visitVarInsn(ALOAD, 1);
688     mv.visitMethodInsn(
689       INVOKEINTERFACE,
690       "org/objectweb/jonathan/apis/presentation/UnMarshaller",
691       "readInt",
692       "()I");
693     mv.visitVarInsn(ISTORE, 3);
694
695     // generate the "switch (methodIndex)" statement
696
Method JavaDoc[] meths = itf.getMethods();
697     sort(meths);
698     Label[] cases = new Label[meths.length];
699     for (int i = 0; i < cases.length; ++i) {
700       cases[i] = new Label();
701     }
702     if (meths.length > 0) {
703       mv.visitVarInsn(ILOAD, 3);
704       mv.visitTableSwitchInsn(0, meths.length - 1, defaultL, cases);
705     }
706
707     // generate the "case" blocks
708
for (int i = 0; i < meths.length; ++i) {
709       Method JavaDoc m = meths[i];
710       String JavaDoc mName = m.getName();
711       String JavaDoc mDesc = Type.getMethodDescriptor(m);
712       Class JavaDoc[] params = m.getParameterTypes();
713       Class JavaDoc result = m.getReturnType();
714
715       mv.visitLabel(cases[i]);
716
717       // generate "<type> obj = (<type>)target;"
718
mv.visitVarInsn(ALOAD, 0);
719       mv.visitFieldInsn(GETFIELD, name, "target", "Ljava/lang/Object;");
720       mv.visitTypeInsn(CHECKCAST, Type.getInternalName(itf));
721
722       // parameter unmarshalling
723
for (int j = 0; j < params.length; ++j) {
724         if (params[j].isPrimitive()) {
725           mv.visitVarInsn(ALOAD, 1);
726           mv.visitMethodInsn(
727             INVOKEINTERFACE,
728             "org/objectweb/jonathan/apis/presentation/UnMarshaller",
729             "read" + getMarshallerMethod(params[j]),
730             "()" + Type.getDescriptor(params[j]));
731         } else {
732           if (isClassParameter(m, j)) {
733             mv.visitVarInsn(ALOAD, 0);
734             mv.visitVarInsn(ALOAD, 1);
735             mv.visitMethodInsn(
736               INVOKEINTERFACE,
737               "org/objectweb/jonathan/apis/presentation/UnMarshaller",
738               "readValue",
739               "()Ljava/lang/Object;");
740             mv.visitMethodInsn(
741               INVOKEVIRTUAL,
742               "org/objectweb/fractal/rmi/stub/Skeleton",
743               "replaceClassValue",
744               "(Ljava/lang/Object;)Ljava/lang/Object;");
745           } else {
746             mv.visitVarInsn(ALOAD, 1);
747             mv.visitMethodInsn(
748               INVOKEINTERFACE,
749               "org/objectweb/jonathan/apis/presentation/UnMarshaller",
750               "readValue",
751               "()Ljava/lang/Object;");
752           }
753           mv.visitTypeInsn(CHECKCAST, Type.getInternalName(params[j]));
754         }
755       }
756
757       // generate "unmarshaller.close();"
758
mv.visitVarInsn(ALOAD, 1);
759       mv.visitMethodInsn(
760         INVOKEINTERFACE,
761         "org/objectweb/jonathan/apis/presentation/UnMarshaller",
762         "close",
763         "()V");
764
765       // generate code to call target method and store result
766
mv.visitMethodInsn(INVOKEINTERFACE, Type.getInternalName(itf), mName, mDesc);
767       if (result != Void.TYPE) {
768         mv.visitVarInsn(ISTORE + getOpcodeOffset(result), 5);
769       }
770
771       // generate "Marshaller marshaller = session.prepareReply();"
772
mv.visitVarInsn(ALOAD, 2);
773       mv.visitMethodInsn(
774         INVOKEINTERFACE,
775         "org/objectweb/jonathan/apis/protocols/ReplySession",
776         "prepareReply",
777         "()Lorg/objectweb/jonathan/apis/presentation/Marshaller;");
778       mv.visitVarInsn(ASTORE, 4);
779
780       // generate code to marshall result value
781
if (result != Void.TYPE) {
782         mv.visitVarInsn(ALOAD, 4);
783         if (result.isPrimitive()) {
784           mv.visitVarInsn(ILOAD + getOpcodeOffset(result), 5);
785           mv.visitMethodInsn(
786             INVOKEINTERFACE,
787             "org/objectweb/jonathan/apis/presentation/Marshaller",
788             "write" + getMarshallerMethod(result),
789             "(" + Type.getDescriptor(result)+ ")V");
790         } else {
791           if (isClassParameter(m, -1)) {
792             mv.visitVarInsn(ALOAD, 0);
793             mv.visitVarInsn(ILOAD + getOpcodeOffset(result), 5);
794             mv.visitMethodInsn(
795               INVOKEVIRTUAL,
796               "org/objectweb/fractal/rmi/stub/Skeleton",
797               "replaceClassName",
798               "(Ljava/lang/Object;)Ljava/lang/Object;");
799           } else {
800             mv.visitVarInsn(ILOAD + getOpcodeOffset(result), 5);
801           }
802           mv.visitMethodInsn(
803             INVOKEINTERFACE,
804             "org/objectweb/jonathan/apis/presentation/Marshaller",
805             "writeValue",
806             "(Ljava/lang/Object;)V");
807         }
808       }
809
810       // generate "session.send(marshaller);"
811
mv.visitVarInsn(ALOAD, 2);
812       mv.visitVarInsn(ALOAD, 4);
813       mv.visitMethodInsn(
814         INVOKEINTERFACE,
815         "org/objectweb/jonathan/apis/protocols/ReplySession",
816         "send",
817         "(Lorg/objectweb/jonathan/apis/presentation/Marshaller;)V");
818
819       // generate "session.close();"
820
mv.visitVarInsn(ALOAD, 2);
821       mv.visitMethodInsn(
822         INVOKEINTERFACE,
823         "org/objectweb/jonathan/apis/protocols/ReplySession",
824         "close",
825         "()V");
826
827       // generate "return;"
828
mv.visitInsn(RETURN);
829     }
830
831     // generate default block:
832
// "handleInterfaceMethods(unmarshaller, session, methodIndex);"
833
// "return;"
834
mv.visitLabel(defaultL);
835     mv.visitVarInsn(ALOAD, 0);
836     mv.visitVarInsn(ALOAD, 1);
837     mv.visitVarInsn(ALOAD, 2);
838     mv.visitVarInsn(ILOAD, 3);
839     mv.visitMethodInsn(
840       INVOKEVIRTUAL,
841       name,
842       "handleInterfaceMethods",
843       "(Lorg/objectweb/jonathan/apis/presentation/UnMarshaller;Lorg/objectweb/jonathan/apis/protocols/ReplySession;I)V");
844     mv.visitInsn(RETURN);
845
846     // generate "catch (Exception e) { handleException(e, session); }"
847
mv.visitLabel(endL);
848     mv.visitVarInsn(ASTORE, 3);
849     mv.visitVarInsn(ALOAD, 0);
850     mv.visitVarInsn(ALOAD, 3);
851     mv.visitVarInsn(ALOAD, 2);
852     mv.visitMethodInsn(
853       INVOKEVIRTUAL,
854       name,
855       "handleException",
856       "(Ljava/lang/Exception;Lorg/objectweb/jonathan/apis/protocols/ReplySession;)V");
857     mv.visitInsn(RETURN);
858
859     // end method code
860
mv.visitTryCatchBlock(beginL, endL, endL, "java/lang/Exception");
861     mv.visitMaxs(0, 0);
862   }
863
864   /**
865    * Returns <tt>true</tt> if the specified parameter contains the name of a
866    * class. This method is used to marshall a <tt>Class</tt> object, instead of
867    * a <tt>String</tt>, for parameters that contains class names, in order to
868    * enable these classes to be downloaded from the network if needed. The
869    * default implementation of this method returns <tt>true</tt> for:
870    * <ul>
871    * <li>the return value of <tt>Factory.getFcContentDesc</tt>.</li>
872    * <li>the second parameter of <tt>GenericFactory.newFcInstance</tt>.</li>
873    * <li>the return value of <tt>InterfaceType.getFcItfSignature</tt>.</li>
874    * <li>the second parameter of <tt>TypeFactory.createFcItfType</tt>.</li>
875    * </ul>
876    *
877    * @param m a method.
878    * @param p index of a parameter of this method, or <tt>-1</tt> to designate
879    * the return value.
880    * @return <tt>true</tt> if the specified parameter contains the name of a
881    * class.
882    */

883
884   protected boolean isClassParameter (final Method JavaDoc m, final int p) {
885     String JavaDoc name = m.getName();
886     if (name.equals("getFcContentDesc")) {
887       return p == -1;
888     } else if (name.equals("newFcInstance")) {
889       return p == 2;
890     } else if (name.equals("getFcItfSignature")) {
891       return p == -1;
892     } else if (name.equals("createFcItfType")) {
893       return p == 1;
894     }
895     return false;
896   }
897
898   /**
899    * Sorts the given methods. This method is used to assign an unambiguous
900    * index to each method of an interface (the index of a method in
901    * the array returned by {@link Class#getMethods() getMethods} cannot directly
902    * be used for this purpose, since this method returns the methods in any
903    * order).
904    *
905    * @param methods the method array to be sorted.
906    */

907
908   protected static void sort (final Method JavaDoc[] methods) {
909     String JavaDoc[] descs = new String JavaDoc[methods.length];
910     for (int i = 0; i < descs.length; ++i) {
911       descs[i] = methods[i].getName() + Type.getMethodDescriptor(methods[i]);
912     }
913     for (int i = 0; i < descs.length; ++i) {
914       for (int j = i + 1; j < descs.length; ++j) {
915         if (descs[j].compareTo(descs[i]) < 0) {
916           String JavaDoc s = descs[i];
917           descs[i] = descs[j];
918           descs[j] = s;
919           Method JavaDoc m = methods[i];
920           methods[i] = methods[j];
921           methods[j] = m;
922         }
923       }
924     }
925   }
926
927   /**
928    * Returns the suffix of the (un)marshaller method corresponding to the given
929    * primitive type.
930    *
931    * @param type a primitive type.
932    * @return the suffix of the (un)marshaller method corresponding to the given
933    * primitive type (such as "Byte" for byte, or "Char16" for char).
934    */

935
936   private static String JavaDoc getMarshallerMethod (final Class JavaDoc type) {
937     if (type == Byte.TYPE) {
938       return "Byte";
939     } else if (type == Integer.TYPE) {
940       return "Int";
941     } else if (type == Boolean.TYPE) {
942       return "Boolean";
943     } else if (type == Double.TYPE) {
944       return "Double";
945     } else if (type == Float.TYPE) {
946       return "Float";
947     } else if (type == Long.TYPE) {
948       return "Long";
949     } else if (type == Character.TYPE) {
950       return "Char16";
951     } else /*if (result == Short.TYPE)*/ {
952       return "Short";
953     }
954   }
955
956   /**
957    * Returns the offset which must be added to some opcodes to get an opcode of
958    * the given type. More precisely, returns the offset which must be added to
959    * an opc_iXXXX opcode to get the opc_YXXXX opcode corresponding to the given
960    * type. For example, if the given type is double the result is 3, which
961    * means that opc_dload, opc_dstore, opc_dreturn... opcodes are equal to
962    * opc_iload+3, opc_istore+3, opc_ireturn+3...
963    *
964    * @param type a Java class representing a Java type (primitive or not).
965    * @return the opcode offset of the corresponding to the given type.
966    */

967
968   private static int getOpcodeOffset (final Class JavaDoc type) {
969     if (type == Double.TYPE) {
970       return 3;
971     } else if (type == Float.TYPE) {
972       return 2;
973     } else if (type == Long.TYPE) {
974       return 1;
975     } else if (type.isPrimitive()) {
976       return 0;
977     }
978     return 4;
979   }
980
981   /**
982    * Returns the size of the given type. This size is 2 for the double and long
983    * types, and 1 for the other types.
984    *
985    * @param type a Java class representing a Java type (primitive or not).
986    * @return the size of the given type.
987    */

988
989   private static int getSize (final Class JavaDoc type) {
990     return (type == Double.TYPE || type == Long.TYPE ? 2 : 1);
991   }
992
993   /**
994    * Returns the name of the stub class for the given class.
995    *
996    * @param className a fully qualified class name.
997    * @return the name of the stub class for the given class.
998    */

999
1000  private static String JavaDoc getStubClassName (final String JavaDoc className) {
1001    if (className.startsWith("java.")) {
1002      return "stub." + className + "_JavaStub";
1003    } else {
1004      return className + "_Stub";
1005    }
1006  }
1007
1008  /**
1009   * Returns <tt>true</tt> if the given name if the name of a stub class.
1010   *
1011   * @param className a fully qualified class name.
1012   * @return <tt>true</tt> if the given name if the name of a stub class.
1013   */

1014
1015  private static boolean isStubClassName (final String JavaDoc className) {
1016    return className.endsWith("_JavaStub") || className.endsWith("_Stub");
1017  }
1018
1019  /**
1020   * Returns the class name corresponding to the given stub class name.
1021   *
1022   * @param stubClassName a fully qualified stub class name.
1023   * @return the class name corresponding to the given stub class name.
1024   */

1025
1026  private static String JavaDoc resolveStubClassName (final String JavaDoc stubClassName) {
1027    if (stubClassName.endsWith("_JavaStub")) {
1028      return stubClassName.substring(5, stubClassName.length() - 9);
1029    } else {
1030      return stubClassName.substring(0, stubClassName.length() - 5);
1031    }
1032  }
1033
1034  /**
1035   * Returns the name of the skeleton class for the given class.
1036   *
1037   * @param className a fully qualified class name.
1038   * @return the name of the skeleton class for the given class.
1039   */

1040
1041  private static String JavaDoc getSkelClassName (final String JavaDoc className) {
1042    if (className.startsWith("java.")) {
1043      return "skel." + className + "_JavaSkel";
1044    } else {
1045      return className + "_Skel";
1046    }
1047  }
1048
1049  /**
1050   * Returns <tt>true</tt> if the given name if the name of a skeleton class.
1051   *
1052   * @param className a fully qualified class name.
1053   * @return <tt>true</tt> if the given name if the name of a skeleton class.
1054   */

1055
1056  private static boolean isSkelClassName (final String JavaDoc className) {
1057    return className.endsWith("_JavaSkel") || className.endsWith("_Skel");
1058  }
1059
1060  /**
1061   * Returns the class name corresponding to the given skeleton class name.
1062   *
1063   * @param skelClassName a fully qualified skeleton class name.
1064   * @return the class name corresponding to the given skeleton class name.
1065   */

1066
1067  private static String JavaDoc resolveSkelClassName (final String JavaDoc skelClassName) {
1068    if (skelClassName.endsWith("_JavaSkel")) {
1069      return skelClassName.substring(5, skelClassName.length() - 9);
1070    } else {
1071      return skelClassName.substring(0, skelClassName.length() - 5);
1072    }
1073  }
1074}
1075
Popular Tags