StubSkeletonWriter.java revision 2362
2362N/A * Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved. 0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 0N/A * This code is free software; you can redistribute it and/or modify it 0N/A * under the terms of the GNU General Public License version 2 only, as 2362N/A * published by the Free Software Foundation. Oracle designates this 0N/A * particular file as subject to the "Classpath" exception as provided 2362N/A * by Oracle in the LICENSE file that accompanied this code. 0N/A * This code is distributed in the hope that it will be useful, but WITHOUT 0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 0N/A * version 2 for more details (a copy is included in the LICENSE file that 0N/A * accompanied this code). 0N/A * You should have received a copy of the GNU General Public License version 0N/A * 2 along with this work; if not, write to the Free Software Foundation, 0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 2362N/A * or visit www.oracle.com if you need additional information or have any 0N/A * Writes the source code for the stub class and (optionally) skeleton 0N/A * class for a particular remote implementation class. 0N/A * WARNING: The contents of this source file are not part of any 0N/A * supported API. Code that depends on them does so at its own risk: 0N/A * they are subject to change or removal without notice. 0N/A * @author Peter Jones 0N/A /** rmic environment for this object */ 0N/A /** the remote implemention class to generate code for */ 0N/A /** version of the JRMP stub protocol to generate code for */ 0N/A * binary names of the stub and skeleton classes to generate for 0N/A /* package name and simple names of the stub and skeleton classes */ 0N/A /** remote methods of class, indexed by operation number */ 0N/A * Names to use for the java.lang.reflect.Method static fields in 0N/A * the generated stub class corresponding to each remote method. 0N/A * Creates a StubSkeletonWriter instance for the specified remote 0N/A * implementation class. The generated code will implement the 0N/A * specified JRMP stub protocol version. 0N/A * Returns the binary name of the stub class to generate for the 0N/A * remote implementation class. 0N/A * Returns the binary name of the skeleton class to generate for 0N/A * the remote implementation class. 0N/A * Writes the stub class for the remote class to a stream. 0N/A * Write boiler plate comment. 0N/A p.
pln(
"// Stub class generated by rmic, do not edit.");
0N/A p.
pln(
"// Contents subject to change without notice.");
0N/A * If remote implementation class was in a particular package, 0N/A * declare the stub class to be in the same package. 0N/A * Declare the stub class; implement all remote interfaces. 0N/A p.
pln(
"private static final long serialVersionUID = " +
0N/A * We only need to declare and initialize the static fields of 0N/A * Method objects for each remote method if there are any remote 0N/A * methods; otherwise, skip this code entirely, to avoid generating 0N/A * a try/catch block for a checked exception that cannot occur 0N/A * (see bugid 4125181). 0N/A p.
pln(
"private static boolean useNewInvoke;");
0N/A * Initialize java.lang.reflect.Method fields for each remote 0N/A * method in a static initializer. 0N/A * Fat stubs must determine whether the API required for 0N/A * the JDK 1.2 stub protocol is supported in the current 0N/A * runtime, so that it can use it if supported. This is 0N/A * determined by using the Reflection API to test if the 0N/A * new invoke method on RemoteRef exists, and setting the 0N/A * static boolean "useNewInvoke" to true if it does, or 0N/A * to false if a NoSuchMethodException is thrown. 0N/A p.
pln(
"java.lang.reflect.Method.class,");
0N/A p.
pln(
"java.lang.Object[].class,");
0N/A p.
pln(
"useNewInvoke = true;");
0N/A p.
pOlnI(
"} catch (java.lang.NoSuchMethodException e) {");
0N/A p.
pln(
"useNewInvoke = false;");
0N/A p.
plnI(
"throw new java.lang.NoSuchMethodError(");
0N/A p.
pln(
"\"stub class initialization failed\");");
0N/A p.
pOln(
"}");
// end static initializer 0N/A * Write each stub method. 0N/A p.
pln(
"// methods from remote interfaces");
0N/A * Writes the constructors for the stub class. 0N/A * Only stubs compatible with the JDK 1.1 stub protocol need 0N/A * a no-arg constructor; later versions use reflection to find 0N/A * the constructor that directly takes a RemoteRef argument. 0N/A * Writes the stub method for the remote method with the given 0N/A * Declare stub method; throw exceptions declared in remote 0N/A p.
pln(
"// implementation of " +
0N/A * The RemoteRef.invoke methods throw Exception, but unless 0N/A * this stub method throws Exception as well, we must catch 0N/A * Exceptions thrown from the invocation. So we must catch 0N/A * Exception and rethrow something we can throw: 0N/A * UnexpectedException, which is a subclass of 0N/A * RemoteException. But for any subclasses of Exception that 0N/A * we can throw, like RemoteException, RuntimeException, and 0N/A * any of the exceptions declared by this stub method, we want 0N/A * them to pass through unmodified, so first we must catch any 0N/A * such exceptions and rethrow them directly. 0N/A * We have to be careful generating the rethrowing catch 0N/A * blocks here, because javac will flag an error if there are 0N/A * any unreachable catch blocks, i.e. if the catch of an 0N/A * exception class follows a previous catch of it or of one of 0N/A * its superclasses. The following method invocation takes 0N/A * care of these details. 0N/A * If we need to catch any particular exceptions (i.e. this method 0N/A * does not declare java.lang.Exception), put the entire stub 0N/A * method in a try block. 0N/A p.p(
"Object $result = ");
// REMIND: why $? 0N/A p.p(
"new java.lang.Object[] {");
0N/A ") this, operations, " +
opnum +
", interfaceHash);");
0N/A p.
pln(
"java.io.ObjectOutput out = call.getOutputStream();");
0N/A p.
pOlnI(
"} catch (java.io.IOException e) {");
0N/A "(\"error marshalling arguments\", e);");
0N/A p.
pln(
"java.io.ObjectInput in = call.getInputStream();");
0N/A p.
pOlnI(
"} catch (java.io.IOException e) {");
0N/A "(\"error unmarshalling return\", e);");
0N/A * If any only if readObject has been invoked, we must catch 0N/A * ClassNotFoundException as well as IOException. 0N/A p.
pOlnI(
"} catch (java.lang.ClassNotFoundException e) {");
0N/A "(\"error unmarshalling return\", e);");
0N/A * If we need to catch any particular exceptions, finally write 0N/A * the catch blocks for them, rethrow any other Exceptions with an 0N/A * UnexpectedException, and end the try block. 0N/A p.
pOlnI(
"} catch (java.lang.Exception e) {");
0N/A "(\"undeclared checked exception\", e);");
0N/A * Computes the exceptions that need to be caught and rethrown in 0N/A * a stub method before wrapping Exceptions in 0N/A * UnexpectedExceptions, given the exceptions declared in the 0N/A * throws clause of the method. Returns a list containing the 0N/A * exception to catch. Each exception is guaranteed to be unique, 0N/A * i.e. not a subclass of any of the other exceptions in the list, 0N/A * so the catch blocks for these exceptions may be generated in 0N/A * any order relative to each other. 0N/A * RemoteException and RuntimeException are each automatically 0N/A * placed in the returned list (unless any of their superclasses 0N/A * are already present), since those exceptions should always be 0N/A * directly rethrown by a stub method. 0N/A * The returned list will be empty if java.lang.Exception or one 0N/A * of its superclasses is in the throws clause of the method, 0N/A * indicating that no exceptions need to be caught. 0N/A /* For each exception declared by the stub method's throws clause: */ 0N/A * If java.lang.Exception (or a superclass) was declared 0N/A * in the throws clause of this stub method, then we don't 0N/A * have to bother catching anything; clear the list and 0N/A * Ignore other Throwables that do not extend Exception, 0N/A * because they cannot be thrown by the invoke methods. 0N/A * Compare this exception against the current list of 0N/A * exceptions that need to be caught: 0N/A * If a superclass of this exception is already on 0N/A * the list to catch, then ignore this one and continue; 0N/A * If a subclass of this exception is on the list 0N/A * to catch, then remove it; 0N/A /* This exception is unique: add it to the list to catch. */ 0N/A * Writes the skeleton for the remote class to a stream. 0N/A "should not generate skeleton for version " +
version);
0N/A * Write boiler plate comment. 0N/A p.
pln(
"// Skeleton class generated by rmic, do not edit.");
0N/A p.
pln(
"// Contents subject to change without notice.");
0N/A * If remote implementation class was in a particular package, 0N/A * declare the skeleton class to be in the same package. 0N/A * Declare the skeleton class. 0N/A * Define the getOperations() method. 0N/A * Define the dispatch() method. 0N/A p.
pln(
"throws java.lang.Exception");
0N/A * Skeleton throws UnmarshalException if it does not recognize 0N/A * the method hash; this is what UnicastServerRef.dispatch() 0N/A * Ignore the validation of the interface hash if the 0N/A * operation number was negative, since it is really a 0N/A * method hash instead. 0N/A p.
plnI(
"if (hash != interfaceHash)");
0N/A * Cast remote object reference to the remote implementation 0N/A * class, if it's not private. We don't use the binary name 0N/A * of the class like previous implementations did because that 0N/A * would not compile with javac (since 1.4.1). If the remote 0N/A * implementation class is private, then we can't cast to it 0N/A * like previous implementations did because that also would 0N/A * not compile with javac-- so instead, we'll have to try to 0N/A * cast to the remote interface for each remote method. 0N/A * Process call according to the operation number. 0N/A * Skeleton throws UnmarshalException if it does not recognize 0N/A * the operation number; this is consistent with the case of an 0N/A * unrecognized method hash. 0N/A "(\"invalid method number\");");
0N/A p.
pOln(
"}");
// end switch statement 0N/A p.
pOln(
"}");
// end dispatch() method 0N/A * Writes the case block for the skeleton's dispatch method for 0N/A * the remote method with the given "opnum". 0N/A * Use nested block statement inside case to provide an independent 0N/A * namespace for local variables used to unmarshal parameters for 0N/A * this remote method. 0N/A * Declare local variables to hold arguments. 0N/A * Unmarshal arguments from call stream. 0N/A p.
pln(
"java.io.ObjectInput in = call.getInputStream();");
0N/A p.
pOlnI(
"} catch (java.io.IOException e) {");
0N/A "(\"error unmarshalling arguments\", e);");
0N/A * If any only if readObject has been invoked, we must catch 0N/A * ClassNotFoundException as well as IOException. 0N/A p.
pOlnI(
"} catch (java.lang.ClassNotFoundException e) {");
0N/A "(\"error unmarshalling arguments\", e);");
0N/A p.
pln(
"call.releaseInputStream();");
0N/A p.
pln(
"call.releaseInputStream();");
0N/A * Declare variable to hold return type, if not void. 0N/A * Invoke the method on the server object. If the remote 0N/A * implementation class is private, then we don't have a 0N/A * reference cast to it, and so we try to cast to the remote 0N/A * object reference to the method's declaring interface here. 0N/A * Always invoke getResultStream(true) on the call object to send 0N/A * the indication of a successful invocation to the caller. If 0N/A * the return type is not void, keep the result stream and marshal 0N/A p.p(
"java.io.ObjectOutput out = ");
0N/A p.
pln(
"call.getResultStream(true);");
0N/A p.
pOlnI(
"} catch (java.io.IOException e) {");
0N/A p.
pln(
"break;");
// break from switch statement 0N/A p.
pOlnI(
"}");
// end nested block statement 0N/A * Writes declaration and initializer for "operations" static array. 0N/A * Writes declaration and initializer for "interfaceHash" static field. 0N/A p.
pln(
"private static final long interfaceHash = " +
0N/A * Writes declaration for java.lang.reflect.Method static fields 0N/A * corresponding to each remote method in a stub. 0N/A p.
pln(
"private static java.lang.reflect.Method " +
name +
";");
0N/A * Writes code to initialize the static fields for each method 0N/A * using the Java Reflection API. 0N/A * Look up the Method object in the somewhat arbitrary 0N/A * interface that we find in the Method object. 0N/A * Following are a series of static utility methods useful during 0N/A * the code generation process: 0N/A * Generates an array of names for fields correspondins to the 0N/A * given array of remote methods. Each name in the returned array 0N/A * is guaranteed to be unique. 0N/A * The name of a method is included in its corresponding field 0N/A * name to enhance readability of the generated code. 0N/A * Generates an array of names for parameters corresponding to the 0N/A * given array of types for the parameters. Each name in the 0N/A * returned array is guaranteed to be unique. 0N/A * A representation of the type of a parameter is included in its 0N/A * corresponding parameter name to enhance the readability of the 0N/A * Generates a readable string representing the given type 0N/A * suitable for embedding within a Java identifier. 0N/A * Writes a snippet of Java code to marshal a value named "name" 0N/A * of type "type" to the java.io.ObjectOutput stream named 0N/A * Primitive types are marshalled with their corresponding methods 0N/A * in the java.io.DataOutput interface, and objects (including 0N/A * arrays) are marshalled using the writeObject method. 0N/A * Writes Java statements to marshal a series of values in order 0N/A * as named in the "names" array, with types as specified in the 0N/A * "types" array, to the java.io.ObjectOutput stream named 0N/A * Writes a snippet of Java code to unmarshal a value of type 0N/A * "type" from the java.io.ObjectInput stream named "stream" into 0N/A * a variable named "name" (if "name" is null, the value is 0N/A * unmarshalled and discarded). 0N/A * Primitive types are unmarshalled with their corresponding 0N/A * methods in the java.io.DataInput interface, and objects 0N/A * (including arrays) are unmarshalled using the readObject 0N/A * Returns true if code to invoke readObject was written, and 0N/A * Writes Java statements to unmarshal a series of values in order 0N/A * of types as in the "types" array from the java.io.ObjectInput 0N/A * stream named "stream" into variables as named in "names" (for 0N/A * any element of "names" that is null, the corresponding value is 0N/A * unmarshalled and discarded). 0N/A * Returns a snippet of Java code to wrap a value named "name" of 0N/A * type "type" into an object as appropriate for use by the Java 0N/A * For primitive types, an appropriate wrapper class is 0N/A * instantiated with the primitive value. For object types 0N/A * (including arrays), no wrapping is necessary, so the value is 0N/A " ? java.lang.Boolean.TRUE : java.lang.Boolean.FALSE)");
0N/A return "new java.lang.Byte(" +
name +
")";
0N/A return "new java.lang.Character(" +
name +
")";
0N/A return "new java.lang.Short(" +
name +
")";
0N/A return "new java.lang.Integer(" +
name +
")";
0N/A return "new java.lang.Long(" +
name +
")";
0N/A return "new java.lang.Float(" +
name +
")";
0N/A return "new java.lang.Double(" +
name +
")";
0N/A * Returns a snippet of Java code to unwrap a value named "name" 0N/A * into a value of type "type", as appropriate for the Java 0N/A * For primitive types, the value is assumed to be of the 0N/A * corresponding wrapper class, and a method is called on the 0N/A * wrapper to retrieve the primitive value. For object types 0N/A * (include arrays), no unwrapping is necessary; the value is 0N/A * simply cast to the expected real object type. 0N/A return "((java.lang.Boolean) " +
name +
").booleanValue()";
0N/A return "((java.lang.Byte) " +
name +
").byteValue()";
0N/A return "((java.lang.Character) " +
name +
").charValue()";
0N/A return "((java.lang.Short) " +
name +
").shortValue()";
0N/A return "((java.lang.Integer) " +
name +
").intValue()";
0N/A return "((java.lang.Long) " +
name +
").longValue()";
0N/A return "((java.lang.Float) " +
name +
").floatValue()";
0N/A return "((java.lang.Double) " +
name +
").doubleValue()";