/*
* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.rmi.rmic.newrmic.jrmp;
import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.MethodDoc;
import com.sun.javadoc.Type;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import sun.rmi.rmic.newrmic.BatchEnvironment;
import sun.rmi.rmic.newrmic.IndentingWriter;
import static sun.rmi.rmic.newrmic.Constants.*;
import static sun.rmi.rmic.newrmic.jrmp.Constants.*;
Writes the source code for the stub class and (optionally) skeleton
class for a particular remote implementation class.
WARNING: The contents of this source file are not part of any
supported API. Code that depends on them does so at its own risk:
they are subject to change or removal without notice.
Author: Peter Jones
/**
* Writes the source code for the stub class and (optionally) skeleton
* class for a particular remote implementation class.
*
* WARNING: The contents of this source file are not part of any
* supported API. Code that depends on them does so at its own risk:
* they are subject to change or removal without notice.
*
* @author Peter Jones
**/
class StubSkeletonWriter {
rmic environment for this object /** rmic environment for this object */
private final BatchEnvironment env;
the remote implementation class to generate code for /** the remote implementation class to generate code for */
private final RemoteClass remoteClass;
version of the JRMP stub protocol to generate code for /** version of the JRMP stub protocol to generate code for */
private final StubVersion version;
/*
* binary names of the stub and skeleton classes to generate for
* the remote class
*/
private final String stubClassName;
private final String skeletonClassName;
/* package name and simple names of the stub and skeleton classes */
private final String packageName;
private final String stubClassSimpleName;
private final String skeletonClassSimpleName;
remote methods of class, indexed by operation number /** remote methods of class, indexed by operation number */
private final RemoteClass.Method[] remoteMethods;
Names to use for the java.lang.reflect.Method static fields in
the generated stub class corresponding to each remote method.
/**
* Names to use for the java.lang.reflect.Method static fields in
* the generated stub class corresponding to each remote method.
**/
private final String[] methodFieldNames;
Creates a StubSkeletonWriter instance for the specified remote
implementation class. The generated code will implement the
specified JRMP stub protocol version.
/**
* Creates a StubSkeletonWriter instance for the specified remote
* implementation class. The generated code will implement the
* specified JRMP stub protocol version.
**/
StubSkeletonWriter(BatchEnvironment env,
RemoteClass remoteClass,
StubVersion version)
{
this.env = env;
this.remoteClass = remoteClass;
this.version = version;
stubClassName = Util.binaryNameOf(remoteClass.classDoc()) + "_Stub";
skeletonClassName =
Util.binaryNameOf(remoteClass.classDoc()) + "_Skel";
int i = stubClassName.lastIndexOf('.');
packageName = (i != -1 ? stubClassName.substring(0, i) : "");
stubClassSimpleName = stubClassName.substring(i + 1);
skeletonClassSimpleName = skeletonClassName.substring(i + 1);
remoteMethods = remoteClass.remoteMethods();
methodFieldNames = nameMethodFields(remoteMethods);
}
Returns the binary name of the stub class to generate for the
remote implementation class.
/**
* Returns the binary name of the stub class to generate for the
* remote implementation class.
**/
String stubClassName() {
return stubClassName;
}
Returns the binary name of the skeleton class to generate for
the remote implementation class.
/**
* Returns the binary name of the skeleton class to generate for
* the remote implementation class.
**/
String skeletonClassName() {
return skeletonClassName;
}
Writes the stub class for the remote class to a stream.
/**
* Writes the stub class for the remote class to a stream.
**/
void writeStub(IndentingWriter p) throws IOException {
/*
* Write boiler plate comment.
*/
p.pln("// Stub class generated by rmic, do not edit.");
p.pln("// Contents subject to change without notice.");
p.pln();
/*
* If remote implementation class was in a particular package,
* declare the stub class to be in the same package.
*/
if (!packageName.equals("")) {
p.pln("package " + packageName + ";");
p.pln();
}
/*
* Declare the stub class; implement all remote interfaces.
*/
p.plnI("public final class " + stubClassSimpleName);
p.pln("extends " + REMOTE_STUB);
ClassDoc[] remoteInterfaces = remoteClass.remoteInterfaces();
if (remoteInterfaces.length > 0) {
p.p("implements ");
for (int i = 0; i < remoteInterfaces.length; i++) {
if (i > 0) {
p.p(", ");
}
p.p(remoteInterfaces[i].qualifiedName());
}
p.pln();
}
p.pOlnI("{");
if (version == StubVersion.V1_1 ||
version == StubVersion.VCOMPAT)
{
writeOperationsArray(p);
p.pln();
writeInterfaceHash(p);
p.pln();
}
if (version == StubVersion.VCOMPAT ||
version == StubVersion.V1_2)
{
p.pln("private static final long serialVersionUID = " +
STUB_SERIAL_VERSION_UID + ";");
p.pln();
/*
* We only need to declare and initialize the static fields of
* Method objects for each remote method if there are any remote
* methods; otherwise, skip this code entirely, to avoid generating
* a try/catch block for a checked exception that cannot occur
* (see bugid 4125181).
*/
if (methodFieldNames.length > 0) {
if (version == StubVersion.VCOMPAT) {
p.pln("private static boolean useNewInvoke;");
}
writeMethodFieldDeclarations(p);
p.pln();
/*
* Initialize java.lang.reflect.Method fields for each remote
* method in a static initializer.
*/
p.plnI("static {");
p.plnI("try {");
if (version == StubVersion.VCOMPAT) {
/*
* Fat stubs must determine whether the API required for
* the JDK 1.2 stub protocol is supported in the current
* runtime, so that it can use it if supported. This is
* determined by using the Reflection API to test if the
* new invoke method on RemoteRef exists, and setting the
* static boolean "useNewInvoke" to true if it does, or
* to false if a NoSuchMethodException is thrown.
*/
p.plnI(REMOTE_REF + ".class.getMethod(\"invoke\",");
p.plnI("new java.lang.Class[] {");
p.pln(REMOTE + ".class,");
p.pln("java.lang.reflect.Method.class,");
p.pln("java.lang.Object[].class,");
p.pln("long.class");
p.pOln("});");
p.pO();
p.pln("useNewInvoke = true;");
}
writeMethodFieldInitializers(p);
p.pOlnI("} catch (java.lang.NoSuchMethodException e) {");
if (version == StubVersion.VCOMPAT) {
p.pln("useNewInvoke = false;");
} else {
p.plnI("throw new java.lang.NoSuchMethodError(");
p.pln("\"stub class initialization failed\");");
p.pO();
}
p.pOln("}"); // end try/catch block
p.pOln("}"); // end static initializer
p.pln();
}
}
writeStubConstructors(p);
p.pln();
/*
* Write each stub method.
*/
if (remoteMethods.length > 0) {
p.pln("// methods from remote interfaces");
for (int i = 0; i < remoteMethods.length; ++i) {
p.pln();
writeStubMethod(p, i);
}
}
p.pOln("}"); // end stub class
}
Writes the constructors for the stub class.
/**
* Writes the constructors for the stub class.
**/
private void writeStubConstructors(IndentingWriter p)
throws IOException
{
p.pln("// constructors");
/*
* Only stubs compatible with the JDK 1.1 stub protocol need
* a no-arg constructor; later versions use reflection to find
* the constructor that directly takes a RemoteRef argument.
*/
if (version == StubVersion.V1_1 ||
version == StubVersion.VCOMPAT)
{
p.plnI("public " + stubClassSimpleName + "() {");
p.pln("super();");
p.pOln("}");
}
p.plnI("public " + stubClassSimpleName + "(" + REMOTE_REF + " ref) {");
p.pln("super(ref);");
p.pOln("}");
}
Writes the stub method for the remote method with the given
operation number.
/**
* Writes the stub method for the remote method with the given
* operation number.
**/
private void writeStubMethod(IndentingWriter p, int opnum)
throws IOException
{
RemoteClass.Method method = remoteMethods[opnum];
MethodDoc methodDoc = method.methodDoc();
String methodName = methodDoc.name();
Type[] paramTypes = method.parameterTypes();
String paramNames[] = nameParameters(paramTypes);
Type returnType = methodDoc.returnType();
ClassDoc[] exceptions = method.exceptionTypes();
/*
* Declare stub method; throw exceptions declared in remote
* interface(s).
*/
p.pln("// implementation of " +
Util.getFriendlyUnqualifiedSignature(methodDoc));
p.p("public " + returnType.toString() + " " + methodName + "(");
for (int i = 0; i < paramTypes.length; i++) {
if (i > 0) {
p.p(", ");
}
p.p(paramTypes[i].toString() + " " + paramNames[i]);
}
p.plnI(")");
if (exceptions.length > 0) {
p.p("throws ");
for (int i = 0; i < exceptions.length; i++) {
if (i > 0) {
p.p(", ");
}
p.p(exceptions[i].qualifiedName());
}
p.pln();
}
p.pOlnI("{");
/*
* The RemoteRef.invoke methods throw Exception, but unless
* this stub method throws Exception as well, we must catch
* Exceptions thrown from the invocation. So we must catch
* Exception and rethrow something we can throw:
* UnexpectedException, which is a subclass of
* RemoteException. But for any subclasses of Exception that
* we can throw, like RemoteException, RuntimeException, and
* any of the exceptions declared by this stub method, we want
* them to pass through unmodified, so first we must catch any
* such exceptions and rethrow them directly.
*
* We have to be careful generating the rethrowing catch
* blocks here, because javac will flag an error if there are
* any unreachable catch blocks, i.e. if the catch of an
* exception class follows a previous catch of it or of one of
* its superclasses. The following method invocation takes
* care of these details.
*/
List<ClassDoc> catchList = computeUniqueCatchList(exceptions);
/*
* If we need to catch any particular exceptions (i.e. this method
* does not declare java.lang.Exception), put the entire stub
* method in a try block.
*/
if (catchList.size() > 0) {
p.plnI("try {");
}
if (version == StubVersion.VCOMPAT) {
p.plnI("if (useNewInvoke) {");
}
if (version == StubVersion.VCOMPAT ||
version == StubVersion.V1_2)
{
if (!Util.isVoid(returnType)) {
p.p("Object $result = "); // REMIND: why $?
}
p.p("ref.invoke(this, " + methodFieldNames[opnum] + ", ");
if (paramTypes.length > 0) {
p.p("new java.lang.Object[] {");
for (int i = 0; i < paramTypes.length; i++) {
if (i > 0)
p.p(", ");
p.p(wrapArgumentCode(paramTypes[i], paramNames[i]));
}
p.p("}");
} else {
p.p("null");
}
p.pln(", " + method.methodHash() + "L);");
if (!Util.isVoid(returnType)) {
p.pln("return " +
unwrapArgumentCode(returnType, "$result") + ";");
}
}
if (version == StubVersion.VCOMPAT) {
p.pOlnI("} else {");
}
if (version == StubVersion.V1_1 ||
version == StubVersion.VCOMPAT)
{
p.pln(REMOTE_CALL + " call = ref.newCall((" + REMOTE_OBJECT +
") this, operations, " + opnum + ", interfaceHash);");
if (paramTypes.length > 0) {
p.plnI("try {");
p.pln("java.io.ObjectOutput out = call.getOutputStream();");
writeMarshalArguments(p, "out", paramTypes, paramNames);
p.pOlnI("} catch (java.io.IOException e) {");
p.pln("throw new " + MARSHAL_EXCEPTION +
"(\"error marshalling arguments\", e);");
p.pOln("}");
}
p.pln("ref.invoke(call);");
if (Util.isVoid(returnType)) {
p.pln("ref.done(call);");
} else {
p.pln(returnType.toString() + " $result;");
// REMIND: why $?
p.plnI("try {");
p.pln("java.io.ObjectInput in = call.getInputStream();");
boolean objectRead =
writeUnmarshalArgument(p, "in", returnType, "$result");
p.pln(";");
p.pOlnI("} catch (java.io.IOException e) {");
p.pln("throw new " + UNMARSHAL_EXCEPTION +
"(\"error unmarshalling return\", e);");
/*
* If any only if readObject has been invoked, we must catch
* ClassNotFoundException as well as IOException.
*/
if (objectRead) {
p.pOlnI("} catch (java.lang.ClassNotFoundException e) {");
p.pln("throw new " + UNMARSHAL_EXCEPTION +
"(\"error unmarshalling return\", e);");
}
p.pOlnI("} finally {");
p.pln("ref.done(call);");
p.pOln("}");
p.pln("return $result;");
}
}
if (version == StubVersion.VCOMPAT) {
p.pOln("}"); // end if/else (useNewInvoke) block
}
/*
* If we need to catch any particular exceptions, finally write
* the catch blocks for them, rethrow any other Exceptions with an
* UnexpectedException, and end the try block.
*/
if (catchList.size() > 0) {
for (ClassDoc catchClass : catchList) {
p.pOlnI("} catch (" + catchClass.qualifiedName() + " e) {");
p.pln("throw e;");
}
p.pOlnI("} catch (java.lang.Exception e) {");
p.pln("throw new " + UNEXPECTED_EXCEPTION +
"(\"undeclared checked exception\", e);");
p.pOln("}"); // end try/catch block
}
p.pOln("}"); // end stub method
}
Computes the exceptions that need to be caught and rethrown in
a stub method before wrapping Exceptions in
UnexpectedExceptions, given the exceptions declared in the
throws clause of the method. Returns a list containing the
exception to catch. Each exception is guaranteed to be unique,
i.e. not a subclass of any of the other exceptions in the list,
so the catch blocks for these exceptions may be generated in
any order relative to each other.
RemoteException and RuntimeException are each automatically
placed in the returned list (unless any of their superclasses
are already present), since those exceptions should always be
directly rethrown by a stub method.
The returned list will be empty if java.lang.Exception or one
of its superclasses is in the throws clause of the method,
indicating that no exceptions need to be caught.
/**
* Computes the exceptions that need to be caught and rethrown in
* a stub method before wrapping Exceptions in
* UnexpectedExceptions, given the exceptions declared in the
* throws clause of the method. Returns a list containing the
* exception to catch. Each exception is guaranteed to be unique,
* i.e. not a subclass of any of the other exceptions in the list,
* so the catch blocks for these exceptions may be generated in
* any order relative to each other.
*
* RemoteException and RuntimeException are each automatically
* placed in the returned list (unless any of their superclasses
* are already present), since those exceptions should always be
* directly rethrown by a stub method.
*
* The returned list will be empty if java.lang.Exception or one
* of its superclasses is in the throws clause of the method,
* indicating that no exceptions need to be caught.
**/
private List<ClassDoc> computeUniqueCatchList(ClassDoc[] exceptions) {
List<ClassDoc> uniqueList = new ArrayList<ClassDoc>();
uniqueList.add(env.docRuntimeException());
uniqueList.add(env.docRemoteException()); // always catch/rethrow these
/* For each exception declared by the stub method's throws clause: */
nextException:
for (ClassDoc ex : exceptions) {
if (env.docException().subclassOf(ex)) {
/*
* If java.lang.Exception (or a superclass) was declared
* in the throws clause of this stub method, then we don't
* have to bother catching anything; clear the list and
* return.
*/
uniqueList.clear();
break;
} else if (!ex.subclassOf(env.docException())) {
/*
* Ignore other Throwables that do not extend Exception,
* because they cannot be thrown by the invoke methods.
*/
continue;
}
/*
* Compare this exception against the current list of
* exceptions that need to be caught:
*/
for (Iterator<ClassDoc> i = uniqueList.iterator(); i.hasNext();) {
ClassDoc ex2 = i.next();
if (ex.subclassOf(ex2)) {
/*
* If a superclass of this exception is already on
* the list to catch, then ignore this one and continue;
*/
continue nextException;
} else if (ex2.subclassOf(ex)) {
/*
* If a subclass of this exception is on the list
* to catch, then remove it;
*/
i.remove();
}
}
/* This exception is unique: add it to the list to catch. */
uniqueList.add(ex);
}
return uniqueList;
}
Writes the skeleton for the remote class to a stream.
/**
* Writes the skeleton for the remote class to a stream.
**/
void writeSkeleton(IndentingWriter p) throws IOException {
if (version == StubVersion.V1_2) {
throw new AssertionError(
"should not generate skeleton for version " + version);
}
/*
* Write boiler plate comment.
*/
p.pln("// Skeleton class generated by rmic, do not edit.");
p.pln("// Contents subject to change without notice.");
p.pln();
/*
* If remote implementation class was in a particular package,
* declare the skeleton class to be in the same package.
*/
if (!packageName.equals("")) {
p.pln("package " + packageName + ";");
p.pln();
}
/*
* Declare the skeleton class.
*/
p.plnI("public final class " + skeletonClassSimpleName);
p.pln("implements " + SKELETON);
p.pOlnI("{");
writeOperationsArray(p);
p.pln();
writeInterfaceHash(p);
p.pln();
/*
* Define the getOperations() method.
*/
p.plnI("public " + OPERATION + "[] getOperations() {");
p.pln("return (" + OPERATION + "[]) operations.clone();");
p.pOln("}");
p.pln();
/*
* Define the dispatch() method.
*/
p.plnI("public void dispatch(" + REMOTE + " obj, " +
REMOTE_CALL + " call, int opnum, long hash)");
p.pln("throws java.lang.Exception");
p.pOlnI("{");
if (version == StubVersion.VCOMPAT) {
p.plnI("if (opnum < 0) {");
if (remoteMethods.length > 0) {
for (int opnum = 0; opnum < remoteMethods.length; opnum++) {
if (opnum > 0)
p.pO("} else ");
p.plnI("if (hash == " +
remoteMethods[opnum].methodHash() + "L) {");
p.pln("opnum = " + opnum + ";");
}
p.pOlnI("} else {");
}
/*
* Skeleton throws UnmarshalException if it does not recognize
* the method hash; this is what UnicastServerRef.dispatch()
* would do.
*/
p.pln("throw new " +
UNMARSHAL_EXCEPTION + "(\"invalid method hash\");");
if (remoteMethods.length > 0) {
p.pOln("}");
}
/*
* Ignore the validation of the interface hash if the
* operation number was negative, since it is really a
* method hash instead.
*/
p.pOlnI("} else {");
}
p.plnI("if (hash != interfaceHash)");
p.pln("throw new " +
SKELETON_MISMATCH_EXCEPTION + "(\"interface hash mismatch\");");
p.pO();
if (version == StubVersion.VCOMPAT) {
p.pOln("}"); // end if/else (opnum < 0) block
}
p.pln();
/*
* Cast remote object reference to the remote implementation
* class, if it's not private. We don't use the binary name
* of the class like previous implementations did because that
* would not compile with javac (since 1.4.1). If the remote
* implementation class is private, then we can't cast to it
* like previous implementations did because that also would
* not compile with javac-- so instead, we'll have to try to
* cast to the remote interface for each remote method.
*/
if (!remoteClass.classDoc().isPrivate()) {
p.pln(remoteClass.classDoc().qualifiedName() + " server = (" +
remoteClass.classDoc().qualifiedName() + ") obj;");
}
/*
* Process call according to the operation number.
*/
p.plnI("switch (opnum) {");
for (int opnum = 0; opnum < remoteMethods.length; opnum++) {
writeSkeletonDispatchCase(p, opnum);
}
p.pOlnI("default:");
/*
* Skeleton throws UnmarshalException if it does not recognize
* the operation number; this is consistent with the case of an
* unrecognized method hash.
*/
p.pln("throw new " + UNMARSHAL_EXCEPTION +
"(\"invalid method number\");");
p.pOln("}"); // end switch statement
p.pOln("}"); // end dispatch() method
p.pOln("}"); // end skeleton class
}
Writes the case block for the skeleton's dispatch method for
the remote method with the given "opnum".
/**
* Writes the case block for the skeleton's dispatch method for
* the remote method with the given "opnum".
**/
private void writeSkeletonDispatchCase(IndentingWriter p, int opnum)
throws IOException
{
RemoteClass.Method method = remoteMethods[opnum];
MethodDoc methodDoc = method.methodDoc();
String methodName = methodDoc.name();
Type paramTypes[] = method.parameterTypes();
String paramNames[] = nameParameters(paramTypes);
Type returnType = methodDoc.returnType();
p.pOlnI("case " + opnum + ": // " +
Util.getFriendlyUnqualifiedSignature(methodDoc));
/*
* Use nested block statement inside case to provide an independent
* namespace for local variables used to unmarshal parameters for
* this remote method.
*/
p.pOlnI("{");
if (paramTypes.length > 0) {
/*
* Declare local variables to hold arguments.
*/
for (int i = 0; i < paramTypes.length; i++) {
p.pln(paramTypes[i].toString() + " " + paramNames[i] + ";");
}
/*
* Unmarshal arguments from call stream.
*/
p.plnI("try {");
p.pln("java.io.ObjectInput in = call.getInputStream();");
boolean objectsRead = writeUnmarshalArguments(p, "in",
paramTypes, paramNames);
p.pOlnI("} catch (java.io.IOException e) {");
p.pln("throw new " + UNMARSHAL_EXCEPTION +
"(\"error unmarshalling arguments\", e);");
/*
* If any only if readObject has been invoked, we must catch
* ClassNotFoundException as well as IOException.
*/
if (objectsRead) {
p.pOlnI("} catch (java.lang.ClassNotFoundException e) {");
p.pln("throw new " + UNMARSHAL_EXCEPTION +
"(\"error unmarshalling arguments\", e);");
}
p.pOlnI("} finally {");
p.pln("call.releaseInputStream();");
p.pOln("}");
} else {
p.pln("call.releaseInputStream();");
}
if (!Util.isVoid(returnType)) {
/*
* Declare variable to hold return type, if not void.
*/
p.p(returnType.toString() + " $result = ");
// REMIND: why $?
}
/*
* Invoke the method on the server object. If the remote
* implementation class is private, then we don't have a
* reference cast to it, and so we try to cast to the remote
* object reference to the method's declaring interface here.
*/
String target = remoteClass.classDoc().isPrivate() ?
"((" + methodDoc.containingClass().qualifiedName() + ") obj)" :
"server";
p.p(target + "." + methodName + "(");
for (int i = 0; i < paramNames.length; i++) {
if (i > 0)
p.p(", ");
p.p(paramNames[i]);
}
p.pln(");");
/*
* Always invoke getResultStream(true) on the call object to send
* the indication of a successful invocation to the caller. If
* the return type is not void, keep the result stream and marshal
* the return value.
*/
p.plnI("try {");
if (!Util.isVoid(returnType)) {
p.p("java.io.ObjectOutput out = ");
}
p.pln("call.getResultStream(true);");
if (!Util.isVoid(returnType)) {
writeMarshalArgument(p, "out", returnType, "$result");
p.pln(";");
}
p.pOlnI("} catch (java.io.IOException e) {");
p.pln("throw new " +
MARSHAL_EXCEPTION + "(\"error marshalling return\", e);");
p.pOln("}");
p.pln("break;"); // break from switch statement
p.pOlnI("}"); // end nested block statement
p.pln();
}
Writes declaration and initializer for "operations" static array.
/**
* Writes declaration and initializer for "operations" static array.
**/
private void writeOperationsArray(IndentingWriter p)
throws IOException
{
p.plnI("private static final " + OPERATION + "[] operations = {");
for (int i = 0; i < remoteMethods.length; i++) {
if (i > 0)
p.pln(",");
p.p("new " + OPERATION + "(\"" +
remoteMethods[i].operationString() + "\")");
}
p.pln();
p.pOln("};");
}
Writes declaration and initializer for "interfaceHash" static field.
/**
* Writes declaration and initializer for "interfaceHash" static field.
**/
private void writeInterfaceHash(IndentingWriter p)
throws IOException
{
p.pln("private static final long interfaceHash = " +
remoteClass.interfaceHash() + "L;");
}
Writes declaration for java.lang.reflect.Method static fields
corresponding to each remote method in a stub.
/**
* Writes declaration for java.lang.reflect.Method static fields
* corresponding to each remote method in a stub.
**/
private void writeMethodFieldDeclarations(IndentingWriter p)
throws IOException
{
for (String name : methodFieldNames) {
p.pln("private static java.lang.reflect.Method " + name + ";");
}
}
Writes code to initialize the static fields for each method
using the Java Reflection API.
/**
* Writes code to initialize the static fields for each method
* using the Java Reflection API.
**/
private void writeMethodFieldInitializers(IndentingWriter p)
throws IOException
{
for (int i = 0; i < methodFieldNames.length; i++) {
p.p(methodFieldNames[i] + " = ");
/*
* Look up the Method object in the somewhat arbitrary
* interface that we find in the Method object.
*/
RemoteClass.Method method = remoteMethods[i];
MethodDoc methodDoc = method.methodDoc();
String methodName = methodDoc.name();
Type paramTypes[] = method.parameterTypes();
p.p(methodDoc.containingClass().qualifiedName() + ".class.getMethod(\"" +
methodName + "\", new java.lang.Class[] {");
for (int j = 0; j < paramTypes.length; j++) {
if (j > 0)
p.p(", ");
p.p(paramTypes[j].toString() + ".class");
}
p.pln("});");
}
}
/*
* Following are a series of static utility methods useful during
* the code generation process:
*/
Generates an array of names for fields correspondins to the
given array of remote methods. Each name in the returned array
is guaranteed to be unique.
The name of a method is included in its corresponding field
name to enhance readability of the generated code.
/**
* Generates an array of names for fields correspondins to the
* given array of remote methods. Each name in the returned array
* is guaranteed to be unique.
*
* The name of a method is included in its corresponding field
* name to enhance readability of the generated code.
**/
private static String[] nameMethodFields(RemoteClass.Method[] methods) {
String[] names = new String[methods.length];
for (int i = 0; i < names.length; i++) {
names[i] = "$method_" + methods[i].methodDoc().name() + "_" + i;
}
return names;
}
Generates an array of names for parameters corresponding to the
given array of types for the parameters. Each name in the
returned array is guaranteed to be unique.
A representation of the type of a parameter is included in its
corresponding parameter name to enhance the readability of the
generated code.
/**
* Generates an array of names for parameters corresponding to the
* given array of types for the parameters. Each name in the
* returned array is guaranteed to be unique.
*
* A representation of the type of a parameter is included in its
* corresponding parameter name to enhance the readability of the
* generated code.
**/
private static String[] nameParameters(Type[] types) {
String[] names = new String[types.length];
for (int i = 0; i < names.length; i++) {
names[i] = "$param_" +
generateNameFromType(types[i]) + "_" + (i + 1);
}
return names;
}
Generates a readable string representing the given type
suitable for embedding within a Java identifier.
/**
* Generates a readable string representing the given type
* suitable for embedding within a Java identifier.
**/
private static String generateNameFromType(Type type) {
String name = type.typeName().replace('.', '$');
int dimensions = type.dimension().length() / 2;
for (int i = 0; i < dimensions; i++) {
name = "arrayOf_" + name;
}
return name;
}
Writes a snippet of Java code to marshal a value named "name"
of type "type" to the java.io.ObjectOutput stream named
"stream".
Primitive types are marshalled with their corresponding methods
in the java.io.DataOutput interface, and objects (including
arrays) are marshalled using the writeObject method.
/**
* Writes a snippet of Java code to marshal a value named "name"
* of type "type" to the java.io.ObjectOutput stream named
* "stream".
*
* Primitive types are marshalled with their corresponding methods
* in the java.io.DataOutput interface, and objects (including
* arrays) are marshalled using the writeObject method.
**/
private static void writeMarshalArgument(IndentingWriter p,
String streamName,
Type type, String name)
throws IOException
{
if (type.dimension().length() > 0 || type.asClassDoc() != null) {
p.p(streamName + ".writeObject(" + name + ")");
} else if (type.typeName().equals("boolean")) {
p.p(streamName + ".writeBoolean(" + name + ")");
} else if (type.typeName().equals("byte")) {
p.p(streamName + ".writeByte(" + name + ")");
} else if (type.typeName().equals("char")) {
p.p(streamName + ".writeChar(" + name + ")");
} else if (type.typeName().equals("short")) {
p.p(streamName + ".writeShort(" + name + ")");
} else if (type.typeName().equals("int")) {
p.p(streamName + ".writeInt(" + name + ")");
} else if (type.typeName().equals("long")) {
p.p(streamName + ".writeLong(" + name + ")");
} else if (type.typeName().equals("float")) {
p.p(streamName + ".writeFloat(" + name + ")");
} else if (type.typeName().equals("double")) {
p.p(streamName + ".writeDouble(" + name + ")");
} else {
throw new AssertionError(type);
}
}
Writes Java statements to marshal a series of values in order
as named in the "names" array, with types as specified in the
"types" array, to the java.io.ObjectOutput stream named
"stream".
/**
* Writes Java statements to marshal a series of values in order
* as named in the "names" array, with types as specified in the
* "types" array, to the java.io.ObjectOutput stream named
* "stream".
**/
private static void writeMarshalArguments(IndentingWriter p,
String streamName,
Type[] types, String[] names)
throws IOException
{
assert types.length == names.length;
for (int i = 0; i < types.length; i++) {
writeMarshalArgument(p, streamName, types[i], names[i]);
p.pln(";");
}
}
Writes a snippet of Java code to unmarshal a value of type
"type" from the java.io.ObjectInput stream named "stream" into
a variable named "name" (if "name" is null, the value is
unmarshalled and discarded).
Primitive types are unmarshalled with their corresponding
methods in the java.io.DataInput interface, and objects
(including arrays) are unmarshalled using the readObject
method.
Returns true if code to invoke readObject was written, and
false otherwise.
/**
* Writes a snippet of Java code to unmarshal a value of type
* "type" from the java.io.ObjectInput stream named "stream" into
* a variable named "name" (if "name" is null, the value is
* unmarshalled and discarded).
*
* Primitive types are unmarshalled with their corresponding
* methods in the java.io.DataInput interface, and objects
* (including arrays) are unmarshalled using the readObject
* method.
*
* Returns true if code to invoke readObject was written, and
* false otherwise.
**/
private static boolean writeUnmarshalArgument(IndentingWriter p,
String streamName,
Type type, String name)
throws IOException
{
boolean readObject = false;
if (name != null) {
p.p(name + " = ");
}
if (type.dimension().length() > 0 || type.asClassDoc() != null) {
p.p("(" + type.toString() + ") " + streamName + ".readObject()");
readObject = true;
} else if (type.typeName().equals("boolean")) {
p.p(streamName + ".readBoolean()");
} else if (type.typeName().equals("byte")) {
p.p(streamName + ".readByte()");
} else if (type.typeName().equals("char")) {
p.p(streamName + ".readChar()");
} else if (type.typeName().equals("short")) {
p.p(streamName + ".readShort()");
} else if (type.typeName().equals("int")) {
p.p(streamName + ".readInt()");
} else if (type.typeName().equals("long")) {
p.p(streamName + ".readLong()");
} else if (type.typeName().equals("float")) {
p.p(streamName + ".readFloat()");
} else if (type.typeName().equals("double")) {
p.p(streamName + ".readDouble()");
} else {
throw new AssertionError(type);
}
return readObject;
}
Writes Java statements to unmarshal a series of values in order
of types as in the "types" array from the java.io.ObjectInput
stream named "stream" into variables as named in "names" (for
any element of "names" that is null, the corresponding value is
unmarshalled and discarded).
/**
* Writes Java statements to unmarshal a series of values in order
* of types as in the "types" array from the java.io.ObjectInput
* stream named "stream" into variables as named in "names" (for
* any element of "names" that is null, the corresponding value is
* unmarshalled and discarded).
**/
private static boolean writeUnmarshalArguments(IndentingWriter p,
String streamName,
Type[] types,
String[] names)
throws IOException
{
assert types.length == names.length;
boolean readObject = false;
for (int i = 0; i < types.length; i++) {
if (writeUnmarshalArgument(p, streamName, types[i], names[i])) {
readObject = true;
}
p.pln(";");
}
return readObject;
}
Returns a snippet of Java code to wrap a value named "name" of
type "type" into an object as appropriate for use by the Java
Reflection API.
For primitive types, an appropriate wrapper class is
instantiated with the primitive value. For object types
(including arrays), no wrapping is necessary, so the value is
named directly.
/**
* Returns a snippet of Java code to wrap a value named "name" of
* type "type" into an object as appropriate for use by the Java
* Reflection API.
*
* For primitive types, an appropriate wrapper class is
* instantiated with the primitive value. For object types
* (including arrays), no wrapping is necessary, so the value is
* named directly.
**/
private static String wrapArgumentCode(Type type, String name) {
if (type.dimension().length() > 0 || type.asClassDoc() != null) {
return name;
} else if (type.typeName().equals("boolean")) {
return ("(" + name +
" ? java.lang.Boolean.TRUE : java.lang.Boolean.FALSE)");
} else if (type.typeName().equals("byte")) {
return "new java.lang.Byte(" + name + ")";
} else if (type.typeName().equals("char")) {
return "new java.lang.Character(" + name + ")";
} else if (type.typeName().equals("short")) {
return "new java.lang.Short(" + name + ")";
} else if (type.typeName().equals("int")) {
return "new java.lang.Integer(" + name + ")";
} else if (type.typeName().equals("long")) {
return "new java.lang.Long(" + name + ")";
} else if (type.typeName().equals("float")) {
return "new java.lang.Float(" + name + ")";
} else if (type.typeName().equals("double")) {
return "new java.lang.Double(" + name + ")";
} else {
throw new AssertionError(type);
}
}
Returns a snippet of Java code to unwrap a value named "name"
into a value of type "type", as appropriate for the Java
Reflection API.
For primitive types, the value is assumed to be of the
corresponding wrapper class, and a method is called on the
wrapper to retrieve the primitive value. For object types
(include arrays), no unwrapping is necessary; the value is
simply cast to the expected real object type.
/**
* Returns a snippet of Java code to unwrap a value named "name"
* into a value of type "type", as appropriate for the Java
* Reflection API.
*
* For primitive types, the value is assumed to be of the
* corresponding wrapper class, and a method is called on the
* wrapper to retrieve the primitive value. For object types
* (include arrays), no unwrapping is necessary; the value is
* simply cast to the expected real object type.
**/
private static String unwrapArgumentCode(Type type, String name) {
if (type.dimension().length() > 0 || type.asClassDoc() != null) {
return "((" + type.toString() + ") " + name + ")";
} else if (type.typeName().equals("boolean")) {
return "((java.lang.Boolean) " + name + ").booleanValue()";
} else if (type.typeName().equals("byte")) {
return "((java.lang.Byte) " + name + ").byteValue()";
} else if (type.typeName().equals("char")) {
return "((java.lang.Character) " + name + ").charValue()";
} else if (type.typeName().equals("short")) {
return "((java.lang.Short) " + name + ").shortValue()";
} else if (type.typeName().equals("int")) {
return "((java.lang.Integer) " + name + ").intValue()";
} else if (type.typeName().equals("long")) {
return "((java.lang.Long) " + name + ").longValue()";
} else if (type.typeName().equals("float")) {
return "((java.lang.Float) " + name + ").floatValue()";
} else if (type.typeName().equals("double")) {
return "((java.lang.Double) " + name + ").doubleValue()";
} else {
throw new AssertionError(type);
}
}
}