1 /* |
|
2 * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. |
|
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
4 * |
|
5 * This code is free software; you can redistribute it and/or modify it |
|
6 * under the terms of the GNU General Public License version 2 only, as |
|
7 * published by the Free Software Foundation. Oracle designates this |
|
8 * particular file as subject to the "Classpath" exception as provided |
|
9 * by Oracle in the LICENSE file that accompanied this code. |
|
10 * |
|
11 * This code is distributed in the hope that it will be useful, but WITHOUT |
|
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
14 * version 2 for more details (a copy is included in the LICENSE file that |
|
15 * accompanied this code). |
|
16 * |
|
17 * You should have received a copy of the GNU General Public License version |
|
18 * 2 along with this work; if not, write to the Free Software Foundation, |
|
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
20 * |
|
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
22 * or visit www.oracle.com if you need additional information or have any |
|
23 * questions. |
|
24 */ |
|
25 |
|
26 package sun.rmi.rmic.newrmic.jrmp; |
|
27 |
|
28 import com.sun.javadoc.ClassDoc; |
|
29 import com.sun.javadoc.MethodDoc; |
|
30 import com.sun.javadoc.Type; |
|
31 import java.io.IOException; |
|
32 import java.util.ArrayList; |
|
33 import java.util.Iterator; |
|
34 import java.util.List; |
|
35 import sun.rmi.rmic.newrmic.BatchEnvironment; |
|
36 import sun.rmi.rmic.newrmic.IndentingWriter; |
|
37 |
|
38 import static sun.rmi.rmic.newrmic.Constants.*; |
|
39 import static sun.rmi.rmic.newrmic.jrmp.Constants.*; |
|
40 |
|
41 /** |
|
42 * Writes the source code for the stub class and (optionally) skeleton |
|
43 * class for a particular remote implementation class. |
|
44 * |
|
45 * WARNING: The contents of this source file are not part of any |
|
46 * supported API. Code that depends on them does so at its own risk: |
|
47 * they are subject to change or removal without notice. |
|
48 * |
|
49 * @author Peter Jones |
|
50 **/ |
|
51 class StubSkeletonWriter { |
|
52 |
|
53 /** rmic environment for this object */ |
|
54 private final BatchEnvironment env; |
|
55 |
|
56 /** the remote implementation class to generate code for */ |
|
57 private final RemoteClass remoteClass; |
|
58 |
|
59 /** version of the JRMP stub protocol to generate code for */ |
|
60 private final StubVersion version; |
|
61 |
|
62 /* |
|
63 * binary names of the stub and skeleton classes to generate for |
|
64 * the remote class |
|
65 */ |
|
66 private final String stubClassName; |
|
67 private final String skeletonClassName; |
|
68 |
|
69 /* package name and simple names of the stub and skeleton classes */ |
|
70 private final String packageName; |
|
71 private final String stubClassSimpleName; |
|
72 private final String skeletonClassSimpleName; |
|
73 |
|
74 /** remote methods of class, indexed by operation number */ |
|
75 private final RemoteClass.Method[] remoteMethods; |
|
76 |
|
77 /** |
|
78 * Names to use for the java.lang.reflect.Method static fields in |
|
79 * the generated stub class corresponding to each remote method. |
|
80 **/ |
|
81 private final String[] methodFieldNames; |
|
82 |
|
83 /** |
|
84 * Creates a StubSkeletonWriter instance for the specified remote |
|
85 * implementation class. The generated code will implement the |
|
86 * specified JRMP stub protocol version. |
|
87 **/ |
|
88 StubSkeletonWriter(BatchEnvironment env, |
|
89 RemoteClass remoteClass, |
|
90 StubVersion version) |
|
91 { |
|
92 this.env = env; |
|
93 this.remoteClass = remoteClass; |
|
94 this.version = version; |
|
95 |
|
96 stubClassName = Util.binaryNameOf(remoteClass.classDoc()) + "_Stub"; |
|
97 skeletonClassName = |
|
98 Util.binaryNameOf(remoteClass.classDoc()) + "_Skel"; |
|
99 |
|
100 int i = stubClassName.lastIndexOf('.'); |
|
101 packageName = (i != -1 ? stubClassName.substring(0, i) : ""); |
|
102 stubClassSimpleName = stubClassName.substring(i + 1); |
|
103 skeletonClassSimpleName = skeletonClassName.substring(i + 1); |
|
104 |
|
105 remoteMethods = remoteClass.remoteMethods(); |
|
106 methodFieldNames = nameMethodFields(remoteMethods); |
|
107 } |
|
108 |
|
109 /** |
|
110 * Returns the binary name of the stub class to generate for the |
|
111 * remote implementation class. |
|
112 **/ |
|
113 String stubClassName() { |
|
114 return stubClassName; |
|
115 } |
|
116 |
|
117 /** |
|
118 * Returns the binary name of the skeleton class to generate for |
|
119 * the remote implementation class. |
|
120 **/ |
|
121 String skeletonClassName() { |
|
122 return skeletonClassName; |
|
123 } |
|
124 |
|
125 /** |
|
126 * Writes the stub class for the remote class to a stream. |
|
127 **/ |
|
128 void writeStub(IndentingWriter p) throws IOException { |
|
129 |
|
130 /* |
|
131 * Write boiler plate comment. |
|
132 */ |
|
133 p.pln("// Stub class generated by rmic, do not edit."); |
|
134 p.pln("// Contents subject to change without notice."); |
|
135 p.pln(); |
|
136 |
|
137 /* |
|
138 * If remote implementation class was in a particular package, |
|
139 * declare the stub class to be in the same package. |
|
140 */ |
|
141 if (!packageName.equals("")) { |
|
142 p.pln("package " + packageName + ";"); |
|
143 p.pln(); |
|
144 } |
|
145 |
|
146 /* |
|
147 * Declare the stub class; implement all remote interfaces. |
|
148 */ |
|
149 p.plnI("public final class " + stubClassSimpleName); |
|
150 p.pln("extends " + REMOTE_STUB); |
|
151 ClassDoc[] remoteInterfaces = remoteClass.remoteInterfaces(); |
|
152 if (remoteInterfaces.length > 0) { |
|
153 p.p("implements "); |
|
154 for (int i = 0; i < remoteInterfaces.length; i++) { |
|
155 if (i > 0) { |
|
156 p.p(", "); |
|
157 } |
|
158 p.p(remoteInterfaces[i].qualifiedName()); |
|
159 } |
|
160 p.pln(); |
|
161 } |
|
162 p.pOlnI("{"); |
|
163 |
|
164 if (version == StubVersion.V1_1 || |
|
165 version == StubVersion.VCOMPAT) |
|
166 { |
|
167 writeOperationsArray(p); |
|
168 p.pln(); |
|
169 writeInterfaceHash(p); |
|
170 p.pln(); |
|
171 } |
|
172 |
|
173 if (version == StubVersion.VCOMPAT || |
|
174 version == StubVersion.V1_2) |
|
175 { |
|
176 p.pln("private static final long serialVersionUID = " + |
|
177 STUB_SERIAL_VERSION_UID + ";"); |
|
178 p.pln(); |
|
179 |
|
180 /* |
|
181 * We only need to declare and initialize the static fields of |
|
182 * Method objects for each remote method if there are any remote |
|
183 * methods; otherwise, skip this code entirely, to avoid generating |
|
184 * a try/catch block for a checked exception that cannot occur |
|
185 * (see bugid 4125181). |
|
186 */ |
|
187 if (methodFieldNames.length > 0) { |
|
188 if (version == StubVersion.VCOMPAT) { |
|
189 p.pln("private static boolean useNewInvoke;"); |
|
190 } |
|
191 writeMethodFieldDeclarations(p); |
|
192 p.pln(); |
|
193 |
|
194 /* |
|
195 * Initialize java.lang.reflect.Method fields for each remote |
|
196 * method in a static initializer. |
|
197 */ |
|
198 p.plnI("static {"); |
|
199 p.plnI("try {"); |
|
200 if (version == StubVersion.VCOMPAT) { |
|
201 /* |
|
202 * Fat stubs must determine whether the API required for |
|
203 * the JDK 1.2 stub protocol is supported in the current |
|
204 * runtime, so that it can use it if supported. This is |
|
205 * determined by using the Reflection API to test if the |
|
206 * new invoke method on RemoteRef exists, and setting the |
|
207 * static boolean "useNewInvoke" to true if it does, or |
|
208 * to false if a NoSuchMethodException is thrown. |
|
209 */ |
|
210 p.plnI(REMOTE_REF + ".class.getMethod(\"invoke\","); |
|
211 p.plnI("new java.lang.Class[] {"); |
|
212 p.pln(REMOTE + ".class,"); |
|
213 p.pln("java.lang.reflect.Method.class,"); |
|
214 p.pln("java.lang.Object[].class,"); |
|
215 p.pln("long.class"); |
|
216 p.pOln("});"); |
|
217 p.pO(); |
|
218 p.pln("useNewInvoke = true;"); |
|
219 } |
|
220 writeMethodFieldInitializers(p); |
|
221 p.pOlnI("} catch (java.lang.NoSuchMethodException e) {"); |
|
222 if (version == StubVersion.VCOMPAT) { |
|
223 p.pln("useNewInvoke = false;"); |
|
224 } else { |
|
225 p.plnI("throw new java.lang.NoSuchMethodError("); |
|
226 p.pln("\"stub class initialization failed\");"); |
|
227 p.pO(); |
|
228 } |
|
229 p.pOln("}"); // end try/catch block |
|
230 p.pOln("}"); // end static initializer |
|
231 p.pln(); |
|
232 } |
|
233 } |
|
234 |
|
235 writeStubConstructors(p); |
|
236 p.pln(); |
|
237 |
|
238 /* |
|
239 * Write each stub method. |
|
240 */ |
|
241 if (remoteMethods.length > 0) { |
|
242 p.pln("// methods from remote interfaces"); |
|
243 for (int i = 0; i < remoteMethods.length; ++i) { |
|
244 p.pln(); |
|
245 writeStubMethod(p, i); |
|
246 } |
|
247 } |
|
248 |
|
249 p.pOln("}"); // end stub class |
|
250 } |
|
251 |
|
252 /** |
|
253 * Writes the constructors for the stub class. |
|
254 **/ |
|
255 private void writeStubConstructors(IndentingWriter p) |
|
256 throws IOException |
|
257 { |
|
258 p.pln("// constructors"); |
|
259 |
|
260 /* |
|
261 * Only stubs compatible with the JDK 1.1 stub protocol need |
|
262 * a no-arg constructor; later versions use reflection to find |
|
263 * the constructor that directly takes a RemoteRef argument. |
|
264 */ |
|
265 if (version == StubVersion.V1_1 || |
|
266 version == StubVersion.VCOMPAT) |
|
267 { |
|
268 p.plnI("public " + stubClassSimpleName + "() {"); |
|
269 p.pln("super();"); |
|
270 p.pOln("}"); |
|
271 } |
|
272 |
|
273 p.plnI("public " + stubClassSimpleName + "(" + REMOTE_REF + " ref) {"); |
|
274 p.pln("super(ref);"); |
|
275 p.pOln("}"); |
|
276 } |
|
277 |
|
278 /** |
|
279 * Writes the stub method for the remote method with the given |
|
280 * operation number. |
|
281 **/ |
|
282 private void writeStubMethod(IndentingWriter p, int opnum) |
|
283 throws IOException |
|
284 { |
|
285 RemoteClass.Method method = remoteMethods[opnum]; |
|
286 MethodDoc methodDoc = method.methodDoc(); |
|
287 String methodName = methodDoc.name(); |
|
288 Type[] paramTypes = method.parameterTypes(); |
|
289 String paramNames[] = nameParameters(paramTypes); |
|
290 Type returnType = methodDoc.returnType(); |
|
291 ClassDoc[] exceptions = method.exceptionTypes(); |
|
292 |
|
293 /* |
|
294 * Declare stub method; throw exceptions declared in remote |
|
295 * interface(s). |
|
296 */ |
|
297 p.pln("// implementation of " + |
|
298 Util.getFriendlyUnqualifiedSignature(methodDoc)); |
|
299 p.p("public " + returnType.toString() + " " + methodName + "("); |
|
300 for (int i = 0; i < paramTypes.length; i++) { |
|
301 if (i > 0) { |
|
302 p.p(", "); |
|
303 } |
|
304 p.p(paramTypes[i].toString() + " " + paramNames[i]); |
|
305 } |
|
306 p.plnI(")"); |
|
307 if (exceptions.length > 0) { |
|
308 p.p("throws "); |
|
309 for (int i = 0; i < exceptions.length; i++) { |
|
310 if (i > 0) { |
|
311 p.p(", "); |
|
312 } |
|
313 p.p(exceptions[i].qualifiedName()); |
|
314 } |
|
315 p.pln(); |
|
316 } |
|
317 p.pOlnI("{"); |
|
318 |
|
319 /* |
|
320 * The RemoteRef.invoke methods throw Exception, but unless |
|
321 * this stub method throws Exception as well, we must catch |
|
322 * Exceptions thrown from the invocation. So we must catch |
|
323 * Exception and rethrow something we can throw: |
|
324 * UnexpectedException, which is a subclass of |
|
325 * RemoteException. But for any subclasses of Exception that |
|
326 * we can throw, like RemoteException, RuntimeException, and |
|
327 * any of the exceptions declared by this stub method, we want |
|
328 * them to pass through unmodified, so first we must catch any |
|
329 * such exceptions and rethrow them directly. |
|
330 * |
|
331 * We have to be careful generating the rethrowing catch |
|
332 * blocks here, because javac will flag an error if there are |
|
333 * any unreachable catch blocks, i.e. if the catch of an |
|
334 * exception class follows a previous catch of it or of one of |
|
335 * its superclasses. The following method invocation takes |
|
336 * care of these details. |
|
337 */ |
|
338 List<ClassDoc> catchList = computeUniqueCatchList(exceptions); |
|
339 |
|
340 /* |
|
341 * If we need to catch any particular exceptions (i.e. this method |
|
342 * does not declare java.lang.Exception), put the entire stub |
|
343 * method in a try block. |
|
344 */ |
|
345 if (catchList.size() > 0) { |
|
346 p.plnI("try {"); |
|
347 } |
|
348 |
|
349 if (version == StubVersion.VCOMPAT) { |
|
350 p.plnI("if (useNewInvoke) {"); |
|
351 } |
|
352 if (version == StubVersion.VCOMPAT || |
|
353 version == StubVersion.V1_2) |
|
354 { |
|
355 if (!Util.isVoid(returnType)) { |
|
356 p.p("Object $result = "); // REMIND: why $? |
|
357 } |
|
358 p.p("ref.invoke(this, " + methodFieldNames[opnum] + ", "); |
|
359 if (paramTypes.length > 0) { |
|
360 p.p("new java.lang.Object[] {"); |
|
361 for (int i = 0; i < paramTypes.length; i++) { |
|
362 if (i > 0) |
|
363 p.p(", "); |
|
364 p.p(wrapArgumentCode(paramTypes[i], paramNames[i])); |
|
365 } |
|
366 p.p("}"); |
|
367 } else { |
|
368 p.p("null"); |
|
369 } |
|
370 p.pln(", " + method.methodHash() + "L);"); |
|
371 if (!Util.isVoid(returnType)) { |
|
372 p.pln("return " + |
|
373 unwrapArgumentCode(returnType, "$result") + ";"); |
|
374 } |
|
375 } |
|
376 if (version == StubVersion.VCOMPAT) { |
|
377 p.pOlnI("} else {"); |
|
378 } |
|
379 if (version == StubVersion.V1_1 || |
|
380 version == StubVersion.VCOMPAT) |
|
381 { |
|
382 p.pln(REMOTE_CALL + " call = ref.newCall((" + REMOTE_OBJECT + |
|
383 ") this, operations, " + opnum + ", interfaceHash);"); |
|
384 |
|
385 if (paramTypes.length > 0) { |
|
386 p.plnI("try {"); |
|
387 p.pln("java.io.ObjectOutput out = call.getOutputStream();"); |
|
388 writeMarshalArguments(p, "out", paramTypes, paramNames); |
|
389 p.pOlnI("} catch (java.io.IOException e) {"); |
|
390 p.pln("throw new " + MARSHAL_EXCEPTION + |
|
391 "(\"error marshalling arguments\", e);"); |
|
392 p.pOln("}"); |
|
393 } |
|
394 |
|
395 p.pln("ref.invoke(call);"); |
|
396 |
|
397 if (Util.isVoid(returnType)) { |
|
398 p.pln("ref.done(call);"); |
|
399 } else { |
|
400 p.pln(returnType.toString() + " $result;"); |
|
401 // REMIND: why $? |
|
402 p.plnI("try {"); |
|
403 p.pln("java.io.ObjectInput in = call.getInputStream();"); |
|
404 boolean objectRead = |
|
405 writeUnmarshalArgument(p, "in", returnType, "$result"); |
|
406 p.pln(";"); |
|
407 p.pOlnI("} catch (java.io.IOException e) {"); |
|
408 p.pln("throw new " + UNMARSHAL_EXCEPTION + |
|
409 "(\"error unmarshalling return\", e);"); |
|
410 /* |
|
411 * If any only if readObject has been invoked, we must catch |
|
412 * ClassNotFoundException as well as IOException. |
|
413 */ |
|
414 if (objectRead) { |
|
415 p.pOlnI("} catch (java.lang.ClassNotFoundException e) {"); |
|
416 p.pln("throw new " + UNMARSHAL_EXCEPTION + |
|
417 "(\"error unmarshalling return\", e);"); |
|
418 } |
|
419 p.pOlnI("} finally {"); |
|
420 p.pln("ref.done(call);"); |
|
421 p.pOln("}"); |
|
422 p.pln("return $result;"); |
|
423 } |
|
424 } |
|
425 if (version == StubVersion.VCOMPAT) { |
|
426 p.pOln("}"); // end if/else (useNewInvoke) block |
|
427 } |
|
428 |
|
429 /* |
|
430 * If we need to catch any particular exceptions, finally write |
|
431 * the catch blocks for them, rethrow any other Exceptions with an |
|
432 * UnexpectedException, and end the try block. |
|
433 */ |
|
434 if (catchList.size() > 0) { |
|
435 for (ClassDoc catchClass : catchList) { |
|
436 p.pOlnI("} catch (" + catchClass.qualifiedName() + " e) {"); |
|
437 p.pln("throw e;"); |
|
438 } |
|
439 p.pOlnI("} catch (java.lang.Exception e) {"); |
|
440 p.pln("throw new " + UNEXPECTED_EXCEPTION + |
|
441 "(\"undeclared checked exception\", e);"); |
|
442 p.pOln("}"); // end try/catch block |
|
443 } |
|
444 |
|
445 p.pOln("}"); // end stub method |
|
446 } |
|
447 |
|
448 /** |
|
449 * Computes the exceptions that need to be caught and rethrown in |
|
450 * a stub method before wrapping Exceptions in |
|
451 * UnexpectedExceptions, given the exceptions declared in the |
|
452 * throws clause of the method. Returns a list containing the |
|
453 * exception to catch. Each exception is guaranteed to be unique, |
|
454 * i.e. not a subclass of any of the other exceptions in the list, |
|
455 * so the catch blocks for these exceptions may be generated in |
|
456 * any order relative to each other. |
|
457 * |
|
458 * RemoteException and RuntimeException are each automatically |
|
459 * placed in the returned list (unless any of their superclasses |
|
460 * are already present), since those exceptions should always be |
|
461 * directly rethrown by a stub method. |
|
462 * |
|
463 * The returned list will be empty if java.lang.Exception or one |
|
464 * of its superclasses is in the throws clause of the method, |
|
465 * indicating that no exceptions need to be caught. |
|
466 **/ |
|
467 private List<ClassDoc> computeUniqueCatchList(ClassDoc[] exceptions) { |
|
468 List<ClassDoc> uniqueList = new ArrayList<ClassDoc>(); |
|
469 |
|
470 uniqueList.add(env.docRuntimeException()); |
|
471 uniqueList.add(env.docRemoteException()); // always catch/rethrow these |
|
472 |
|
473 /* For each exception declared by the stub method's throws clause: */ |
|
474 nextException: |
|
475 for (ClassDoc ex : exceptions) { |
|
476 if (env.docException().subclassOf(ex)) { |
|
477 /* |
|
478 * If java.lang.Exception (or a superclass) was declared |
|
479 * in the throws clause of this stub method, then we don't |
|
480 * have to bother catching anything; clear the list and |
|
481 * return. |
|
482 */ |
|
483 uniqueList.clear(); |
|
484 break; |
|
485 } else if (!ex.subclassOf(env.docException())) { |
|
486 /* |
|
487 * Ignore other Throwables that do not extend Exception, |
|
488 * because they cannot be thrown by the invoke methods. |
|
489 */ |
|
490 continue; |
|
491 } |
|
492 /* |
|
493 * Compare this exception against the current list of |
|
494 * exceptions that need to be caught: |
|
495 */ |
|
496 for (Iterator<ClassDoc> i = uniqueList.iterator(); i.hasNext();) { |
|
497 ClassDoc ex2 = i.next(); |
|
498 if (ex.subclassOf(ex2)) { |
|
499 /* |
|
500 * If a superclass of this exception is already on |
|
501 * the list to catch, then ignore this one and continue; |
|
502 */ |
|
503 continue nextException; |
|
504 } else if (ex2.subclassOf(ex)) { |
|
505 /* |
|
506 * If a subclass of this exception is on the list |
|
507 * to catch, then remove it; |
|
508 */ |
|
509 i.remove(); |
|
510 } |
|
511 } |
|
512 /* This exception is unique: add it to the list to catch. */ |
|
513 uniqueList.add(ex); |
|
514 } |
|
515 return uniqueList; |
|
516 } |
|
517 |
|
518 /** |
|
519 * Writes the skeleton for the remote class to a stream. |
|
520 **/ |
|
521 void writeSkeleton(IndentingWriter p) throws IOException { |
|
522 if (version == StubVersion.V1_2) { |
|
523 throw new AssertionError( |
|
524 "should not generate skeleton for version " + version); |
|
525 } |
|
526 |
|
527 /* |
|
528 * Write boiler plate comment. |
|
529 */ |
|
530 p.pln("// Skeleton class generated by rmic, do not edit."); |
|
531 p.pln("// Contents subject to change without notice."); |
|
532 p.pln(); |
|
533 |
|
534 /* |
|
535 * If remote implementation class was in a particular package, |
|
536 * declare the skeleton class to be in the same package. |
|
537 */ |
|
538 if (!packageName.equals("")) { |
|
539 p.pln("package " + packageName + ";"); |
|
540 p.pln(); |
|
541 } |
|
542 |
|
543 /* |
|
544 * Declare the skeleton class. |
|
545 */ |
|
546 p.plnI("public final class " + skeletonClassSimpleName); |
|
547 p.pln("implements " + SKELETON); |
|
548 p.pOlnI("{"); |
|
549 |
|
550 writeOperationsArray(p); |
|
551 p.pln(); |
|
552 |
|
553 writeInterfaceHash(p); |
|
554 p.pln(); |
|
555 |
|
556 /* |
|
557 * Define the getOperations() method. |
|
558 */ |
|
559 p.plnI("public " + OPERATION + "[] getOperations() {"); |
|
560 p.pln("return (" + OPERATION + "[]) operations.clone();"); |
|
561 p.pOln("}"); |
|
562 p.pln(); |
|
563 |
|
564 /* |
|
565 * Define the dispatch() method. |
|
566 */ |
|
567 p.plnI("public void dispatch(" + REMOTE + " obj, " + |
|
568 REMOTE_CALL + " call, int opnum, long hash)"); |
|
569 p.pln("throws java.lang.Exception"); |
|
570 p.pOlnI("{"); |
|
571 |
|
572 if (version == StubVersion.VCOMPAT) { |
|
573 p.plnI("if (opnum < 0) {"); |
|
574 if (remoteMethods.length > 0) { |
|
575 for (int opnum = 0; opnum < remoteMethods.length; opnum++) { |
|
576 if (opnum > 0) |
|
577 p.pO("} else "); |
|
578 p.plnI("if (hash == " + |
|
579 remoteMethods[opnum].methodHash() + "L) {"); |
|
580 p.pln("opnum = " + opnum + ";"); |
|
581 } |
|
582 p.pOlnI("} else {"); |
|
583 } |
|
584 /* |
|
585 * Skeleton throws UnmarshalException if it does not recognize |
|
586 * the method hash; this is what UnicastServerRef.dispatch() |
|
587 * would do. |
|
588 */ |
|
589 p.pln("throw new " + |
|
590 UNMARSHAL_EXCEPTION + "(\"invalid method hash\");"); |
|
591 if (remoteMethods.length > 0) { |
|
592 p.pOln("}"); |
|
593 } |
|
594 /* |
|
595 * Ignore the validation of the interface hash if the |
|
596 * operation number was negative, since it is really a |
|
597 * method hash instead. |
|
598 */ |
|
599 p.pOlnI("} else {"); |
|
600 } |
|
601 |
|
602 p.plnI("if (hash != interfaceHash)"); |
|
603 p.pln("throw new " + |
|
604 SKELETON_MISMATCH_EXCEPTION + "(\"interface hash mismatch\");"); |
|
605 p.pO(); |
|
606 |
|
607 if (version == StubVersion.VCOMPAT) { |
|
608 p.pOln("}"); // end if/else (opnum < 0) block |
|
609 } |
|
610 p.pln(); |
|
611 |
|
612 /* |
|
613 * Cast remote object reference to the remote implementation |
|
614 * class, if it's not private. We don't use the binary name |
|
615 * of the class like previous implementations did because that |
|
616 * would not compile with javac (since 1.4.1). If the remote |
|
617 * implementation class is private, then we can't cast to it |
|
618 * like previous implementations did because that also would |
|
619 * not compile with javac-- so instead, we'll have to try to |
|
620 * cast to the remote interface for each remote method. |
|
621 */ |
|
622 if (!remoteClass.classDoc().isPrivate()) { |
|
623 p.pln(remoteClass.classDoc().qualifiedName() + " server = (" + |
|
624 remoteClass.classDoc().qualifiedName() + ") obj;"); |
|
625 } |
|
626 |
|
627 /* |
|
628 * Process call according to the operation number. |
|
629 */ |
|
630 p.plnI("switch (opnum) {"); |
|
631 for (int opnum = 0; opnum < remoteMethods.length; opnum++) { |
|
632 writeSkeletonDispatchCase(p, opnum); |
|
633 } |
|
634 p.pOlnI("default:"); |
|
635 /* |
|
636 * Skeleton throws UnmarshalException if it does not recognize |
|
637 * the operation number; this is consistent with the case of an |
|
638 * unrecognized method hash. |
|
639 */ |
|
640 p.pln("throw new " + UNMARSHAL_EXCEPTION + |
|
641 "(\"invalid method number\");"); |
|
642 p.pOln("}"); // end switch statement |
|
643 |
|
644 p.pOln("}"); // end dispatch() method |
|
645 |
|
646 p.pOln("}"); // end skeleton class |
|
647 } |
|
648 |
|
649 /** |
|
650 * Writes the case block for the skeleton's dispatch method for |
|
651 * the remote method with the given "opnum". |
|
652 **/ |
|
653 private void writeSkeletonDispatchCase(IndentingWriter p, int opnum) |
|
654 throws IOException |
|
655 { |
|
656 RemoteClass.Method method = remoteMethods[opnum]; |
|
657 MethodDoc methodDoc = method.methodDoc(); |
|
658 String methodName = methodDoc.name(); |
|
659 Type paramTypes[] = method.parameterTypes(); |
|
660 String paramNames[] = nameParameters(paramTypes); |
|
661 Type returnType = methodDoc.returnType(); |
|
662 |
|
663 p.pOlnI("case " + opnum + ": // " + |
|
664 Util.getFriendlyUnqualifiedSignature(methodDoc)); |
|
665 /* |
|
666 * Use nested block statement inside case to provide an independent |
|
667 * namespace for local variables used to unmarshal parameters for |
|
668 * this remote method. |
|
669 */ |
|
670 p.pOlnI("{"); |
|
671 |
|
672 if (paramTypes.length > 0) { |
|
673 /* |
|
674 * Declare local variables to hold arguments. |
|
675 */ |
|
676 for (int i = 0; i < paramTypes.length; i++) { |
|
677 p.pln(paramTypes[i].toString() + " " + paramNames[i] + ";"); |
|
678 } |
|
679 |
|
680 /* |
|
681 * Unmarshal arguments from call stream. |
|
682 */ |
|
683 p.plnI("try {"); |
|
684 p.pln("java.io.ObjectInput in = call.getInputStream();"); |
|
685 boolean objectsRead = writeUnmarshalArguments(p, "in", |
|
686 paramTypes, paramNames); |
|
687 p.pOlnI("} catch (java.io.IOException e) {"); |
|
688 p.pln("throw new " + UNMARSHAL_EXCEPTION + |
|
689 "(\"error unmarshalling arguments\", e);"); |
|
690 /* |
|
691 * If any only if readObject has been invoked, we must catch |
|
692 * ClassNotFoundException as well as IOException. |
|
693 */ |
|
694 if (objectsRead) { |
|
695 p.pOlnI("} catch (java.lang.ClassNotFoundException e) {"); |
|
696 p.pln("throw new " + UNMARSHAL_EXCEPTION + |
|
697 "(\"error unmarshalling arguments\", e);"); |
|
698 } |
|
699 p.pOlnI("} finally {"); |
|
700 p.pln("call.releaseInputStream();"); |
|
701 p.pOln("}"); |
|
702 } else { |
|
703 p.pln("call.releaseInputStream();"); |
|
704 } |
|
705 |
|
706 if (!Util.isVoid(returnType)) { |
|
707 /* |
|
708 * Declare variable to hold return type, if not void. |
|
709 */ |
|
710 p.p(returnType.toString() + " $result = "); |
|
711 // REMIND: why $? |
|
712 } |
|
713 |
|
714 /* |
|
715 * Invoke the method on the server object. If the remote |
|
716 * implementation class is private, then we don't have a |
|
717 * reference cast to it, and so we try to cast to the remote |
|
718 * object reference to the method's declaring interface here. |
|
719 */ |
|
720 String target = remoteClass.classDoc().isPrivate() ? |
|
721 "((" + methodDoc.containingClass().qualifiedName() + ") obj)" : |
|
722 "server"; |
|
723 p.p(target + "." + methodName + "("); |
|
724 for (int i = 0; i < paramNames.length; i++) { |
|
725 if (i > 0) |
|
726 p.p(", "); |
|
727 p.p(paramNames[i]); |
|
728 } |
|
729 p.pln(");"); |
|
730 |
|
731 /* |
|
732 * Always invoke getResultStream(true) on the call object to send |
|
733 * the indication of a successful invocation to the caller. If |
|
734 * the return type is not void, keep the result stream and marshal |
|
735 * the return value. |
|
736 */ |
|
737 p.plnI("try {"); |
|
738 if (!Util.isVoid(returnType)) { |
|
739 p.p("java.io.ObjectOutput out = "); |
|
740 } |
|
741 p.pln("call.getResultStream(true);"); |
|
742 if (!Util.isVoid(returnType)) { |
|
743 writeMarshalArgument(p, "out", returnType, "$result"); |
|
744 p.pln(";"); |
|
745 } |
|
746 p.pOlnI("} catch (java.io.IOException e) {"); |
|
747 p.pln("throw new " + |
|
748 MARSHAL_EXCEPTION + "(\"error marshalling return\", e);"); |
|
749 p.pOln("}"); |
|
750 |
|
751 p.pln("break;"); // break from switch statement |
|
752 |
|
753 p.pOlnI("}"); // end nested block statement |
|
754 p.pln(); |
|
755 } |
|
756 |
|
757 /** |
|
758 * Writes declaration and initializer for "operations" static array. |
|
759 **/ |
|
760 private void writeOperationsArray(IndentingWriter p) |
|
761 throws IOException |
|
762 { |
|
763 p.plnI("private static final " + OPERATION + "[] operations = {"); |
|
764 for (int i = 0; i < remoteMethods.length; i++) { |
|
765 if (i > 0) |
|
766 p.pln(","); |
|
767 p.p("new " + OPERATION + "(\"" + |
|
768 remoteMethods[i].operationString() + "\")"); |
|
769 } |
|
770 p.pln(); |
|
771 p.pOln("};"); |
|
772 } |
|
773 |
|
774 /** |
|
775 * Writes declaration and initializer for "interfaceHash" static field. |
|
776 **/ |
|
777 private void writeInterfaceHash(IndentingWriter p) |
|
778 throws IOException |
|
779 { |
|
780 p.pln("private static final long interfaceHash = " + |
|
781 remoteClass.interfaceHash() + "L;"); |
|
782 } |
|
783 |
|
784 /** |
|
785 * Writes declaration for java.lang.reflect.Method static fields |
|
786 * corresponding to each remote method in a stub. |
|
787 **/ |
|
788 private void writeMethodFieldDeclarations(IndentingWriter p) |
|
789 throws IOException |
|
790 { |
|
791 for (String name : methodFieldNames) { |
|
792 p.pln("private static java.lang.reflect.Method " + name + ";"); |
|
793 } |
|
794 } |
|
795 |
|
796 /** |
|
797 * Writes code to initialize the static fields for each method |
|
798 * using the Java Reflection API. |
|
799 **/ |
|
800 private void writeMethodFieldInitializers(IndentingWriter p) |
|
801 throws IOException |
|
802 { |
|
803 for (int i = 0; i < methodFieldNames.length; i++) { |
|
804 p.p(methodFieldNames[i] + " = "); |
|
805 /* |
|
806 * Look up the Method object in the somewhat arbitrary |
|
807 * interface that we find in the Method object. |
|
808 */ |
|
809 RemoteClass.Method method = remoteMethods[i]; |
|
810 MethodDoc methodDoc = method.methodDoc(); |
|
811 String methodName = methodDoc.name(); |
|
812 Type paramTypes[] = method.parameterTypes(); |
|
813 |
|
814 p.p(methodDoc.containingClass().qualifiedName() + ".class.getMethod(\"" + |
|
815 methodName + "\", new java.lang.Class[] {"); |
|
816 for (int j = 0; j < paramTypes.length; j++) { |
|
817 if (j > 0) |
|
818 p.p(", "); |
|
819 p.p(paramTypes[j].toString() + ".class"); |
|
820 } |
|
821 p.pln("});"); |
|
822 } |
|
823 } |
|
824 |
|
825 |
|
826 /* |
|
827 * Following are a series of static utility methods useful during |
|
828 * the code generation process: |
|
829 */ |
|
830 |
|
831 /** |
|
832 * Generates an array of names for fields correspondins to the |
|
833 * given array of remote methods. Each name in the returned array |
|
834 * is guaranteed to be unique. |
|
835 * |
|
836 * The name of a method is included in its corresponding field |
|
837 * name to enhance readability of the generated code. |
|
838 **/ |
|
839 private static String[] nameMethodFields(RemoteClass.Method[] methods) { |
|
840 String[] names = new String[methods.length]; |
|
841 for (int i = 0; i < names.length; i++) { |
|
842 names[i] = "$method_" + methods[i].methodDoc().name() + "_" + i; |
|
843 } |
|
844 return names; |
|
845 } |
|
846 |
|
847 /** |
|
848 * Generates an array of names for parameters corresponding to the |
|
849 * given array of types for the parameters. Each name in the |
|
850 * returned array is guaranteed to be unique. |
|
851 * |
|
852 * A representation of the type of a parameter is included in its |
|
853 * corresponding parameter name to enhance the readability of the |
|
854 * generated code. |
|
855 **/ |
|
856 private static String[] nameParameters(Type[] types) { |
|
857 String[] names = new String[types.length]; |
|
858 for (int i = 0; i < names.length; i++) { |
|
859 names[i] = "$param_" + |
|
860 generateNameFromType(types[i]) + "_" + (i + 1); |
|
861 } |
|
862 return names; |
|
863 } |
|
864 |
|
865 /** |
|
866 * Generates a readable string representing the given type |
|
867 * suitable for embedding within a Java identifier. |
|
868 **/ |
|
869 private static String generateNameFromType(Type type) { |
|
870 String name = type.typeName().replace('.', '$'); |
|
871 int dimensions = type.dimension().length() / 2; |
|
872 for (int i = 0; i < dimensions; i++) { |
|
873 name = "arrayOf_" + name; |
|
874 } |
|
875 return name; |
|
876 } |
|
877 |
|
878 /** |
|
879 * Writes a snippet of Java code to marshal a value named "name" |
|
880 * of type "type" to the java.io.ObjectOutput stream named |
|
881 * "stream". |
|
882 * |
|
883 * Primitive types are marshalled with their corresponding methods |
|
884 * in the java.io.DataOutput interface, and objects (including |
|
885 * arrays) are marshalled using the writeObject method. |
|
886 **/ |
|
887 private static void writeMarshalArgument(IndentingWriter p, |
|
888 String streamName, |
|
889 Type type, String name) |
|
890 throws IOException |
|
891 { |
|
892 if (type.dimension().length() > 0 || type.asClassDoc() != null) { |
|
893 p.p(streamName + ".writeObject(" + name + ")"); |
|
894 } else if (type.typeName().equals("boolean")) { |
|
895 p.p(streamName + ".writeBoolean(" + name + ")"); |
|
896 } else if (type.typeName().equals("byte")) { |
|
897 p.p(streamName + ".writeByte(" + name + ")"); |
|
898 } else if (type.typeName().equals("char")) { |
|
899 p.p(streamName + ".writeChar(" + name + ")"); |
|
900 } else if (type.typeName().equals("short")) { |
|
901 p.p(streamName + ".writeShort(" + name + ")"); |
|
902 } else if (type.typeName().equals("int")) { |
|
903 p.p(streamName + ".writeInt(" + name + ")"); |
|
904 } else if (type.typeName().equals("long")) { |
|
905 p.p(streamName + ".writeLong(" + name + ")"); |
|
906 } else if (type.typeName().equals("float")) { |
|
907 p.p(streamName + ".writeFloat(" + name + ")"); |
|
908 } else if (type.typeName().equals("double")) { |
|
909 p.p(streamName + ".writeDouble(" + name + ")"); |
|
910 } else { |
|
911 throw new AssertionError(type); |
|
912 } |
|
913 } |
|
914 |
|
915 /** |
|
916 * Writes Java statements to marshal a series of values in order |
|
917 * as named in the "names" array, with types as specified in the |
|
918 * "types" array, to the java.io.ObjectOutput stream named |
|
919 * "stream". |
|
920 **/ |
|
921 private static void writeMarshalArguments(IndentingWriter p, |
|
922 String streamName, |
|
923 Type[] types, String[] names) |
|
924 throws IOException |
|
925 { |
|
926 assert types.length == names.length; |
|
927 |
|
928 for (int i = 0; i < types.length; i++) { |
|
929 writeMarshalArgument(p, streamName, types[i], names[i]); |
|
930 p.pln(";"); |
|
931 } |
|
932 } |
|
933 |
|
934 /** |
|
935 * Writes a snippet of Java code to unmarshal a value of type |
|
936 * "type" from the java.io.ObjectInput stream named "stream" into |
|
937 * a variable named "name" (if "name" is null, the value is |
|
938 * unmarshalled and discarded). |
|
939 * |
|
940 * Primitive types are unmarshalled with their corresponding |
|
941 * methods in the java.io.DataInput interface, and objects |
|
942 * (including arrays) are unmarshalled using the readObject |
|
943 * method. |
|
944 * |
|
945 * Returns true if code to invoke readObject was written, and |
|
946 * false otherwise. |
|
947 **/ |
|
948 private static boolean writeUnmarshalArgument(IndentingWriter p, |
|
949 String streamName, |
|
950 Type type, String name) |
|
951 throws IOException |
|
952 { |
|
953 boolean readObject = false; |
|
954 |
|
955 if (name != null) { |
|
956 p.p(name + " = "); |
|
957 } |
|
958 |
|
959 if (type.dimension().length() > 0 || type.asClassDoc() != null) { |
|
960 p.p("(" + type.toString() + ") " + streamName + ".readObject()"); |
|
961 readObject = true; |
|
962 } else if (type.typeName().equals("boolean")) { |
|
963 p.p(streamName + ".readBoolean()"); |
|
964 } else if (type.typeName().equals("byte")) { |
|
965 p.p(streamName + ".readByte()"); |
|
966 } else if (type.typeName().equals("char")) { |
|
967 p.p(streamName + ".readChar()"); |
|
968 } else if (type.typeName().equals("short")) { |
|
969 p.p(streamName + ".readShort()"); |
|
970 } else if (type.typeName().equals("int")) { |
|
971 p.p(streamName + ".readInt()"); |
|
972 } else if (type.typeName().equals("long")) { |
|
973 p.p(streamName + ".readLong()"); |
|
974 } else if (type.typeName().equals("float")) { |
|
975 p.p(streamName + ".readFloat()"); |
|
976 } else if (type.typeName().equals("double")) { |
|
977 p.p(streamName + ".readDouble()"); |
|
978 } else { |
|
979 throw new AssertionError(type); |
|
980 } |
|
981 |
|
982 return readObject; |
|
983 } |
|
984 |
|
985 /** |
|
986 * Writes Java statements to unmarshal a series of values in order |
|
987 * of types as in the "types" array from the java.io.ObjectInput |
|
988 * stream named "stream" into variables as named in "names" (for |
|
989 * any element of "names" that is null, the corresponding value is |
|
990 * unmarshalled and discarded). |
|
991 **/ |
|
992 private static boolean writeUnmarshalArguments(IndentingWriter p, |
|
993 String streamName, |
|
994 Type[] types, |
|
995 String[] names) |
|
996 throws IOException |
|
997 { |
|
998 assert types.length == names.length; |
|
999 |
|
1000 boolean readObject = false; |
|
1001 for (int i = 0; i < types.length; i++) { |
|
1002 if (writeUnmarshalArgument(p, streamName, types[i], names[i])) { |
|
1003 readObject = true; |
|
1004 } |
|
1005 p.pln(";"); |
|
1006 } |
|
1007 return readObject; |
|
1008 } |
|
1009 |
|
1010 /** |
|
1011 * Returns a snippet of Java code to wrap a value named "name" of |
|
1012 * type "type" into an object as appropriate for use by the Java |
|
1013 * Reflection API. |
|
1014 * |
|
1015 * For primitive types, an appropriate wrapper class is |
|
1016 * instantiated with the primitive value. For object types |
|
1017 * (including arrays), no wrapping is necessary, so the value is |
|
1018 * named directly. |
|
1019 **/ |
|
1020 private static String wrapArgumentCode(Type type, String name) { |
|
1021 if (type.dimension().length() > 0 || type.asClassDoc() != null) { |
|
1022 return name; |
|
1023 } else if (type.typeName().equals("boolean")) { |
|
1024 return ("(" + name + |
|
1025 " ? java.lang.Boolean.TRUE : java.lang.Boolean.FALSE)"); |
|
1026 } else if (type.typeName().equals("byte")) { |
|
1027 return "new java.lang.Byte(" + name + ")"; |
|
1028 } else if (type.typeName().equals("char")) { |
|
1029 return "new java.lang.Character(" + name + ")"; |
|
1030 } else if (type.typeName().equals("short")) { |
|
1031 return "new java.lang.Short(" + name + ")"; |
|
1032 } else if (type.typeName().equals("int")) { |
|
1033 return "new java.lang.Integer(" + name + ")"; |
|
1034 } else if (type.typeName().equals("long")) { |
|
1035 return "new java.lang.Long(" + name + ")"; |
|
1036 } else if (type.typeName().equals("float")) { |
|
1037 return "new java.lang.Float(" + name + ")"; |
|
1038 } else if (type.typeName().equals("double")) { |
|
1039 return "new java.lang.Double(" + name + ")"; |
|
1040 } else { |
|
1041 throw new AssertionError(type); |
|
1042 } |
|
1043 } |
|
1044 |
|
1045 /** |
|
1046 * Returns a snippet of Java code to unwrap a value named "name" |
|
1047 * into a value of type "type", as appropriate for the Java |
|
1048 * Reflection API. |
|
1049 * |
|
1050 * For primitive types, the value is assumed to be of the |
|
1051 * corresponding wrapper class, and a method is called on the |
|
1052 * wrapper to retrieve the primitive value. For object types |
|
1053 * (include arrays), no unwrapping is necessary; the value is |
|
1054 * simply cast to the expected real object type. |
|
1055 **/ |
|
1056 private static String unwrapArgumentCode(Type type, String name) { |
|
1057 if (type.dimension().length() > 0 || type.asClassDoc() != null) { |
|
1058 return "((" + type.toString() + ") " + name + ")"; |
|
1059 } else if (type.typeName().equals("boolean")) { |
|
1060 return "((java.lang.Boolean) " + name + ").booleanValue()"; |
|
1061 } else if (type.typeName().equals("byte")) { |
|
1062 return "((java.lang.Byte) " + name + ").byteValue()"; |
|
1063 } else if (type.typeName().equals("char")) { |
|
1064 return "((java.lang.Character) " + name + ").charValue()"; |
|
1065 } else if (type.typeName().equals("short")) { |
|
1066 return "((java.lang.Short) " + name + ").shortValue()"; |
|
1067 } else if (type.typeName().equals("int")) { |
|
1068 return "((java.lang.Integer) " + name + ").intValue()"; |
|
1069 } else if (type.typeName().equals("long")) { |
|
1070 return "((java.lang.Long) " + name + ").longValue()"; |
|
1071 } else if (type.typeName().equals("float")) { |
|
1072 return "((java.lang.Float) " + name + ").floatValue()"; |
|
1073 } else if (type.typeName().equals("double")) { |
|
1074 return "((java.lang.Double) " + name + ").doubleValue()"; |
|
1075 } else { |
|
1076 throw new AssertionError(type); |
|
1077 } |
|
1078 } |
|
1079 } |
|