/*
* 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;
}
}
}