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());
        
    }
}

Reply via email to