The attached code shares the same motivations as JEP 187 and may help
establish a baseline.
One more thing we're looking into (for a later release) is trusted class
lists to avoid deserialization attacks.
https://issues.apache.org/jira/browse/RIVER-362
http://www.ibm.com/developerworks/java/library/se-lookahead/index.html
Regards,
Peter.
/*
* 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 org.apache.river.api.io;
/**
* Portable is an extension to the Java Serialization Framework.
*
* Portable objects are suitable for use as immutable value objects with
* final fields that may be freely replicated, alternatively they are also
* suited as safely published thread safe mutable objects used
* to store service snapshots in a {@link net.jini.io.MarshalledInstance} for
* fail over replication to other nodes, or to upgrade a service.
* <p>
* Portable objects are not serialized, instead they are only created using an
* accessible constructor, public static factory method or builder object
making them
* more suitable for security; validating class invariants and concurrent code
* that relies on immutability and safe publication of fields using final or
* volatile.
* <p>
* Portable Objects are created remotely with an AccessControlContext
* containing one ProtectionDomain with a CodeSource that has a null location
* and null Certificates.
* Only minimal permissions granted to any location by the administrator will
apply.
* Minimal privilege is required to prevent remote instantiation
* of ClassLoader, Policy, SecurityManager, or any other type of object with
* security checks performed during construction. The developer is free to
* use privileged access, including login context from within constructors and
* methods.
* <p>
* The serial form of a Portable object is managed by PortableFactory
* and is solely dependant on the classes, parameters and signatures of methods
* or constructors.
* <p>
* Portable Objects with equal PortableFactory's shall be identical
* in Object form after un-marshaling within an identical jvm and one
* may be substituted for the other to reduce network traffic.
* <p>
* Portable Objects that are equal in Object form are not guaranteed to be equal
* in serial form. The implementor may enforce serial form equality by ensuring
* identical methods of creation are used for equal objects and document it in
* Javadoc. Portable objects equal in Object form at one node should also be
* equal after un-marshaling to a second remote node even when serial form
differs.
* <p>
* Portable Objects (boomerangs) that are duplicated across
* nodes may not be equal when returning to a local node after construction and
* redistribution on different nodes. Later versions of code may elect to
* use different classes, constructors or method signatures that result in
* inequality.
* <p>
* Portable objects while free to evolve and possibly having completely
different
* classes or being completely unequal after distribution to separate nodes,
* must always share a common public interface or superclass for referential
* purposes, this may of course be Object, however if it is, it should be stated
* clearly in Javadoc to avoid ClassCastException's upon un-marshaling.
* <p>
* Portable objects have no version, instead PortableFactory contains all
* information required to recreate any Portable Object.
* For this reason, Portable objects cannot be used as Entry
* objects, as they are dependant on published serial form. It may be possible
* in a later release to use Portable objects as fields in Entry objects, this
* is not supported presently.
* <p>
* Portable objects are highly recommended for use as value objects in domain
* driven design, they may also be used for value objects. PortableFactory can
* be used to create the root entity in an aggregate.
<p>
* Although final is not enforced, all fields should be final or volatile, safe
* construction must be honored 'this' must not be allowed to
* escape during construction, Portable objects will be exposed to multiple
* threads on multiple nodes, without external synchronization.
* <p>
* Portable objects are thread safe.
* <p>
* Do not use Portable if you don't intend to honor this contract, use
* Serializable instead.
* <p>
* Caveat:<br>
* Portable Objects cannot be stored directly in a
* {@link java.rmi.MarshalledObject}, a {@link net.jini.io.MarshalledInstance}
* must first be created and converted, also a Portable Object will be
* returned as a {@link PortableFactory} when {@link java.rmi.MarshalledObject}
* is un-marshaled, a {@link java.rmi.MarshalledObject} must first be
* converted to {@link net.jini.io.MarshalledInstance} before un-marshaling.
* <p>
* @author Peter Firmstone.
* @since 3.0.0
*/
public interface Portable {
/**
* Prepare for transport in a PortableObjectOutputStream.
* ObjectInput uses PortableFactory to create the Portable Object at the
* remote end using a constructor, static method or object method.
*
* @return A PortableFactory, PortableObjectInputStream uses PortableFactory
* to create a Portable Object at the remote end using a constructor,
* static method or an object method.
*/
PortableFactory factory();
}
/*
* 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 org.apache.river.api.io;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.Serializable;
import java.io.StreamCorruptedException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.ProtocolException;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.CodeSource;
import java.security.Guard;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.security.ProtectionDomain;
import java.security.cert.Certificate;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Portable form, required to create objects during de-serialization,
* using a constructor, static method or an Object method.
*
* This object must be Thread confined, it is not thread safe. It should be
* created on demand, it is primarily for use by {@link
PortableObjectInputStream}
* and {@link PortableObjectOutputStream}, it is created by {@link Portable}
* implementations.
*
* Internal state is guarded, arrays are not defensively copied.
*
* This is compatible with Version 2 of the Java Serialization Protocol.
*
* @author Peter Firmstone.
* @see Portable
* @see PortablePermission
* @see Serializable
* @see Externalizable
* @since 3.0.0
*/
public final class PortableFactory implements Externalizable {
private static final long serialVersionUID = 1L;
/* Guard private state */
private static final Guard distributable = new PortablePermission();
/* Minimal protocol to write primitives directly to stream, only for
parameters.
* Strings are written as Objects, since they are a special case, identical
* strings are sent by reference to the first and not duplicated.
* Class is also a special case, it too is not duplicated.
*
* Primitives are written separately to objects, the first value written
* to ObjectOutput is the length of the parameter array, each parameter is
* proceeded by a byte header indicating its type. For null values, only
the
* byte header is written to stream.
*
*/
private static byte VERSION = 1;
private static final byte BOOLEAN = 0;
private static final byte BYTE = 1;
private static final byte CHAR = 2;
private static final byte SHORT = 3;
private static final byte INT = 4;
private static final byte LONG = 5;
private static final byte FLOAT = 6;
private static final byte DOUBLE = 7;
private static final byte OBJECT = 8;
private static final byte NULL = 9;
// Serial Form
private Object classOrObject;
private String method;
private Class [] parameterTypes;
private Object [] parameters;
// Private local object state.
private int hash;
private boolean constructed; // default value is false.
/**
* Public method provided for java serialization framework.
*/
public PortableFactory(){
constructed = false;
}
/**
* Reflection is used at the remote end, with information provided to
* PortableFactory, to call a constructor, static method
* or an object method after de-serialization by PortableObjectInputStream.
* <p>
* Information given to PortableFactory is guarded by PortablePermission.
* <p>
* Instantiation of a Portable object at a remote endpoint proceeds as
follows:
* <ul><li>
* If factoryClassOrObject is a Class and methodName is null, a constructor
* of that Class is called reflectively with parameterTypes and parameters.
* </li><li>
* If factoryClassOrObject is a Class and methodName is defined, a static
* method with that name is called reflectively on that Class with
* parameterTypes and parameters.
* </li><li>
* If factoryClassOrObject is an Object and methodName is defined, a method
* with that name is called reflectively on that Object with
* parameterTypes and parameters.
* </li></ul>
* <p>
* Tip: Object versions of primitive values and String parameters
* are relatively fast as are primitive arrays.
* Object versions of primitive parameters are writen to DataOutput
* as primitives.
* <p>
* Constructor parameters must either be Serializable, Externalizable or
* Portable objects.
* <p>
* Creation is performed using only privileges granted to all CodeSource's,
* if there are no default grants set by the policy administrator, the
* creation will be performed with no privileges enabled.
* <p>
* To avoid security vulnerabilities, policy grants to any CodeSource
* should be very limited.
* <p>
*
* @param factoryClassOrObject will be used for constructor, factory static
method,
* or builder Object.
* @param methodName name of static factory method, null if using a
constructor.
* @param parameterTypes Type signature of method or constructor, or null.
* @param parameters array of Objects to be passed to constructor, or null.
* @see PortablePermission
*/
public PortableFactory(Object factoryClassOrObject, String methodName,
Class[] parameterTypes, Object [] parameters){
classOrObject = factoryClassOrObject;
method = methodName;
this.parameterTypes = parameterTypes;
this.parameters = parameters;
constructed = true;
if ( (parameterTypes != null && parameterTypes.length !=
parameters.length) || (parameters != null && parameters.length > 127))
throw new IllegalArgumentException("Array lengths don't match, or
arrays are too long,"
+ " parameter array limit 127, "
+ "you need to see a shrink if you need this many
parameters");
int hash = 7;
hash = 89 * hash + (this.classOrObject != null ?
this.classOrObject.hashCode() : 0);
hash = 89 * hash + (this.method != null ? this.method.hashCode() : 0);
hash = 89 * hash + Arrays.hashCode(this.parameterTypes);
hash = 89 * hash + Arrays.deepHashCode(this.parameters);
this.hash = hash;
}
Object create() throws IOException {
// Perform creation with minimum privileges, so remote code cannot
// create URLClassLoader etc.
// a CodeSource with null URL is used instead of a null CodeSource so
// that an administrator can grant limited default privileges if
desired.
// Eg to read a default system property.
AccessControlContext acc;
ProtectionDomain [] pd = new ProtectionDomain[1];
pd[0] = new ProtectionDomain(new CodeSource(null,(Certificate[]) null),
null);
acc = new AccessControlContext(pd);
try {
return AccessController.doPrivileged(new
PrivilegedExceptionAction(){
@Override
public Object run() throws Exception {
Method m;
Constructor c;
Class clazz;
boolean object;
if (classOrObject instanceof Class) {
clazz = (Class) classOrObject;
object = false;
}
else {
clazz = classOrObject.getClass();
object = true;
}
if (method != null){
m = clazz.getMethod(method, parameterTypes);
if (object) return m.invoke(classOrObject, parameters);
return m.invoke(null, parameters);
} else {
c = clazz.getConstructor(parameterTypes);
return c.newInstance(parameters);
}
}
}
, acc);
} catch (PrivilegedActionException ex) {
Logger.getLogger(PortableFactory.class.getName()).log(Level.SEVERE,
null, ex);
throw new IOException(ex);
}
}
// Inherit documentation
public void writeExternal(ObjectOutput out) throws IOException {
distributable.checkGuard(null);
if (! constructed) throw new IOException("Attempt to write blank
PortableFactory");
out.writeByte(VERSION);
out.writeObject(classOrObject);
out.writeObject(method);
/* don't clone arrays for defensive copies, it's up to constructing
* object to do so if needs to.
*/
out.writeObject(parameterTypes);
int l = parameterTypes != null ? parameterTypes.length : 0;
for (int i = 0; i < l; i++){
writeObject(parameters[i], out);
}
}
/**
* Object primitive values parameters are sent as their values to avoid
* Serialization overhead, this is only performed because primitives aren't
* Objects so can't be used directly. Primitive arrays are Objects so
* they can be used, therefore there's no need to handle them here.
*/
private void writeObject(Object o, ObjectOutput out ) throws IOException{
if (o == null) {
out.writeByte(NULL);
return;
}
if (o instanceof Boolean){
out.writeByte(BOOLEAN);
out.writeBoolean(((Boolean) o).booleanValue());
return;
}
if (o instanceof Byte){
out.writeByte(BYTE);
out.writeByte(((Byte)o).byteValue());
return;
}
if (o instanceof Character){
out.writeByte(CHAR);
out.writeChar(((Character)o).charValue());
return;
}
if (o instanceof Short){
out.writeByte(SHORT);
out.writeShort(((Short)o).shortValue());
return;
}
if (o instanceof Integer){
out.writeByte(INT);
out.writeInt(((Integer)o).intValue());
return;
}
if (o instanceof Long){
out.writeByte(LONG);
out.writeLong(((Long)o).longValue());
return;
}
if (o instanceof Float){
out.writeByte(FLOAT);
out.writeFloat(((Float)o).floatValue());
return;
}
if (o instanceof Double){
out.writeByte(DOUBLE);
out.writeDouble(((Double)o).doubleValue());
return;
}
// Arrays are treated as Objects, java serialization is relatively
// efficient with primitive arrays.
out.writeByte(OBJECT);
out.writeObject(o);
}
private Object readObject(ObjectInput in) throws IOException,
ClassNotFoundException{
byte b = in.readByte();
switch(b){
case BOOLEAN: boolean bool = in.readBoolean();
return Boolean.valueOf(bool);
case BYTE: byte bite = in.readByte();
return Byte.valueOf(bite);
case CHAR: char ch = in.readChar();
return Character.valueOf(ch);
case SHORT: short sh = in.readShort();
return Short.valueOf(sh);
case INT: int i = in.readInt();
return Integer.valueOf(i);
case LONG: long l = in.readLong();
return Long.valueOf(l);
case FLOAT: float f = in.readFloat();
return Float.valueOf(f);
case DOUBLE: double d = in.readDouble();
return Double.valueOf(d);
case OBJECT: return in.readObject();
case NULL: return null;
default: throw new StreamCorruptedException("out of range
byte read from stream");
}
}
// Inherit documentation.
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
/* If created during deserialisation, we needn't be defensive, this
* will never be shared with other threads and will be replaced by
* a fully constructed thread safe immutable object. */
if (constructed) throw new IllegalStateException("Object already
constructed");
constructed = true;
/* Don't defensively copy arrays, the object is used immediately after
* deserialization to construct the Portable Object, the fields are
* not accessed again, it is up to creator methods themselves to
* preserve invariants.
*/
byte version = in.readByte();
// In future we could potentially handle different versions, but for
now,
// bail out.
if (version != VERSION) throw new ProtocolException("Incompatible
PortableFactory protocol");
classOrObject = in.readObject();
method = (String) in.readObject();
parameterTypes = (Class[]) in.readObject();
int len = parameterTypes != null ? parameterTypes.length : 0;
parameters = len == 0 ? null : new Object[len];
for (int i = 0; i < len; i++){
parameters[i] = readObject(in);
}
int hash = 7;
hash = 89 * hash + (this.classOrObject != null ?
this.classOrObject.hashCode() : 0);
hash = 89 * hash + (this.method != null ? this.method.hashCode() : 0);
hash = 89 * hash + Arrays.hashCode(this.parameterTypes);
hash = 89 * hash + Arrays.deepHashCode(this.parameters);
this.hash = hash;
}
// equals and hashcode are implemented to avoid sending duplicates in
// object streams.
@Override
public int hashCode() {
return hash;
}
@Override
public boolean equals(Object o){
if (!(o instanceof PortableFactory)) return false;
if ( hash != o.hashCode()) return false;
PortableFactory other = (PortableFactory) o;
if ( classOrObject == null && other.classOrObject != null) return false;
if ( classOrObject != null && !
classOrObject.equals(other.classOrObject)) return false;
if ( method == null && other.method != null) return false;
if ( method != null && ! method.equals(other.method)) return false;
if (!Arrays.equals(parameterTypes, other.parameterTypes)) return false;
if (!Arrays.deepEquals(parameters, other.parameters)) return false;
return true;
// A locally constructed instance may be equal to a deserialized one.
}
}
/*
* 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 org.apache.river.api.io;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.security.AccessController;
import java.security.PrivilegedAction;
/**
* PortableObjectInputStream, an extension to Java serialization that allows
* for immutable and safely constructed objects
*
* @author Peter.
* @since 3.0.0
*/
public class PortableObjectInputStream extends ObjectInputStream {
public static ObjectInputStream create(InputStream in) throws IOException{
PortableObjectInputStream result = new PortableObjectInputStream(in);
AccessController.doPrivileged(new EnableResolveObject(result));
return result;
}
/**
* Caller must have SerializablePermission("enableSubstitution") to call
* this method.
*
* @param in
* @throws IOException
*/
protected PortableObjectInputStream(InputStream in) throws IOException{
super(in);
}
private void enableResolveObject(){
super.enableResolveObject(true);
}
protected Object resolveObject(Object o) throws IOException{
if (o instanceof PortableFactory) return ((PortableFactory)o).create();
return o;
}
private static class EnableResolveObject implements PrivilegedAction{
private final PortableObjectInputStream in;
EnableResolveObject(PortableObjectInputStream in){
this.in = in;
}
@Override
public Object run() {
in.enableResolveObject();
return null;
}
}
}
/*
* 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 org.apache.river.api.io;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.security.AccessController;
import java.security.PrivilegedAction;
/**
* PortableObjectOutputStream replaces @ref{Portable} instances
* in the OutputStream with a PortableFactory that recreates the
* Portable Object during unmarshalling.
*
* @author peter
* @since 3.0.0
*/
public class PortableObjectOutputStream extends ObjectOutputStream {
public static ObjectOutputStream create(OutputStream out) throws
IOException{
PortableObjectOutputStream result = new PortableObjectOutputStream(out);
AccessController.doPrivileged(new EnableReplaceObject(result));
return result;
}
protected PortableObjectOutputStream (OutputStream out) throws IOException{
super(out);
}
@Override
protected Object replaceObject(Object o){
if (o instanceof Portable) return ((Portable)o).factory();
return o;
}
private void enableReplaceObject(){
super.enableReplaceObject(true);
}
private static class EnableReplaceObject implements PrivilegedAction{
private final PortableObjectOutputStream out;
EnableReplaceObject(PortableObjectOutputStream out){
this.out = out;
}
@Override
public Object run() {
out.enableReplaceObject();
return null;
}
}
}
/*
* 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 org.apache.river.api.io;
import java.io.ObjectOutput;
import java.security.BasicPermission;
/**
* This Permission allows an object to be Portable by an implementation of
* ObjectOutput
*
* @author peter
* @see PortableFactory
* @see ObjectOutput
* @since 3.0.0
*/
public class PortablePermission extends BasicPermission{
private static final long serialVersionUID = 1L;
public PortablePermission(){
super("Portable");
}
}
/*
* 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 tests.support;
import java.io.Serializable;
import org.apache.river.api.io.Portable;
import org.apache.river.api.io.PortableFactory;
/**
*
* @author peter
*/
public class PortableObject implements Portable {
public static PortableObject create(String str){
return new PortableObject(str);
}
private final String testString;
/* 0 - constructor(String)
* 1 - static factory method
* 2 - builder
* 3 - constructor(Boolean)
* 4 - constructor(Character)
*/
private final int method;
public PortableObject(String str){
testString = str;
method = 0;
}
public PortableObject(Number num){
testString = num.toString();
method = 5;
}
public PortableObject(Character ch){
testString = ch.toString();
method = 4;
}
public PortableObject(Boolean b){
testString = b.toString();
method = 3;
}
public PortableObject(boolean b){
testString = Boolean.toString(b);
method = 6;
}
public PortableObject(String str, int method){
testString = str;
this.method = method;
}
public PortableFactory factory() {
Class[] signature = new Class[1];
Object[] parameters = new Object[1];
parameters[0] = testString;
switch (method){
case 0: signature[0] = String.class;
return new PortableFactory(this.getClass(), null,
signature, parameters );
case 1 :
signature[0] = String.class;
return new PortableFactory(this.getClass(), "create",
signature, parameters);
case 2:
Builder builder = new Builder().setString(testString);
return new PortableFactory(builder, "build", null, null);
case 3:
signature[0] = Boolean.class;
parameters[0] = Boolean.valueOf(testString);
return new PortableFactory(this.getClass(), null,
signature, parameters);
case 4:
signature[0] = Character.class;
parameters[0] = Character.valueOf(testString.charAt(0));
return new PortableFactory(this.getClass(), null,
signature, parameters);
case 6:
signature[0] = Boolean.TYPE;
parameters[0] = Boolean.valueOf(testString);
return new PortableFactory(this.getClass(), null,
signature, parameters);
default:
return null;
}
}
public String toString(){
return testString;
}
public static class Builder implements Serializable {
private static final long serialVersionUID = 1L;
private String str;
public Builder(){
}
public Builder setString(String str){
this.str = str;
return this;
}
public PortableObject build(){
return new PortableObject(str);
}
}
}
/*
* 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 org.apache.river.api.io;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import junit.framework.Assert;
import static org.junit.Assert.*;
import org.junit.Test;
/**
*
* @author peter
*/
public class PortableFactoryTest {
private final PortableFactory stringInstance;
private final String str;
public PortableFactoryTest() {
str = "Fat Bear";
Class[] cl = {(new char [0]).getClass()};
Object [] chars = {str.toCharArray()};
stringInstance = new PortableFactory(str.getClass(), null, cl , chars);
}
/**
* Test of hashCode method, of class PortableFactory.
*/
@Test
public void testHashCode() throws IOException {
System.out.println("hashCode");
int result = stringInstance.create().hashCode();
int expResult = str.hashCode();
assertEquals(expResult, result);
}
/**
* Test of equals method, of class PortableFactory.
*/
@Test
public void testEquals() throws IOException {
System.out.println("equals");
String expResult = str;
Class[] cl = {(new char [0]).getClass()};
Object [] chars = {str.toCharArray()};
// More than one way to create a string.
Object secondInstance = new PortableFactory(str.getClass(), null, cl,
chars );
PortableFactory thirdInstance =
new PortableFactory(new StringBuilder(str), "toString", null,
null );
Object result = stringInstance.create();
Assert.assertEquals(stringInstance, secondInstance);
// Demonstrate that equal objects can have different serial form.
Assert.assertNotSame(stringInstance, thirdInstance);
Assert.assertEquals(expResult, result);
result = thirdInstance.create();
Assert.assertEquals(expResult, result);
}
/**
* Test of writeExternal method, of class PortableFactory.
*/
@Test
public void testWriteExternal() throws Exception {
System.out.println("writeExternal");
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream outst = new ObjectOutputStream(out);
outst.writeObject(stringInstance);
ObjectInputStream in = new ObjectInputStream(new
ByteArrayInputStream(out.toByteArray()));
Object result = in.readObject();
assertEquals(stringInstance, result);
}
/**
* Test of readExternal method, of class PortableFactory.
*/
@Test
public void testReadExternal() throws Exception {
System.out.println("readExternal");
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream outst = new ObjectOutputStream(out);
outst.writeObject(stringInstance);
ObjectInputStream in = new ObjectInputStream(new
ByteArrayInputStream(out.toByteArray()));
Object result = in.readObject();
assertEquals(result, stringInstance);
}
}
/*
* 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 org.apache.river.api.io;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import org.junit.Test;
import static org.junit.Assert.*;
import tests.support.PortableObject;
/**
*
* @author peter
*/
public class PortableObjectOutputStreamTest {
public PortableObjectOutputStreamTest() {
}
/**
* Test of create method, of class PortableObjectOutputStream.
*/
@Test
public void testCreate() throws Exception {
System.out.println("create: test constructor, static method and object
method");
PortableObject expResult = new PortableObject("Testing");
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream outst = PortableObjectOutputStream.create(out);
outst.writeObject(expResult);
ObjectInputStream in = PortableObjectInputStream.create(new
ByteArrayInputStream(out.toByteArray()));
Object result = in.readObject();
assertEquals(expResult.toString(), result.toString());
out = new ByteArrayOutputStream();
outst = PortableObjectOutputStream.create(out);
expResult = new PortableObject("Testing", 1);
outst.writeObject(expResult);
in = PortableObjectInputStream.create(new
ByteArrayInputStream(out.toByteArray()));
result = in.readObject();
assertEquals(expResult.toString(), result.toString());
expResult = new PortableObject("Testing", 2);
outst.writeObject(expResult);
in = PortableObjectInputStream.create(new
ByteArrayInputStream(out.toByteArray()));
result = in.readObject();
assertEquals(expResult.toString(), result.toString());
}
@Test
public void testPrimitives() throws Exception {
System.out.println("create: test constructor, static method and object
method");
PortableObject expResult = new PortableObject(Boolean.TRUE);
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream outst = PortableObjectOutputStream.create(out);
outst.writeObject(expResult);
ObjectInputStream in = PortableObjectInputStream.create(new
ByteArrayInputStream(out.toByteArray()));
Object result = in.readObject();
assertEquals(expResult.toString(), result.toString());
out = new ByteArrayOutputStream();
outst = PortableObjectOutputStream.create(out);
expResult = new PortableObject(true);
outst.writeObject(expResult);
in = PortableObjectInputStream.create(new
ByteArrayInputStream(out.toByteArray()));
result = in.readObject();
assertEquals(expResult.toString(), result.toString());
}
}