/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.jini.io; import java.io.IOException; import java.io.InputStream; import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.ObjectStreamClass; import java.net.MalformedURLException; import java.rmi.server.RMIClassLoader; import java.util.Collection; import java.util.HashMap; import java.util.Map; import net.jini.loader.ClassLoading; import net.jini.security.Security; /** * An extension of <code>ObjectInputStream</code> that implements the * dynamic class loading semantics of Java(TM) Remote Method * Invocation (Java RMI) argument and result * unmarshalling (using {@link RMIClassLoader}). A * <code>MarshalInputStream</code> is intended to read data written by * a corresponding {@link MarshalOutputStream}. * * <p><code>MarshalInputStream</code> implements the input side of the * dynamic class loading semantics by overriding {@link * ObjectInputStream#resolveClass resolveClass} and {@link * ObjectInputStream#resolveProxyClass resolveProxyClass} to resolve * class descriptors in the stream using {@link ClassLoading#loadClass * ClassLoading.loadClass} and {@link ClassLoading#loadProxyClass * ClassLoading.loadProxyClass} (which, in turn, use {@link * RMIClassLoader#loadClass(String,String,ClassLoader) * RMIClassLoader.loadClass} and {@link * RMIClassLoader#loadProxyClass(String,String[],ClassLoader) * RMIClassLoader.loadProxyClass}), optionally with codebase * annotation strings written by a <code>MarshalOutputStream</code>. * * <p>By default, a <code>MarshalInputStream</code> ignores all * codebase annotation strings, instead using a codebase value of * <code>null</code> when loading classes. Codebase annotation * strings will only be used by a <code>MarshalInputStream</code> * after its {@link #useCodebaseAnnotations useCodebaseAnnotations} * method has been invoked. * * <p><code>MarshalInputStream</code> supports optional verification * that all codebase annotation URLs that are used to load classes * resolved by the stream provide content integrity (see {@link * Security#verifyCodebaseIntegrity * Security.verifyCodebaseIntegrity}). Whether or not a particular * <code>MarshalInputStream</code> instance verifies that codebase * annotation URLs provide content integrity is determined by the * <code>verifyCodebaseIntegrity</code> constructor argument. See * {@link ClassLoading#loadClass ClassLoading.loadClass} and {@link * ClassLoading#loadProxyClass ClassLoading.loadProxyClass} for * details of how codebase integrity verification is performed. * * <p><code>MarshalInputStream</code> reads class annotations from its * own stream; a subclass can override the {@link #readAnnotation * readAnnotation} method to read the class annotations from a * different location. * * <p>A <code>MarshalInputStream</code> is not guaranteed to be * safe for concurrent use by multiple threads. * * @author Sun Microsystems, Inc. * @since 2.0 **/ public class MarshalInputStream extends ObjectInputStream implements ObjectStreamContext { /** * maps keywords for primitive types and void to corresponding * Class objects **/ private static final Map specialClasses = new HashMap(); static { specialClasses.put("boolean", boolean.class); specialClasses.put("byte", byte.class); specialClasses.put("char", char.class); specialClasses.put("short", short.class); specialClasses.put("int", int.class); specialClasses.put("long", long.class); specialClasses.put("float", float.class); specialClasses.put("double", double.class); specialClasses.put("void", void.class); } /** context for ObjectStreamContext implementation */ private final Collection context; /** * value to pass as the "default loader" argument to loadClass and * loadProxyClass **/ private final ClassLoader defaultLoader; /** true if this stream verifies codebase integrity */ private final boolean verifyCodebaseIntegrity; /** loader to pass to Security.verifyCodebaseIntegrity */ private final ClassLoader verifierLoader; /** * if false, pass null codebase values to loadClass and * loadProxyClass methods; if true, pass codebase values from * stream class annotations **/ private boolean usingCodebaseAnnotations = false; /** * Creates a new <code>MarshalInputStream</code> that reads * marshalled data from the specified underlying * <code>InputStream</code>. * * <p>This constructor passes <code>in</code> to the superclass * constructor that has an <code>InputStream</code> parameter. * * <p><code>defaultLoader</code> will be passed as the * <code>defaultLoader</code> argument to {@link * ClassLoading#loadClass ClassLoading.loadClass} and {@link * ClassLoading#loadProxyClass ClassLoading.loadProxyClass} * whenever those methods are invoked by {@link #resolveClass * resolveClass} and {@link #resolveProxyClass resolveProxyClass}. * * <p>If <code>verifyCodebaseIntegrity</code> is * <code>true</code>, then the created stream will verify that all * codebase annotation URLs that are used to load classes resolved * by the stream provide content integrity, and whenever {@link * Security#verifyCodebaseIntegrity * Security.verifyCodebaseIntegrity} is invoked to enforce that * verification, <code>verifierLoader</code> will be passed as the * <code>loader</code> argument. See {@link * ClassLoading#loadClass ClassLoading.loadClass} and {@link * ClassLoading#loadProxyClass ClassLoading.loadProxyClass} for * details of how codebase integrity verification is performed. * * <p><code>context</code> will be used as the return value of the * created stream's {@link #getObjectStreamContext * getObjectStreamContext} method. * * @param in the input stream to read marshalled data from * * @param defaultLoader the class loader value (possibly * <code>null</code>) to pass as the <code>defaultLoader</code> * argument to <code>ClassLoading</code> methods * * @param verifyCodebaseIntegrity if <code>true</code>, this * stream will verify that codebase annotation URLs used to load * classes resolved by this stream provide content integrity * * @param verifierLoader the class loader value (possibly * <code>null</code>) to pass to * <code>Security.verifyCodebaseIntegrity</code>, if * <code>verifyCodebaseIntegrity</code> is <code>true</code> * * @param context the collection of context information objects to * be returned by this stream's {@link #getObjectStreamContext * getObjectStreamContext} method * * @throws IOException if the superclass's constructor throws an * <code>IOException</code> * * @throws SecurityException if the superclass's constructor * throws a <code>SecurityException</code> * * @throws NullPointerException if <code>in</code> or * <code>context</code> is <code>null</code> **/ public MarshalInputStream(InputStream in, ClassLoader defaultLoader, boolean verifyCodebaseIntegrity, ClassLoader verifierLoader, Collection context) throws IOException { super(in); if (context == null) { throw new NullPointerException(); } this.defaultLoader = defaultLoader; this.verifyCodebaseIntegrity = verifyCodebaseIntegrity; this.verifierLoader = verifierLoader; this.context = context; } /** * Enables the use of codebase annotation strings written by the * corresponding <code>MarshalOutputStream</code>. * * <p>If this method has not been invoked on this stream, then the * <code>resolveClass</code> and <code>resolveProxyClass</code> * methods ignore the class annotation strings and just use a * <code>null</code> codebase value when loading classes. After * this method has been invoked, then the * <code>resolveClass</code> and <code>resolveProxyClass</code> * methods use the class annotation strings as codebase values. **/ public void useCodebaseAnnotations() { usingCodebaseAnnotations = true; } /** * Returns the collection of context information objects that * was passed to this stream's constructor. **/ public Collection getObjectStreamContext() { return context; } /** * Resolves the appropriate {@link Class} object for the stream * class descriptor <code>classDesc</code>. * * <p><code>MarshalInputStream</code> implements this method as * follows: * * <p>This method first invokes this stream's {@link * #readAnnotation readAnnotation} method to read the annotation * string value (possibly <code>null</code>) for the class * descriptor. If <code>readAnnotation</code> throws an * exception, then this method throws that exception. Otherwise, * a codebase value is chosen as follows: if the {@link * #useCodebaseAnnotations useCodebaseAnnotations} method has been * invoked on this stream, then the codebase value chosen is the * value that was returned by <code>readAnnotation</code>; * otherwise, the codebase value chosen is <code>null</code>. * * <p>This method then invokes {@link ClassLoading#loadClass * ClassLoading.loadClass} with the chosen codebase value as the * first argument, the name of the class described by * <code>classDesc</code> as the second argument, and the * <code>defaultLoader</code>, * <code>verifyCodebaseIntegrity</code>, and * <code>verifierLoader</code> values that were passed to this * stream's constructor as the third, fourth, and fifth arguments. * If <code>ClassLoading.loadClass</code> throws a * <code>ClassNotFoundException</code> and the name of the class * described by <code>classDesc</code> equals the Java * programming language keyword for a primitive type or * <code>void</code>, then this method returns the * <code>Class</code> corresponding to that primitive type or * <code>void</code> ({@link Integer#TYPE} for <code>int</code>, * {@link Void#TYPE} for <code>void</code>, and so forth). * Otherwise, if <code>ClassLoading.loadClass</code> throws an * exception, this method throws that exception, and if it returns * normally, this method returns the <code>Class</code> returned * by <code>ClassLoading.loadClass</code>. * * @param classDesc the stream class descriptor to resolve * * @return the resolved class * * @throws IOException if <code>readAnnotation</code> throws an * <code>IOException</code>, or if * <code>ClassLoading.loadClass</code> throws a * <code>MalformedURLException</code> * * @throws ClassNotFoundException if <code>readAnnotation</code> * or <code>ClassLoading.loadClass</code> throws a * <code>ClassNotFoundException</code> * * @throws NullPointerException if <code>classDesc</code> is * <code>null</code> **/ protected Class resolveClass(ObjectStreamClass classDesc) throws IOException, ClassNotFoundException { if (classDesc == null) { throw new NullPointerException(); } // must always consume annotation written by MarshalOutputStream String annotation = readAnnotation(); String codebase = usingCodebaseAnnotations ? annotation : null; String name = classDesc.getName(); try { return ClassLoading.loadClass(codebase, name, defaultLoader, verifyCodebaseIntegrity, verifierLoader); } catch (ClassNotFoundException e) { Class c = (Class) specialClasses.get(name); if (c != null) { return c; } else { throw e; } } } /** * Resolves the appropriate {@link Class} object for the proxy * class described by the interface names * <code>interfaceNames</code>. * * <p><code>MarshalInputStream</code> implements this method as * follows: * * <p>This method first invokes this stream's {@link * #readAnnotation readAnnotation} method to read the annotation * string value (possibly <code>null</code>) for the class * descriptor. If <code>readAnnotation</code> throws an * exception, then this method throws that exception. Otherwise, * a codebase value is chosen as follows: if the {@link * #useCodebaseAnnotations useCodebaseAnnotations} method has been * invoked on this stream, then the codebase value chosen is the * value that was returned by <code>readAnnotation</code>; * otherwise, the codebase value chosen is <code>null</code>. * * <p>This method then invokes {@link ClassLoading#loadProxyClass * ClassLoading.loadProxyClass} with the chosen codebase value as * the first argument, <code>interfaceNames</code> as the second * argument, and the <code>defaultLoader</code>, * <code>verifyCodebaseIntegrity</code>, and * <code>verifierLoader</code> values that were passed to this * stream's constructor as the third, fourth, and fifth arguments. * If <code>ClassLoading.loadProxyClass</code> throws an * exception, then this method throws that exception. Otherwise, * this method returns the <code>Class</code> returned by * <code>ClassLoading.loadProxyClass</code>. * * @param interfaceNames the list of interface names that were * deserialized in the proxy class descriptor * * @return the resolved dynamic proxy class * * @throws IOException if <code>readAnnotation</code> throws an * <code>IOException</code>, or if * <code>ClassLoading.loadProxyClass</code> throws a * <code>MalformedURLException</code> * * @throws ClassNotFoundException if <code>readAnnotation</code> * or <code>ClassLoading.loadProxyClass</code> throws a * <code>ClassNotFoundException</code> * * @throws NullPointerException if <code>interfaceNames</code> is * <code>null</code> or if any element of * <code>interfaceNames</code> is <code>null</code> **/ protected Class resolveProxyClass(String[] interfaceNames) throws IOException, ClassNotFoundException { for (int i = 0; i < interfaceNames.length; i++) { if (interfaceNames[i] == null) { throw new NullPointerException(); } } // must always consume annotation written by MarshalOutputStream String annotation = readAnnotation(); String codebase = usingCodebaseAnnotations ? annotation : null; return ClassLoading.loadProxyClass(codebase, interfaceNames, defaultLoader, verifyCodebaseIntegrity, verifierLoader); } /** * Reads and returns a class annotation string value (possibly * <code>null</code>) that was written by a corresponding * <code>MarshalOutputStream</code> implementation. * * <p><code>MarshalInputStream</code> implements this method to * just read the annotation value from this stream using {@link * ObjectInputStream#readObject readObject}, and if * <code>readObject</code> returns a non-<code>null</code> value * that is not a <code>String</code>, an {@link * InvalidObjectException} is thrown. * * <p>A subclass can override this method to read the annotation * from a different location. * * @return the class annotation string value read (possibly * <code>null</code>) * * @throws IOException if an I/O exception occurs reading the * annotation * * @throws ClassNotFoundException if a * <code>ClassNotFoundException</code> occurs reading the * annotation **/ protected String readAnnotation() throws IOException, ClassNotFoundException { try { return (String) readObject(); } catch (ClassCastException e) { InvalidObjectException ioe = new InvalidObjectException("annotation not String or null"); ioe.initCause(e); throw ioe; } } }