/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* 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.
*/
/**
* 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 */
/** the remote implemention class to generate code for */
/** version of the JRMP stub protocol to generate code for */
/*
* binary names of the stub and skeleton classes to generate for
* the remote class
*/
/* package name and simple names of the stub and skeleton classes */
/** remote methods of class, indexed by operation number */
/**
* Names to use for the java.lang.reflect.Method static fields in
* the generated stub class corresponding to each remote method.
**/
/**
* Creates a StubSkeletonWriter instance for the specified remote
* implementation class. The generated code will implement the
* specified JRMP stub protocol version.
**/
{
this.remoteClass = remoteClass;
}
/**
* Returns the binary name of the stub class to generate for the
* remote implementation class.
**/
return stubClassName;
}
/**
* Returns the binary name of the skeleton class to generate for
* the remote implementation class.
**/
return skeletonClassName;
}
/**
* Writes the stub class for the remote class to a stream.
**/
/*
* 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.
*/
p.pln();
}
/*
* Declare the stub class; implement all remote interfaces.
*/
p.p("implements ");
if (i > 0) {
p.p(", ");
}
p.p(remoteInterfaces[i].qualifiedName());
}
p.pln();
}
p.pOlnI("{");
{
p.pln();
p.pln();
}
{
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
* (see bugid 4125181).
*/
p.pln("private static boolean useNewInvoke;");
}
p.pln();
/*
* Initialize java.lang.reflect.Method fields for each remote
* method in a static initializer.
*/
p.plnI("static {");
p.plnI("try {");
/*
* 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("new java.lang.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;");
}
p.pOlnI("} catch (java.lang.NoSuchMethodException e) {");
p.pln("useNewInvoke = false;");
} else {
p.plnI("throw new java.lang.NoSuchMethodError(");
p.pln("\"stub class initialization failed\");");
p.pO();
}
p.pln();
}
}
p.pln();
/*
* Write each stub method.
*/
p.pln("// methods from remote interfaces");
p.pln();
writeStubMethod(p, i);
}
}
}
/**
* Writes the constructors for the stub class.
**/
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.
*/
{
p.pln("super();");
p.pOln("}");
}
p.pln("super(ref);");
p.pOln("}");
}
/**
* Writes the stub method for the remote method with the given
* operation number.
**/
throws IOException
{
/*
* Declare stub method; throw exceptions declared in remote
* interface(s).
*/
p.pln("// implementation of " +
if (i > 0) {
p.p(", ");
}
}
p.plnI(")");
p.p("throws ");
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.
*/
/*
* 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.
*/
p.plnI("try {");
}
p.plnI("if (useNewInvoke) {");
}
{
p.p("Object $result = "); // REMIND: why $?
}
p.p("new java.lang.Object[] {");
if (i > 0)
p.p(", ");
}
p.p("}");
} else {
p.p("null");
}
p.pln("return " +
}
}
p.pOlnI("} else {");
}
{
p.plnI("try {");
p.pln("java.io.ObjectOutput out = call.getOutputStream();");
p.pOlnI("} catch (java.io.IOException e) {");
"(\"error marshalling arguments\", e);");
p.pOln("}");
}
p.pln("ref.invoke(call);");
p.pln("ref.done(call);");
} else {
// REMIND: why $?
p.plnI("try {");
p.pln("java.io.ObjectInput in = call.getInputStream();");
boolean objectRead =
p.pln(";");
p.pOlnI("} catch (java.io.IOException e) {");
"(\"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) {");
"(\"error unmarshalling return\", e);");
}
p.pOlnI("} finally {");
p.pln("ref.done(call);");
p.pOln("}");
p.pln("return $result;");
}
}
}
/*
* 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.
*/
p.pln("throw e;");
}
p.pOlnI("} catch (java.lang.Exception e) {");
"(\"undeclared checked exception\", e);");
}
}
/**
* 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.
**/
/* For each exception declared by the stub method's throws clause: */
/*
* 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;
/*
* 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:
*/
/*
* If a superclass of this exception is already on
* the list to catch, then ignore this one and continue;
*/
continue nextException;
/*
* 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. */
}
return uniqueList;
}
/**
* Writes the skeleton for the remote class to a stream.
**/
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.
*/
p.pln();
}
/*
* Declare the skeleton class.
*/
p.pOlnI("{");
p.pln();
p.pln();
/*
* Define the getOperations() method.
*/
p.pOln("}");
p.pln();
/*
* Define the dispatch() method.
*/
REMOTE_CALL + " call, int opnum, long hash)");
p.pln("throws java.lang.Exception");
p.pOlnI("{");
p.plnI("if (opnum < 0) {");
if (opnum > 0)
p.pO("} else ");
p.plnI("if (hash == " +
}
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\");");
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();
}
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.
*/
}
/*
* Process call according to the operation number.
*/
p.plnI("switch (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.
*/
"(\"invalid method number\");");
}
/**
* Writes the case block for the skeleton's dispatch method for
* the remote method with the given "opnum".
**/
throws IOException
{
/*
* Use nested block statement inside case to provide an independent
* namespace for local variables used to unmarshal parameters for
* this remote method.
*/
p.pOlnI("{");
/*
* Declare local variables to hold arguments.
*/
}
/*
* Unmarshal arguments from call stream.
*/
p.plnI("try {");
p.pln("java.io.ObjectInput in = call.getInputStream();");
p.pOlnI("} catch (java.io.IOException e) {");
"(\"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) {");
"(\"error unmarshalling arguments\", e);");
}
p.pOlnI("} finally {");
p.pln("call.releaseInputStream();");
p.pOln("}");
} else {
p.pln("call.releaseInputStream();");
}
/*
* Declare variable to hold return type, if not void.
*/
// 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.
*/
"server";
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 {");
p.p("java.io.ObjectOutput out = ");
}
p.pln("call.getResultStream(true);");
p.pln(";");
}
p.pOlnI("} catch (java.io.IOException e) {");
p.pln("throw new " +
MARSHAL_EXCEPTION + "(\"error marshalling return\", e);");
p.pOln("}");
p.pln();
}
/**
* Writes declaration and initializer for "operations" static array.
**/
throws IOException
{
if (i > 0)
p.pln(",");
}
p.pln();
p.pOln("};");
}
/**
* Writes declaration and initializer for "interfaceHash" static field.
**/
throws IOException
{
p.pln("private static final long interfaceHash = " +
}
/**
* Writes declaration for java.lang.reflect.Method static fields
* corresponding to each remote method in a stub.
**/
throws IOException
{
}
}
/**
* Writes code to initialize the static fields for each method
* using the Java Reflection API.
**/
throws IOException
{
p.p(methodFieldNames[i] + " = ");
/*
* Look up the Method object in the somewhat arbitrary
* interface that we find in the Method object.
*/
methodName + "\", new java.lang.Class[] {");
if (j > 0)
p.p(", ");
}
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.
**/
}
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.
**/
names[i] = "$param_" +
}
return names;
}
/**
* Generates a readable string representing the given type
* suitable for embedding within a Java identifier.
**/
for (int i = 0; i < dimensions; i++) {
}
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.
**/
throws IOException
{
} 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".
**/
throws IOException
{
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.
**/
throws IOException
{
boolean readObject = false;
p.p(name + " = ");
}
readObject = true;
p.p(streamName + ".readBoolean()");
p.p(streamName + ".readByte()");
p.p(streamName + ".readChar()");
p.p(streamName + ".readShort()");
p.p(streamName + ".readInt()");
p.p(streamName + ".readLong()");
p.p(streamName + ".readFloat()");
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).
**/
throws IOException
{
boolean readObject = false;
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.
**/
return name;
return ("(" + name +
" ? java.lang.Boolean.TRUE : java.lang.Boolean.FALSE)");
} 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.
**/
} else {
throw new AssertionError(type);
}
}
}