KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > ws > jaxme > js > apps > XmlRpcClientGenerator


1 package org.apache.ws.jaxme.js.apps;
2
3 import java.lang.reflect.Method JavaDoc;
4 import java.lang.reflect.UndeclaredThrowableException JavaDoc;
5 import java.util.ArrayList JavaDoc;
6 import java.util.HashMap JavaDoc;
7 import java.util.Iterator JavaDoc;
8 import java.util.List JavaDoc;
9 import java.util.Map JavaDoc;
10 import java.util.Vector JavaDoc;
11
12 import org.apache.ws.jaxme.js.DirectAccessible;
13 import org.apache.ws.jaxme.js.JavaComment;
14 import org.apache.ws.jaxme.js.JavaConstructor;
15 import org.apache.ws.jaxme.js.JavaField;
16 import org.apache.ws.jaxme.js.JavaInnerClass;
17 import org.apache.ws.jaxme.js.JavaMethod;
18 import org.apache.ws.jaxme.js.JavaQName;
19 import org.apache.ws.jaxme.js.JavaQNameImpl;
20 import org.apache.ws.jaxme.js.JavaSource;
21 import org.apache.ws.jaxme.js.JavaSourceFactory;
22 import org.apache.ws.jaxme.js.LocalJavaField;
23 import org.apache.ws.jaxme.js.Parameter;
24 import org.apache.ws.jaxme.js.pattern.MethodKey;
25
26
27 /** This class generates clients for Apache XML-RPC.
28  * The basic idea goes as follows:
29  * <ol>
30  * <li>Provide a class implementing the interface {@link XmlRpcCaller}.</li>
31  * <li>Provide a server side class being called via XML-RPC. The class
32  * must have a public default constructor, should be stateless, and
33  * all callable methods must be public instance methods.</li>
34  * <li>Run the generator, specifying a target package.</li>
35  * </ol>
36  * On the client, use the generated class, as if it were the server
37  * side class.
38  */

39 public class XmlRpcClientGenerator {
40     private final JavaSourceFactory factory;
41     private final String JavaDoc targetPackage;
42     private final Map JavaDoc methods = new HashMap JavaDoc();
43     private boolean dispatcherImplementsXmlRpcHandler = true;
44
45     /** Returns whether the generated dispatcher implements
46      * XmlRpcHandler. The default value is true.
47      */

48     public boolean isDispatcherImplementsXmlRpcHandler() {
49         return dispatcherImplementsXmlRpcHandler;
50     }
51
52     /** Sets whether the generated dispatcher implements
53      * XmlRpcHandler. The default value is true.
54      */

55     public void setDispatcherImplementsXmlRpcHandler(
56             boolean pDispatcherImplementsXmlRpcHandler) {
57         dispatcherImplementsXmlRpcHandler = pDispatcherImplementsXmlRpcHandler;
58     }
59
60     /** Creates a new instance with the given factory and target package.
61      */

62     public XmlRpcClientGenerator(JavaSourceFactory pFactory, String JavaDoc pTargetPackage) {
63         factory = pFactory;
64         targetPackage = pTargetPackage;
65     }
66
67     /** Returns the factory, that was submitted to the constructor.
68      */

69     public JavaSourceFactory getFactory() { return factory; }
70
71     /** Returns the target package, that was submitted to the constructor.
72      */

73     public String JavaDoc getTargetPackage() { return targetPackage; }
74
75     /** Generates a name for the method <code>pMethod</code> and
76      * adds it to the method map, the name being the key.
77      * @return The generated name.
78      */

79     protected String JavaDoc addMethod(JavaMethod pMethod) {
80         String JavaDoc className = pMethod.getJavaSource().getQName().toString();
81         String JavaDoc methodName = pMethod.getName();
82         for (int i = 0; ; i++) {
83             String JavaDoc name = className + "-" + methodName;
84             if (i > 0) {
85                 name += i;
86             }
87             if (!methods.containsKey(name)) {
88                 methods.put(name, pMethod);
89                 return name;
90             }
91         }
92     }
93
94     /** Converts the result value <code>pValue</code> into the
95      * requested type <code>pType</code>.
96      */

97     protected Object JavaDoc getResultValue(JavaMethod pMethod, JavaQName pType, Object JavaDoc pValue) {
98         if (JavaQNameImpl.BOOLEAN.equals(pType)) {
99             return new Object JavaDoc[]{"((", Boolean JavaDoc.class, ") ", pValue, ").booleanValue()"};
100         } else if (JavaQNameImpl.BYTE.equals(pType)) {
101             return new Object JavaDoc[]{"((", Byte JavaDoc.class, ") ", pValue, ").byteValue()"};
102         } else if (JavaQNameImpl.SHORT.equals(pType)) {
103             return new Object JavaDoc[]{"((", Short JavaDoc.class, ") ", pValue, ").shortValue()"};
104         } else if (JavaQNameImpl.INT.equals(pType)) {
105             return new Object JavaDoc[]{"((", Integer JavaDoc.class, ") ", pValue, ").intValue()"};
106         } else if (JavaQNameImpl.LONG.equals(pType)) {
107             return new Object JavaDoc[]{"((", Long JavaDoc.class, ") ", pValue, ").longValue()"};
108         } else if (JavaQNameImpl.FLOAT.equals(pType)) {
109             return new Object JavaDoc[]{"((", Float JavaDoc.class, ") ", pValue, ").floatValue()"};
110         } else if (JavaQNameImpl.DOUBLE.equals(pType)) {
111             return new Object JavaDoc[]{"((", Double JavaDoc.class, ") ", pValue, ").doubleValue()"};
112         } else if (pType.isArray()) {
113             LocalJavaField resultV = pMethod.newJavaField(Vector JavaDoc.class);
114             resultV.addLine("(", Vector JavaDoc.class, ") ", pValue, ";");
115             LocalJavaField resultA = pMethod.newJavaField(pType);
116             pMethod.addIf(resultV , " == null");
117             pMethod.addLine(resultA, " = null;");
118             pMethod.addElse();
119             pMethod.addLine(resultA, " = new ", pType.getInstanceClass(), "[", resultV, ".size()];");
120             DirectAccessible i = pMethod.addForArray(resultA);
121             Object JavaDoc element = new Object JavaDoc[]{resultV, ".elementAt(", i, ")"};
122             pMethod.addLine(resultA, "[", i, "] = ", getResultValue(pMethod, pType.getInstanceClass(), element), ";");
123             pMethod.addEndFor();
124             pMethod.addEndIf();
125             return resultA;
126         } else if (JavaQNameImpl.getInstance(Object JavaDoc.class).equals(pType)) {
127             return pValue;
128         } else {
129             return new Object JavaDoc[]{"(", pType, ") ", pValue};
130         }
131     }
132
133     /** Converts the given input <code>pValue</code> with type
134      * <code>pType</code> into a valid XML-RPC type.
135      */

136     protected Object JavaDoc getInputValue(JavaMethod pMethod, JavaQName pType, Object JavaDoc pValue) {
137         if (pType.equals(JavaQNameImpl.BOOLEAN)) {
138             return new Object JavaDoc[]{pValue, " ? ", Boolean JavaDoc.class, ".TRUE : ", Boolean JavaDoc.class, ".FALSE"};
139         } else if (pType.equals(JavaQNameImpl.BYTE)) {
140             return new Object JavaDoc[]{"new ", Byte JavaDoc.class, "(", pValue, ")"};
141         } else if (pType.equals(JavaQNameImpl.SHORT)) {
142             return new Object JavaDoc[]{"new ", Short JavaDoc.class, "(", pValue, ")"};
143         } else if (pType.equals(JavaQNameImpl.INT)) {
144             return new Object JavaDoc[]{"new ", Integer JavaDoc.class, "(", pValue, ")"};
145         } else if (pType.equals(JavaQNameImpl.LONG)) {
146             return new Object JavaDoc[]{"new ", Long JavaDoc.class, "(", pValue, ")"};
147         } else if (pType.equals(JavaQNameImpl.FLOAT)) {
148             return new Object JavaDoc[]{"new ", Float JavaDoc.class, "(", pValue, ")"};
149         } else if (pType.equals(JavaQNameImpl.DOUBLE)) {
150             return new Object JavaDoc[]{"new ", Double JavaDoc.class, "(", pValue, ")"};
151         } else if (pType.isArray()) {
152             if (!(pValue instanceof DirectAccessible)) {
153                 LocalJavaField val = pMethod.newJavaField(pType);
154                 val.addLine(pValue);
155                 pValue = val;
156             }
157             LocalJavaField v = pMethod.newJavaField(Vector JavaDoc.class);
158             pMethod.addIf(pValue, " == null");
159             pMethod.addLine(v, " = null;");
160             pMethod.addElse();
161             pMethod.addLine(v, " = new ", Vector JavaDoc.class, "();");
162             DirectAccessible i = pMethod.addForArray(pValue);
163             Object JavaDoc element = new Object JavaDoc[]{pValue, "[", i, "]"};
164             pMethod.addLine(v, ".add(", getInputValue(pMethod, pType.getInstanceClass(), element), ");");
165             pMethod.addEndFor();
166             pMethod.addEndIf();
167             return v;
168         } else {
169             return pValue;
170         }
171     }
172
173     /** Generates a method, invoking method <code>pMethod</code> using
174      * the name <code>pName</code>.
175      * @throws NoSuchMethodException
176      * @throws SecurityException
177      */

178     protected JavaMethod getMethod(JavaSource pJs, JavaField pCaller,
179                                    String JavaDoc pName, JavaMethod pMethod)
180             throws SecurityException JavaDoc, NoSuchMethodException JavaDoc {
181         Method JavaDoc m = XmlRpcCaller.class.getMethod("xmlRpcCall", new Class JavaDoc[]{String JavaDoc.class, Vector JavaDoc.class});
182         Class JavaDoc[] exceptions = m.getExceptionTypes();
183         List JavaDoc exceptionList = new ArrayList JavaDoc();
184         if (exceptions != null) {
185             for (int i = 0; i < exceptions.length; i++) {
186                 JavaQName qName = JavaQNameImpl.getInstance(exceptions[i]);
187                 if (!pMethod.isThrowing(qName)) {
188                     exceptionList.add(qName);
189                 }
190             }
191         }
192         
193         JavaMethod jm = pJs.newJavaMethod(pMethod);
194         LocalJavaField v = jm.newJavaField(Vector JavaDoc.class);
195         v.addLine("new ", Vector JavaDoc.class, "()");
196         Parameter[] params = jm.getParams();
197         for (int i = 0; i < params.length; i++) {
198             Parameter p = params[i];
199             jm.addLine(v, ".add(", getInputValue(jm, p.getType(), p), ");");
200         }
201         if (!exceptionList.isEmpty()) {
202             jm.addTry();
203         }
204         Object JavaDoc result = new Object JavaDoc[]{pCaller, ".xmlRpcCall(",
205                                      JavaSource.getQuoted(pName), ", ", v, ")"};
206         if (JavaQNameImpl.VOID.equals(jm.getType())) {
207             jm.addLine(result, ";");
208         } else {
209             jm.addLine("return ", getResultValue(jm, jm.getType(), result), ";");
210         }
211         if (!exceptionList.isEmpty()) {
212             for (int i = 0; i < exceptionList.size(); i++) {
213                 JavaQName exClass = (JavaQName) exceptionList.get(i);
214                 DirectAccessible e = jm.addCatch(exClass);
215                 jm.addThrowNew(UndeclaredThrowableException JavaDoc.class, e);
216             }
217             jm.addEndTry();
218         }
219         return jm;
220     }
221
222     protected JavaField getXmlRpcCaller(JavaSource pJs) {
223         JavaField jf = pJs.newJavaField("caller", XmlRpcCaller.class, JavaSource.PRIVATE);
224         jf.setFinal(true);
225         return jf;
226     }
227
228     protected JavaConstructor getConstructor(JavaSource pJs, JavaField jf) {
229         JavaConstructor jcon = pJs.newJavaConstructor(JavaSource.PUBLIC);
230         Parameter param = jcon.addParam(XmlRpcCaller.class, "pCaller");
231         jcon.addLine(jf, " = ", param, ";");
232         return jcon;
233     }
234
235     /** Returns, whether a remote method call is generated for method
236      * <code>pMethod</code>. The default implementation returns true,
237      * if the method is public and not static.
238      */

239     protected boolean isMethodGenerated(JavaMethod pMethod) {
240         return JavaSource.PUBLIC.equals(pMethod.getProtection())
241           && !pMethod.isStatic();
242     }
243
244     /** Creates a new client class, which is invoking the given
245      * server side class <code>pSource</code>.
246      */

247     public JavaSource addClass(JavaSource pSource, JavaSourceResolver pResolver)
248             throws SecurityException JavaDoc, NoSuchMethodException JavaDoc {
249         JavaSource js = getFactory().newJavaSource(JavaQNameImpl.getInstance(getTargetPackage(), pSource.getQName().getClassName()), JavaSource.PUBLIC);
250         JavaField jf = getXmlRpcCaller(js);
251         getConstructor(js, jf);
252         Map JavaDoc keys = new HashMap JavaDoc();
253         addMethods(js, pSource, keys, jf, pResolver);
254         return js;
255     }
256
257     protected void addMethods(JavaSource pResult, JavaSource pSource, Map JavaDoc pKeys,
258                               JavaField pField, JavaSourceResolver pResolver)
259             throws SecurityException JavaDoc, NoSuchMethodException JavaDoc {
260         JavaMethod[] methods = pSource.getMethods();
261         for (int i = 0; i < methods.length; i++) {
262             JavaMethod m = methods[i];
263             if (isMethodGenerated(m)) {
264                 MethodKey key = new MethodKey(m);
265                 if (pKeys.containsKey(key)) {
266                     continue;
267                 }
268                 String JavaDoc name = addMethod(m);
269                 pKeys.put(key, getMethod(pResult, pField, name, m));
270             }
271         }
272         if (pResolver != null) {
273             JavaQName[] qNames = pSource.getExtends();
274             for (int i = 0; i < qNames.length; i++) {
275                 JavaSource js = pResolver.getJavaSource(qNames[i]);
276                 if (js != null) {
277                     addMethods(pResult, js, pKeys, pField, pResolver);
278                 }
279             }
280         }
281     }
282
283     /** Generates the abstract invoker class.
284      */

285     public JavaSource getInvokerClass(JavaSource pSource) {
286         JavaInnerClass invoker = pSource.newJavaInnerClass("Invoker", JavaSource.PUBLIC);
287         JavaComment comment = invoker.newComment();
288         comment.addLine("The dispatcher is implemented with a {@link java.util.Map}.");
289         comment.addLine("The map keys are the method names, the values");
290         comment.addLine("are instances of <code>Invoker</code>.");
291         invoker.setType(JavaSource.INTERFACE);
292         JavaMethod jm = invoker.newJavaMethod("invoke", Object JavaDoc.class, JavaSource.PUBLIC);
293         comment = jm.newComment();
294         comment.addLine("This method creates a new instance of the class being");
295         comment.addLine("called, converts the parameter objects (if required)");
296         comment.addLine("and invokes the requested method. If required, the");
297         comment.addLine("result is converted also and returned.");
298         jm.addParam(Vector JavaDoc.class, "pParams");
299         jm.addThrows(Throwable JavaDoc.class);
300         return invoker;
301     }
302
303     /** Creates the field with the {@link Map} of invokers.
304      */

305     protected JavaField getInvokerMap(JavaSource pSource) {
306         JavaField result = pSource.newJavaField("map", Map JavaDoc.class, JavaSource.PRIVATE);
307         result.addLine("new ", HashMap JavaDoc.class, "()");
308         return result;
309     }
310
311     /** Creates a new invoker class for the given method.
312      */

313     protected JavaSource getInvoker(JavaSource pSource, JavaMethod pMethod, JavaQName pInvoker, int pNum) {
314         Parameter[] params = pMethod.getParams();
315
316         JavaInnerClass js = pSource.newJavaInnerClass("Invoker" + pNum, JavaSource.PUBLIC);
317         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
318         for (int i = 0; i < params.length; i++) {
319             if (i > 0) {
320                 sb.append(", ");
321             }
322             sb.append(params[i].getType());
323         }
324         JavaComment comment = js.newComment();
325         comment.addLine("Invoker for method " + pMethod.getName() + "(" + sb + ")");
326         comment.addLine("in class " + pMethod.getJavaSource().getQName() + ".");
327         js.setStatic(true);
328         js.addImplements(pInvoker);
329         JavaMethod jm = js.newJavaMethod("invoke", Object JavaDoc.class, JavaSource.PUBLIC);
330         Parameter param = jm.addParam(Vector JavaDoc.class, "params");
331         JavaQName[] classes = pMethod.getExceptions();
332         for (int i = 0; i < classes.length; i++) {
333             jm.addThrows(classes[i]);
334         }
335         List JavaDoc args = new ArrayList JavaDoc();
336         for (int i = 0; i < params.length; i++) {
337             if (i > 0) {
338                 args.add(", ");
339             }
340             Parameter p = params[i];
341             args.add(getResultValue(jm, p.getType(), new Object JavaDoc[]{param, ".elementAt(" + i + ")"}));
342         }
343         Object JavaDoc o = new Object JavaDoc[]{"new ", pMethod.getJavaSource().getQName(), "().",
344                                 pMethod.getName(), "(", args, ")"};
345         if (JavaQNameImpl.VOID.equals(pMethod.getType())) {
346             jm.addLine(o, ";");
347             jm.addLine("return null;");
348         } else {
349             jm.addLine("return ", getInputValue(jm, pMethod.getType(), o), ";");
350         }
351         return js;
352     }
353
354     /** Creates the dispatchers constructor.
355      */

356     public JavaConstructor getDispatcherConstructor(JavaSource pSource,
357                                                     JavaField pMap,
358                                                     JavaQName pInvoker) {
359         JavaConstructor con = pSource.newJavaConstructor(JavaSource.PUBLIC);
360         JavaComment comment = con.newComment();
361         comment.addLine("Creates a new dispatcher.");
362         int num = 0;
363         for (Iterator JavaDoc iter = methods.entrySet().iterator(); iter.hasNext(); ) {
364             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) iter.next();
365             String JavaDoc name = (String JavaDoc) entry.getKey();
366             JavaMethod method = (JavaMethod) entry.getValue();
367             JavaSource innerClass = getInvoker(pSource, method, pInvoker, num++);
368             con.addLine(pMap, ".put(", JavaSource.getQuoted(name), ", new ", innerClass.getQName(), "());");
369         }
370         return con;
371     }
372
373     /** Creates the dispatchers <code>getInvoker</code> method.
374      */

375     protected JavaMethod getGetInvokerMethod(JavaSource pSource, JavaQName pInvoker, JavaField pMap) {
376         JavaMethod jm = pSource.newJavaMethod("getInvoker", pInvoker, JavaSource.PROTECTED);
377         Parameter param = jm.addParam(String JavaDoc.class, "pName");
378         jm.addLine("return (", pInvoker, ") ", pMap, ".get(", param, ");");
379         return jm;
380     }
381
382     /** Creates the dispatchers <code>invoke</code> method.
383      */

384     protected JavaMethod getDispatcherInvokeMethod(JavaSource pSource, JavaQName pInvoker) {
385         JavaMethod jm = pSource.newJavaMethod("execute", Object JavaDoc.class, JavaSource.PUBLIC);
386         JavaComment comment = jm.newComment();
387         comment.addLine("Called for invocation of method <code>pName</code> with");
388         comment.addLine("the parameters given by <code>pParams</code>.");
389         Parameter name = jm.addParam(String JavaDoc.class, "pName");
390         Parameter args = jm.addParam(Vector JavaDoc.class, "pParams");
391         jm.addThrows(Exception JavaDoc.class);
392         LocalJavaField invoker = jm.newJavaField(pInvoker);
393         invoker.addLine("getInvoker(", name, ")");
394         jm.addIf(invoker, " == null");
395         jm.addThrowNew(IllegalStateException JavaDoc.class,
396                        JavaSource.getQuoted("Unknown method name: "),
397                        " + ", name);
398         jm.addEndIf();
399         jm.addTry();
400         jm.addLine("return ", invoker, ".invoke(", args, ");");
401         DirectAccessible e = jm.addCatch(Exception JavaDoc.class);
402         jm.addLine("throw ", e, ";");
403         DirectAccessible t = jm.addCatch(Throwable JavaDoc.class);
404         jm.addThrowNew(UndeclaredThrowableException JavaDoc.class, t);
405         jm.addEndTry();
406         return jm;
407     }
408
409     /** Creates the dispatcher class. Make sure, that this method
410      * is invoked <em>after</em> {@link #addClass(JavaSource, JavaSourceResolver)}!
411      * @param pQName Fully qualified class name of the dispatcher class.
412      */

413     public JavaSource getDispatcher(JavaQName pQName) {
414         JavaSource js = getFactory().newJavaSource(pQName, JavaSource.PUBLIC);
415         if (isDispatcherImplementsXmlRpcHandler()) {
416             js.addImport(JavaQNameImpl.getInstance("org.apache.xmlrpc.XmlRpcHandler", true));
417         }
418         JavaComment comment = js.newComment();
419         comment.addLine("The dispatcher is being used by the XmlRpcServer.");
420         comment.addLine("It delegates incoming XML-RPC calls to the classes");
421         comment.addLine("and methods, for which client classes have been");
422         comment.addLine("created by the " +
423                         XmlRpcClientGenerator.class.getName() + ".");
424         JavaSource invoker = getInvokerClass(js);
425         JavaField map = getInvokerMap(js);
426         getDispatcherConstructor(js, map, invoker.getQName());
427         getGetInvokerMethod(js, invoker.getQName(), map);
428         getDispatcherInvokeMethod(js, invoker.getQName());
429         return js;
430     }
431 }
432
Popular Tags