Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.jboss.marshalling.river.RiverUnmarshaller Maven / Gradle / Ivy
Go to download
JBoss Marshalling OSGi Bundle with API and implementations
/*
* JBoss, Home of Professional Open Source.
* Copyright 2014 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed 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.jboss.marshalling.river;
import static java.lang.System.getSecurityManager;
import static java.security.AccessController.doPrivileged;
import static org.jboss.marshalling.river.Protocol.*;
import java.io.Externalizable;
import java.io.IOException;
import java.io.InvalidClassException;
import java.io.InvalidObjectException;
import java.io.NotSerializableException;
import java.io.ObjectInputValidation;
import java.io.StreamCorruptedException;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.EnumSet;
import java.util.EnumMap;
import java.util.AbstractCollection;
import java.util.AbstractList;
import java.util.AbstractQueue;
import java.util.AbstractSequentialList;
import java.util.AbstractSet;
import java.util.Vector;
import java.util.Stack;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import org.jboss.marshalling.AbstractUnmarshaller;
import org.jboss.marshalling.ByteInput;
import org.jboss.marshalling.Externalizer;
import org.jboss.marshalling.MarshallingConfiguration;
import org.jboss.marshalling.Pair;
import org.jboss.marshalling.UTFUtils;
import org.jboss.marshalling.TraceInformation;
import org.jboss.marshalling._private.GetDeclaredFieldAction;
import org.jboss.marshalling._private.GetUnsafeAction;
import org.jboss.marshalling.reflect.SerializableClass;
import org.jboss.marshalling.reflect.SerializableClassRegistry;
import org.jboss.marshalling.reflect.SerializableField;
import org.jboss.marshalling.util.FlatNavigableMap;
import org.jboss.marshalling.util.FlatNavigableSet;
import sun.misc.Unsafe;
/**
*
*/
public class RiverUnmarshaller extends AbstractUnmarshaller {
private final ArrayList instanceCache;
private final ArrayList classCache;
private final SerializableClassRegistry registry;
private int version;
private int depth;
private BlockUnmarshaller blockUnmarshaller;
private RiverObjectInputStream objectInputStream;
private SortedSet validators;
private int validatorSeq;
private static final Object UNRESOLVED = new Object();
private static final Field proxyInvocationHandler;
private static final long proxyInvocationHandlerOffset;
private static class UnsafeHolder {
// WFLY-14077 Never ever refactor out unsafe field from this wrapper class
private static final Unsafe unsafe = getSecurityManager() == null ? GetUnsafeAction.INSTANCE.run() : doPrivileged(GetUnsafeAction.INSTANCE);
}
static {
doPrivileged(new PrivilegedAction() {
// WFLY-14077 Never ever remove this doPrivileged() call
@Override
public Void run() {
try {
Class.forName("sun.misc.Unsafe", true, UnsafeHolder.class.getClassLoader());
} catch (Exception ignored) {
// do nothing
}
return null;
}
});
if (getSecurityManager() == null) {
proxyInvocationHandler = new GetDeclaredFieldAction(Proxy.class, "h").run();
} else {
proxyInvocationHandler = doPrivileged(new GetDeclaredFieldAction(Proxy.class, "h"));
}
proxyInvocationHandlerOffset = UnsafeHolder.unsafe.objectFieldOffset(proxyInvocationHandler);
}
protected RiverUnmarshaller(final RiverMarshallerFactory marshallerFactory, final SerializableClassRegistry registry, final MarshallingConfiguration configuration) {
super(marshallerFactory, configuration);
this.registry = registry;
instanceCache = new ArrayList(configuration.getInstanceCount());
classCache = new ArrayList(configuration.getClassCount());
}
public void clearInstanceCache() throws IOException {
instanceCache.clear();
}
public void clearClassCache() throws IOException {
clearInstanceCache();
classCache.clear();
}
public void close() throws IOException {
finish();
}
public void finish() throws IOException {
super.finish();
blockUnmarshaller = null;
objectInputStream = null;
}
private BlockUnmarshaller getBlockUnmarshaller() {
final BlockUnmarshaller blockUnmarshaller = this.blockUnmarshaller;
return blockUnmarshaller == null ? this.blockUnmarshaller = new BlockUnmarshaller(this) : blockUnmarshaller;
}
private final PrivilegedExceptionAction createObjectInputStreamAction = new PrivilegedExceptionAction() {
public RiverObjectInputStream run() throws IOException {
return new RiverObjectInputStream(RiverUnmarshaller.this, getBlockUnmarshaller());
}
};
private RiverObjectInputStream getObjectInputStream() throws IOException {
final RiverObjectInputStream objectInputStream = this.objectInputStream;
return objectInputStream == null ? this.objectInputStream = createObjectInputStream() : objectInputStream;
}
private RiverObjectInputStream createObjectInputStream() throws IOException {
if (getSecurityManager() == null) {
return new RiverObjectInputStream(RiverUnmarshaller.this, getBlockUnmarshaller());
} else {
try {
return doPrivileged(createObjectInputStreamAction);
} catch (PrivilegedActionException e) {
throw (IOException) e.getCause();
}
}
}
Object doReadNestedObject(final boolean unshared, final String enclosingClassName) throws ClassNotFoundException, IOException {
try {
return doReadObject(unshared);
} catch (IOException e) {
TraceInformation.addIncompleteObjectInformation(e, enclosingClassName);
throw e;
} catch (ClassNotFoundException e) {
TraceInformation.addIncompleteObjectInformation(e, enclosingClassName);
throw e;
} catch (RuntimeException e) {
TraceInformation.addIncompleteObjectInformation(e, enclosingClassName);
throw e;
}
}
Object doReadCollectionObject(final boolean unshared, final int idx, final int size, final boolean discardMissing) throws ClassNotFoundException, IOException {
try {
return doReadObject(unshared, discardMissing);
} catch (IOException e) {
TraceInformation.addIndexInformation(e, idx, size, TraceInformation.IndexType.ELEMENT);
throw e;
} catch (ClassNotFoundException e) {
TraceInformation.addIndexInformation(e, idx, size, TraceInformation.IndexType.ELEMENT);
throw e;
} catch (RuntimeException e) {
TraceInformation.addIndexInformation(e, idx, size, TraceInformation.IndexType.ELEMENT);
throw e;
}
}
Object doReadMapObject(final boolean unshared, final int idx, final int size, final boolean key, final boolean discardMissing) throws ClassNotFoundException, IOException {
try {
return doReadObject(unshared, discardMissing);
} catch (IOException e) {
TraceInformation.addIndexInformation(e, idx, size, key ? TraceInformation.IndexType.MAP_KEY : TraceInformation.IndexType.MAP_VALUE);
throw e;
} catch (ClassNotFoundException e) {
TraceInformation.addIndexInformation(e, idx, size, key ? TraceInformation.IndexType.MAP_KEY : TraceInformation.IndexType.MAP_VALUE);
throw e;
} catch (RuntimeException e) {
TraceInformation.addIndexInformation(e, idx, size, key ? TraceInformation.IndexType.MAP_KEY : TraceInformation.IndexType.MAP_VALUE);
throw e;
}
}
protected Object doReadObject(final boolean unshared) throws ClassNotFoundException, IOException {
final Object obj = doReadObject(readUnsignedByte(), unshared, false);
if (depth == 0) {
final SortedSet validators = this.validators;
if (validators != null) {
this.validators = null;
validatorSeq = 0;
for (Validator validator : validators) {
validator.getValidation().validateObject();
}
}
}
return obj;
}
Object doReadObject(final boolean unshared, final boolean discardMissing) throws IOException, ClassNotFoundException {
return doReadObject(readUnsignedByte(), unshared, discardMissing);
}
@SuppressWarnings({ "unchecked" })
Object doReadObject(int leadByte, final boolean unshared, final boolean discardMissing) throws IOException, ClassNotFoundException {
depth ++;
try {
for (;;) switch (leadByte) {
case ID_NULL: {
return null;
}
case ID_REPEAT_OBJECT_FAR: {
if (unshared) {
throw new InvalidObjectException("Attempt to read a backreference as unshared");
}
final int index = readInt();
try {
final Object obj = instanceCache.get(index);
if (obj != UNRESOLVED) return obj;
} catch (IndexOutOfBoundsException e) {
}
throw new InvalidObjectException("Attempt to read a backreference with an invalid ID (absolute " + index + ")");
}
case ID_REPEAT_OBJECT_NEAR: {
if (unshared) {
throw new InvalidObjectException("Attempt to read a backreference as unshared");
}
final int index = readByte() | 0xffffff00;
try {
final Object obj = instanceCache.get(index + instanceCache.size());
if (obj != UNRESOLVED) return obj;
} catch (IndexOutOfBoundsException e) {
}
throw new InvalidObjectException("Attempt to read a backreference with an invalid ID (relative near " + index + ")");
}
case ID_REPEAT_OBJECT_NEARISH: {
if (unshared) {
throw new InvalidObjectException("Attempt to read a backreference as unshared");
}
final int index = readShort() | 0xffff0000;
try {
final Object obj = instanceCache.get(index + instanceCache.size());
if (obj != UNRESOLVED) return obj;
} catch (IndexOutOfBoundsException e) {
}
throw new InvalidObjectException("Attempt to read a backreference with an invalid ID (relative nearish " + index + ")");
}
case ID_NEW_OBJECT:
case ID_NEW_OBJECT_UNSHARED: {
if (unshared != (leadByte == ID_NEW_OBJECT_UNSHARED)) {
throw sharedMismatch();
}
return replace(doReadNewObject(readUnsignedByte(), unshared, discardMissing));
}
// v2 string types
case ID_STRING_EMPTY: {
return "";
}
case ID_STRING_SMALL: {
// ignore unshared setting
int length = readUnsignedByte();
final String s = UTFUtils.readUTFBytes(this, length == 0 ? 0x100 : length);
instanceCache.add(s);
return s;
}
case ID_STRING_MEDIUM: {
// ignore unshared setting
int length = readUnsignedShort();
final String s = UTFUtils.readUTFBytes(this, length == 0 ? 0x10000 : length);
instanceCache.add(s);
return s;
}
case ID_STRING_LARGE: {
// ignore unshared setting
int length = readInt();
if (length <= 0) {
throw new StreamCorruptedException("Invalid length value for string in stream (" + length + ")");
}
final String s = UTFUtils.readUTFBytes(this, length);
instanceCache.add(s);
return s;
}
case ID_ARRAY_EMPTY:
case ID_ARRAY_EMPTY_UNSHARED: {
if (unshared != (leadByte == ID_ARRAY_EMPTY_UNSHARED)) {
throw sharedMismatch();
}
final ArrayList instanceCache = this.instanceCache;
final int idx = instanceCache.size();
final Object obj = Array.newInstance(doReadClassDescriptor(readUnsignedByte(), true).getType(), 0);
instanceCache.add(obj);
final Object resolvedObject = objectResolver.readResolve(obj);
if (unshared) {
instanceCache.set(idx, UNRESOLVED);
} else if (obj != resolvedObject) {
instanceCache.set(idx, resolvedObject);
}
return replace(obj);
}
case ID_ARRAY_SMALL:
case ID_ARRAY_SMALL_UNSHARED: {
if (unshared != (leadByte == ID_ARRAY_SMALL_UNSHARED)) {
throw sharedMismatch();
}
final int len = readUnsignedByte();
return replace(doReadArray(len == 0 ? 0x100 : len, unshared, discardMissing));
}
case ID_ARRAY_MEDIUM:
case ID_ARRAY_MEDIUM_UNSHARED: {
if (unshared != (leadByte == ID_ARRAY_MEDIUM_UNSHARED)) {
throw sharedMismatch();
}
final int len = readUnsignedShort();
return replace(doReadArray(len == 0 ? 0x10000 : len, unshared, discardMissing));
}
case ID_ARRAY_LARGE:
case ID_ARRAY_LARGE_UNSHARED: {
if (unshared != (leadByte == ID_ARRAY_LARGE_UNSHARED)) {
throw sharedMismatch();
}
final int len = readInt();
if (len <= 0) {
throw new StreamCorruptedException("Invalid length value for array in stream (" + len + ")");
}
return replace(doReadArray(len, unshared, discardMissing));
}
case ID_PREDEFINED_OBJECT: {
if (unshared) {
throw new InvalidObjectException("Attempt to read a predefined object as unshared");
}
return objectTable.readObject(this);
}
case ID_BOOLEAN_OBJECT_TRUE: {
return replace(objectResolver.readResolve(Boolean.TRUE));
}
case ID_BOOLEAN_OBJECT_FALSE: {
return replace(objectResolver.readResolve(Boolean.FALSE));
}
case ID_BYTE_OBJECT: {
return replace(objectResolver.readResolve(Byte.valueOf(readByte())));
}
case ID_SHORT_OBJECT: {
return replace(objectResolver.readResolve(Short.valueOf(readShort())));
}
case ID_INTEGER_OBJECT: {
return replace(objectResolver.readResolve(Integer.valueOf(readInt())));
}
case ID_LONG_OBJECT: {
return replace(objectResolver.readResolve(Long.valueOf(readLong())));
}
case ID_FLOAT_OBJECT: {
return replace(objectResolver.readResolve(Float.valueOf(readFloat())));
}
case ID_DOUBLE_OBJECT: {
return replace(objectResolver.readResolve(Double.valueOf(readDouble())));
}
case ID_CHARACTER_OBJECT: {
return replace(objectResolver.readResolve(Character.valueOf(readChar())));
}
case ID_PRIM_BYTE: {
return byte.class;
}
case ID_PRIM_BOOLEAN: {
return boolean.class;
}
case ID_PRIM_CHAR: {
return char.class;
}
case ID_PRIM_DOUBLE: {
return double.class;
}
case ID_PRIM_FLOAT: {
return float.class;
}
case ID_PRIM_INT: {
return int.class;
}
case ID_PRIM_LONG: {
return long.class;
}
case ID_PRIM_SHORT: {
return short.class;
}
case ID_VOID: {
return void.class;
}
case ID_BYTE_CLASS: {
return Byte.class;
}
case ID_BOOLEAN_CLASS: {
return Boolean.class;
}
case ID_CHARACTER_CLASS: {
return Character.class;
}
case ID_DOUBLE_CLASS: {
return Double.class;
}
case ID_FLOAT_CLASS: {
return Float.class;
}
case ID_INTEGER_CLASS: {
return Integer.class;
}
case ID_LONG_CLASS: {
return Long.class;
}
case ID_SHORT_CLASS: {
return Short.class;
}
case ID_VOID_CLASS: {
return Void.class;
}
case ID_OBJECT_CLASS: {
return Object.class;
}
case ID_CLASS_CLASS: {
return Class.class;
}
case ID_STRING_CLASS: {
return String.class;
}
case ID_ENUM_CLASS: {
return Enum.class;
}
case ID_BYTE_ARRAY_CLASS: {
return byte[].class;
}
case ID_BOOLEAN_ARRAY_CLASS: {
return boolean[].class;
}
case ID_CHAR_ARRAY_CLASS: {
return char[].class;
}
case ID_DOUBLE_ARRAY_CLASS: {
return double[].class;
}
case ID_FLOAT_ARRAY_CLASS: {
return float[].class;
}
case ID_INT_ARRAY_CLASS: {
return int[].class;
}
case ID_LONG_ARRAY_CLASS: {
return long[].class;
}
case ID_SHORT_ARRAY_CLASS: {
return short[].class;
}
case ID_CC_ARRAY_LIST: {
return ArrayList.class;
}
case ID_CC_LINKED_LIST: {
return LinkedList.class;
}
case ID_CC_HASH_SET: {
return HashSet.class;
}
case ID_CC_LINKED_HASH_SET: {
return LinkedHashSet.class;
}
case ID_CC_TREE_SET: {
return TreeSet.class;
}
case ID_CC_IDENTITY_HASH_MAP: {
return IdentityHashMap.class;
}
case ID_CC_HASH_MAP: {
return HashMap.class;
}
case ID_CC_HASHTABLE: {
return Hashtable.class;
}
case ID_CC_LINKED_HASH_MAP: {
return LinkedHashMap.class;
}
case ID_CC_TREE_MAP: {
return TreeMap.class;
}
case ID_CC_ENUM_SET_PROXY: {
return enumSetProxyClass;
}
case ID_CC_ENUM_SET: {
return EnumSet.class;
}
case ID_CC_ENUM_MAP: {
return EnumMap.class;
}
case ID_ABSTRACT_COLLECTION: {
return AbstractCollection.class;
}
case ID_ABSTRACT_LIST: {
return AbstractList.class;
}
case ID_ABSTRACT_QUEUE: {
return AbstractQueue.class;
}
case ID_ABSTRACT_SEQUENTIAL_LIST: {
return AbstractSequentialList.class;
}
case ID_ABSTRACT_SET: {
return AbstractSet.class;
}
case ID_CC_CONCURRENT_HASH_MAP: {
return ConcurrentHashMap.class;
}
case ID_CC_COPY_ON_WRITE_ARRAY_LIST: {
return CopyOnWriteArrayList.class;
}
case ID_CC_COPY_ON_WRITE_ARRAY_SET: {
return CopyOnWriteArraySet.class;
}
case ID_CC_VECTOR: {
return Vector.class;
}
case ID_CC_STACK: {
return Stack.class;
}
case ID_CC_NCOPIES: {
return nCopiesClass;
}
case ID_UNMODIFIABLE_COLLECTION: {
final int idx = instanceCache.size();
instanceCache.add(UNRESOLVED);
final Object obj = Collections.unmodifiableCollection((Collection>) doReadNestedObject(false, "Collections#unmodifiableCollection()"));
final Object resolvedObject = objectResolver.readResolve(obj);
if (! unshared) {
instanceCache.set(idx, resolvedObject);
}
return replace(resolvedObject);
}
case ID_UNMODIFIABLE_SET: {
final int idx = instanceCache.size();
instanceCache.add(UNRESOLVED);
final Object obj = Collections.unmodifiableSet((Set>) doReadNestedObject(false, "Collections#unmodifiableSet()"));
final Object resolvedObject = objectResolver.readResolve(obj);
if (! unshared) {
instanceCache.set(idx, resolvedObject);
}
return replace(resolvedObject);
}
case ID_UNMODIFIABLE_LIST: {
final int idx = instanceCache.size();
instanceCache.add(UNRESOLVED);
final Object obj = Collections.unmodifiableList((List>) doReadNestedObject(false, "Collections#unmodifiableList()"));
final Object resolvedObject = objectResolver.readResolve(obj);
if (! unshared) {
instanceCache.set(idx, resolvedObject);
}
return replace(resolvedObject);
}
case ID_UNMODIFIABLE_MAP: {
final int idx = instanceCache.size();
instanceCache.add(UNRESOLVED);
final Object obj = Collections.unmodifiableMap((Map, ?>) doReadNestedObject(false, "Collections#unmodifiableMap()"));
final Object resolvedObject = objectResolver.readResolve(obj);
if (! unshared) {
instanceCache.set(idx, resolvedObject);
}
return replace(resolvedObject);
}
case ID_UNMODIFIABLE_SORTED_SET: {
final int idx = instanceCache.size();
instanceCache.add(UNRESOLVED);
final Object obj = Collections.unmodifiableSortedSet((SortedSet>) doReadNestedObject(false, "Collections#unmodifiableSortedSet()"));
final Object resolvedObject = objectResolver.readResolve(obj);
if (! unshared) {
instanceCache.set(idx, resolvedObject);
}
return replace(resolvedObject);
}
case ID_UNMODIFIABLE_SORTED_MAP: {
final int idx = instanceCache.size();
instanceCache.add(UNRESOLVED);
final Object obj = Collections.unmodifiableSortedMap((SortedMap, ?>) doReadNestedObject(false, "Collections#unmodifiableSortedMap()"));
final Object resolvedObject = objectResolver.readResolve(obj);
if (! unshared) {
instanceCache.set(idx, resolvedObject);
}
return replace(resolvedObject);
}
case ID_UNMODIFIABLE_MAP_ENTRY_SET: {
final int idx = instanceCache.size();
instanceCache.add(UNRESOLVED);
final Object obj;
try {
obj = Protocol.unmodifiableMapEntrySetCtor.newInstance(doReadNestedObject(false, "Collections#unmodifiableSortedMap:entrySet()"));
} catch (Exception e) {
final InvalidObjectException ioe = new InvalidObjectException("Problem instantiating unmodifiable map entry set");
ioe.initCause(e);
throw ioe;
}
final Object resolvedObject = objectResolver.readResolve(obj);
if (! unshared) {
instanceCache.set(idx, resolvedObject);
}
return replace(resolvedObject);
}
case ID_SINGLETON_LIST_OBJECT: {
final int idx = instanceCache.size();
instanceCache.add(UNRESOLVED);
final Object obj = Collections.singletonList(doReadNestedObject(false, "Collections#singletonList()"));
final Object resolvedObject = objectResolver.readResolve(obj);
if (! unshared) {
instanceCache.set(idx, resolvedObject);
}
return replace(resolvedObject);
}
case ID_SINGLETON_SET_OBJECT: {
final int idx = instanceCache.size();
instanceCache.add(UNRESOLVED);
final Object obj = Collections.singleton(doReadNestedObject(false, "Collections#singleton()"));
final Object resolvedObject = objectResolver.readResolve(obj);
if (! unshared) {
instanceCache.set(idx, resolvedObject);
}
return replace(resolvedObject);
}
case ID_SINGLETON_MAP_OBJECT: {
final int idx = instanceCache.size();
instanceCache.add(UNRESOLVED);
final Object obj = Collections.singletonMap(doReadNestedObject(false, "Collections#singletonMap() [key]"), doReadNestedObject(false, "Collections#singletonMap() [value]"));
final Object resolvedObject = objectResolver.readResolve(obj);
if (! unshared) {
instanceCache.set(idx, resolvedObject);
}
return replace(resolvedObject);
}
case ID_REVERSE_ORDER2_OBJECT: {
final int idx = instanceCache.size();
instanceCache.add(UNRESOLVED);
final Object obj = Collections.reverseOrder((Comparator>) doReadNestedObject(false, "Collections#reverseOrder()"));
final Object resolvedObject = objectResolver.readResolve(obj);
if (! unshared) {
instanceCache.set(idx, resolvedObject);
}
return replace(resolvedObject);
}
case ID_EMPTY_LIST_OBJECT: {
return Collections.emptyList();
}
case ID_EMPTY_SET_OBJECT: {
return Collections.emptySet();
}
case ID_EMPTY_MAP_OBJECT: {
return Collections.emptyMap();
}
case ID_REVERSE_ORDER_OBJECT: {
return Collections.reverseOrder();
}
case ID_COLLECTION_EMPTY:
case ID_COLLECTION_EMPTY_UNSHARED:
case ID_COLLECTION_SMALL:
case ID_COLLECTION_SMALL_UNSHARED:
case ID_COLLECTION_MEDIUM:
case ID_COLLECTION_MEDIUM_UNSHARED:
case ID_COLLECTION_LARGE:
case ID_COLLECTION_LARGE_UNSHARED:
{
final int len;
switch (leadByte) {
case ID_COLLECTION_EMPTY:
case ID_COLLECTION_EMPTY_UNSHARED: {
len = 0;
break;
}
case ID_COLLECTION_SMALL:
case ID_COLLECTION_SMALL_UNSHARED: {
int b = readUnsignedByte();
len = b == 0 ? 0x100 : b;
break;
}
case ID_COLLECTION_MEDIUM:
case ID_COLLECTION_MEDIUM_UNSHARED: {
int b = readUnsignedShort();
len = b == 0 ? 0x10000 : b;
break;
}
case ID_COLLECTION_LARGE:
case ID_COLLECTION_LARGE_UNSHARED: {
len = readInt();
break;
}
default: {
throw new IllegalStateException();
}
}
final int id = readUnsignedByte();
switch (id) {
case ID_CC_ARRAY_LIST: {
return replace(readCollectionData(unshared, -1, len, new ArrayList(len), discardMissing));
}
case ID_CC_HASH_SET: {
return replace(readCollectionData(unshared, -1, len, new HashSet(len), discardMissing));
}
case ID_CC_LINKED_HASH_SET: {
return replace(readCollectionData(unshared, -1, len, new LinkedHashSet(len), discardMissing));
}
case ID_CC_LINKED_LIST: {
return replace(readCollectionData(unshared, -1, len, new LinkedList(), discardMissing));
}
case ID_CC_TREE_SET: {
int idx = instanceCache.size();
instanceCache.add(UNRESOLVED);
Comparator comp = (Comparator)doReadNestedObject(false, "java.util.TreeSet comparator");
return replace(readSortedSetData(unshared, idx, len, new TreeSet(comp), discardMissing));
}
case ID_CC_ENUM_SET_PROXY: {
final ClassDescriptor nestedDescriptor = doReadClassDescriptor(readUnsignedByte(), true);
final Class extends Enum> elementType = nestedDescriptor.getType().asSubclass(Enum.class);
return replace(readCollectionData(unshared, -1, len, EnumSet.noneOf(elementType), discardMissing));
}
case ID_CC_VECTOR: {
return replace(readCollectionData(unshared, -1, len, new Vector(len), discardMissing));
}
case ID_CC_STACK: {
return replace(readCollectionData(unshared, -1, len, new Stack(), discardMissing));
}
case ID_CC_ARRAY_DEQUE: {
return replace(readCollectionData(unshared, -1, len, new ArrayDeque(len), discardMissing));
}
case ID_CC_HASH_MAP: {
return replace(readMapData(unshared, -1, len, new HashMap(len), discardMissing));
}
case ID_CC_HASHTABLE: {
return replace(readMapData(unshared, -1, len, new Hashtable(len), discardMissing));
}
case ID_CC_IDENTITY_HASH_MAP: {
return replace(readMapData(unshared, -1, len, new IdentityHashMap(len), discardMissing));
}
case ID_CC_LINKED_HASH_MAP: {
return replace(readMapData(unshared, -1, len, new LinkedHashMap(len), discardMissing));
}
case ID_CC_TREE_MAP: {
int idx = instanceCache.size();
instanceCache.add(UNRESOLVED);
Comparator comp = (Comparator)doReadNestedObject(false, "java.util.TreeSet comparator");
return replace(readSortedMapData(unshared, idx, len, new TreeMap(comp), discardMissing));
}
case ID_CC_ENUM_MAP: {
int idx = instanceCache.size();
instanceCache.add(UNRESOLVED);
final ClassDescriptor nestedDescriptor = doReadClassDescriptor(readUnsignedByte(), true);
final Class extends Enum> elementType = nestedDescriptor.getType().asSubclass(Enum.class);
return replace(readMapData(unshared, idx, len, new EnumMap(elementType), discardMissing));
}
case ID_CC_NCOPIES: {
final int idx = instanceCache.size();
instanceCache.add(UNRESOLVED);
final Object obj = Collections.nCopies(len, doReadNestedObject(false, "n-copies member object"));
final Object resolvedObject = objectResolver.readResolve(obj);
if (! unshared) {
instanceCache.set(idx, resolvedObject);
}
return replace(resolvedObject);
}
default: {
throw new StreamCorruptedException("Unexpected byte found when reading a collection type: " + leadByte);
}
}
}
case ID_PAIR: {
final int idx = instanceCache.size();
instanceCache.add(UNRESOLVED);
final Object obj = Pair.create(doReadNestedObject(unshared, "java.util.marshalling.Pair [A]"), doReadNestedObject(unshared, "java.util.marshalling.Pair [B]"));
final Object resolvedObject = objectResolver.readResolve(obj);
if (! unshared) {
instanceCache.set(idx, resolvedObject);
}
return replace(resolvedObject);
}
case ID_CLEAR_CLASS_CACHE: {
if (depth > 1) {
throw new StreamCorruptedException("ID_CLEAR_CLASS_CACHE token in the middle of stream processing");
}
classCache.clear();
instanceCache.clear();
leadByte = readUnsignedByte();
continue;
}
case ID_CLEAR_INSTANCE_CACHE: {
if (depth > 1) {
throw new StreamCorruptedException("ID_CLEAR_INSTANCE_CACHE token in the middle of stream processing");
}
instanceCache.clear();
leadByte = readUnsignedByte();
continue;
}
default: {
throw new StreamCorruptedException("Unexpected byte found when reading an object: " + leadByte);
}
}
} finally {
depth --;
}
}
@SuppressWarnings({ "unchecked" })
private Object readCollectionData(final boolean unshared, int cacheIdx, final int len, final Collection target, final boolean discardMissing) throws ClassNotFoundException, IOException {
final ArrayList instanceCache = this.instanceCache;
final int idx;
if (cacheIdx == -1) {
idx = instanceCache.size();
instanceCache.add(target);
} else {
idx = cacheIdx;
instanceCache.set(idx, target);
}
for (int i = 0; i < len; i ++) {
target.add(doReadCollectionObject(false, i, len, discardMissing));
}
final Object resolvedObject = objectResolver.readResolve(target);
instanceCache.set(idx, unshared ? UNRESOLVED : resolvedObject);
return resolvedObject;
}
@SuppressWarnings({ "unchecked" })
private Object readSortedSetData(final boolean unshared, int cacheIdx, final int len, final SortedSet target, final boolean discardMissing) throws ClassNotFoundException, IOException {
final ArrayList instanceCache = this.instanceCache;
final int idx;
final FlatNavigableSet filler = new FlatNavigableSet(target.comparator());
if (cacheIdx == -1) {
idx = instanceCache.size();
instanceCache.add(target);
} else {
idx = cacheIdx;
instanceCache.set(idx, target);
}
for (int i = 0; i < len; i ++) {
filler.add(doReadCollectionObject(false, i, len, discardMissing));
}
target.addAll(filler);
final Object resolvedObject = objectResolver.readResolve(target);
instanceCache.set(idx, unshared ? UNRESOLVED : resolvedObject);
return resolvedObject;
}
@SuppressWarnings({ "unchecked" })
private Object readMapData(final boolean unshared, int cacheIdx, final int len, final Map target, final boolean discardMissing) throws ClassNotFoundException, IOException {
final ArrayList instanceCache = this.instanceCache;
final int idx;
if (cacheIdx == -1) {
idx = instanceCache.size();
instanceCache.add(target);
} else {
idx = cacheIdx;
instanceCache.set(idx, target);
}
for (int i = 0; i < len; i ++) {
target.put(doReadMapObject(false, i, len, true, discardMissing), doReadMapObject(false, i, len, false, discardMissing));
}
final Object resolvedObject = objectResolver.readResolve(target);
instanceCache.set(idx, unshared ? UNRESOLVED : resolvedObject);
return resolvedObject;
}
@SuppressWarnings({ "unchecked" })
private Object readSortedMapData(final boolean unshared, int cacheIdx, final int len, final SortedMap target, final boolean discardMissing) throws ClassNotFoundException, IOException {
final ArrayList instanceCache = this.instanceCache;
final int idx;
final FlatNavigableMap filler = new FlatNavigableMap(target.comparator());
if (cacheIdx == -1) {
idx = instanceCache.size();
instanceCache.add(target);
} else {
idx = cacheIdx;
instanceCache.set(idx, target);
}
for (int i = 0; i < len; i ++) {
filler.put(doReadMapObject(false, i, len, true, discardMissing), doReadMapObject(false, i, len, false, discardMissing));
}
// should install entries in order, bypassing any circular ref issues, unless the map is mutated during deserialize of one of its elements
target.putAll(filler);
final Object resolvedObject = objectResolver.readResolve(target);
instanceCache.set(idx, unshared ? UNRESOLVED : resolvedObject);
return resolvedObject;
}
private static InvalidObjectException sharedMismatch() {
return new InvalidObjectException("Shared/unshared object mismatch");
}
ClassDescriptor doReadClassDescriptor(final int classType, final boolean required) throws IOException, ClassNotFoundException {
final ArrayList classCache = this.classCache;
switch (classType) {
case ID_REPEAT_CLASS_FAR: {
return classCache.get(readInt());
}
case ID_REPEAT_CLASS_NEAR: {
return classCache.get((readByte() | 0xffffff00) + classCache.size());
}
case ID_REPEAT_CLASS_NEARISH: {
return classCache.get((readShort() | 0xffff0000) + classCache.size());
}
case ID_PREDEFINED_ENUM_TYPE_CLASS: {
final int idx = classCache.size();
classCache.add(null);
final Class> type = classTable.readClass(this);
final SimpleClassDescriptor descriptor = new SimpleClassDescriptor(type, ID_ENUM_TYPE_CLASS);
classCache.set(idx, descriptor);
return descriptor;
}
case ID_PREDEFINED_EXTERNALIZABLE_CLASS: {
final int idx = classCache.size();
classCache.add(null);
final Class> type = classTable.readClass(this);
final SimpleClassDescriptor descriptor = new SimpleClassDescriptor(type, ID_EXTERNALIZABLE_CLASS);
classCache.set(idx, descriptor);
return descriptor;
}
case ID_PREDEFINED_EXTERNALIZER_CLASS: {
final int idx = classCache.size();
classCache.add(null);
final Class> type = classTable.readClass(this);
final Externalizer externalizer = (Externalizer) readObject();
final SimpleClassDescriptor descriptor = new ExternalizerClassDescriptor(type, externalizer);
classCache.set(idx, descriptor);
return descriptor;
}
case ID_PREDEFINED_PLAIN_CLASS: {
final int idx = classCache.size();
classCache.add(null);
final Class> type = classTable.readClass(this);
final SimpleClassDescriptor descriptor = new SimpleClassDescriptor(type, ID_PLAIN_CLASS);
classCache.set(idx, descriptor);
return descriptor;
}
case ID_PREDEFINED_PROXY_CLASS: {
final int idx = classCache.size();
classCache.add(null);
final Class> type = classTable.readClass(this);
final SimpleClassDescriptor descriptor = new SimpleClassDescriptor(type, ID_PROXY_CLASS);
classCache.set(idx, descriptor);
return descriptor;
}
case ID_PREDEFINED_SERIALIZABLE_CLASS: {
final int idx = classCache.size();
classCache.add(null);
final Class> type = classTable.readClass(this);
final SerializableClass serializableClass = registry.lookup(type);
int descType = serializableClass.hasWriteObject() ? ID_WRITE_OBJECT_CLASS : ID_SERIALIZABLE_CLASS;
final ClassDescriptor descriptor = new BasicSerializableClassDescriptor(serializableClass, doReadClassDescriptor(readUnsignedByte(), true), serializableClass.getFields(), descType);
classCache.set(idx, descriptor);
return descriptor;
}
case ID_PLAIN_CLASS: {
final String className = readString();
final Class> clazz = classResolver.resolveClass(this, className, 0L);
final SimpleClassDescriptor descriptor = new SimpleClassDescriptor(clazz, ID_PLAIN_CLASS);
classCache.add(descriptor);
return descriptor;
}
case ID_PROXY_CLASS: {
String[] interfaces = new String[readInt()];
for (int i = 0; i < interfaces.length; i ++) {
interfaces[i] = readString();
}
final int idx = classCache.size();
classCache.add(null);
final SimpleClassDescriptor descriptor = new SimpleClassDescriptor(classResolver.resolveProxyClass(this, interfaces), ID_PROXY_CLASS);
classCache.set(idx, descriptor);
return descriptor;
}
case ID_WRITE_OBJECT_CLASS:
case ID_SERIALIZABLE_CLASS: {
int idx = classCache.size();
classCache.add(null);
final String className = configuredVersion >= 4 ? readObject(String.class) : readString();
final long uid = readLong();
Class> clazz = null;
try {
clazz = classResolver.resolveClass(this, className, uid);
} catch (ClassNotFoundException cnfe) {
if (required) throw cnfe;
}
final boolean localSerializable = clazz != null && serializabilityChecker.isSerializable(clazz);
if (! localSerializable && required) {
throw new ClassNotFoundException(className);
}
final FutureSerializableClassDescriptor descriptor = new FutureSerializableClassDescriptor(localSerializable ? clazz : null, classType);
classCache.set(idx, descriptor);
final int cnt = readInt();
final String[] names = new String[cnt];
final ClassDescriptor[] descriptors = new ClassDescriptor[cnt];
final boolean[] unshareds = new boolean[cnt];
for (int i = 0; i < cnt; i ++) {
names[i] = configuredVersion >= 4 ? readObject(String.class) : readUTF();
descriptors[i] = doReadClassDescriptor(readUnsignedByte(), true);
unshareds[i] = readBoolean();
}
ClassDescriptor superDescriptor = doReadClassDescriptor(readUnsignedByte(), false);
final Class> superClazz = clazz == null ? superDescriptor.getNearestType() : clazz.getSuperclass();
if (superDescriptor != null && (clazz == null || localSerializable)) {
final Class> superType = superDescriptor.getNearestType();
if (clazz != null && ! superType.isAssignableFrom(clazz)) {
throw new InvalidClassException(clazz.getName(), "Class does not extend stream superclass");
}
Class> cl = superClazz;
while (cl != superType) {
superDescriptor = new SerializableGapClassDescriptor(registry.lookup(cl), superDescriptor);
cl = cl.getSuperclass();
}
} else if (superClazz != null) {
Class> cl = superClazz;
while (serializabilityChecker.isSerializable(cl)) {
superDescriptor = new SerializableGapClassDescriptor(registry.lookup(cl), superDescriptor);
cl = cl.getSuperclass();
}
}
final SerializableClass serializableClass;
final SerializableField[] fields = new SerializableField[cnt];
if (clazz != null && localSerializable) {
serializableClass = registry.lookup(clazz);
for (int i = 0; i < cnt; i ++) {
fields[i] = serializableClass.getSerializableField(names[i], descriptors[i].getType(), unshareds[i]);
}
} else {
serializableClass = null;
for (int i = 0; i < cnt; i ++) {
fields[i] = new SerializableField(descriptors[i].getType(), names[i], unshareds[i]);
}
}
descriptor.setResult(new BasicSerializableClassDescriptor(localSerializable ? serializableClass : null, superDescriptor, fields, classType));
return descriptor;
}
case ID_EXTERNALIZABLE_CLASS: {
final String className = readString();
final long uid = readLong();
final Class> clazz = classResolver.resolveClass(this, className, uid);
final SimpleClassDescriptor descriptor = new SimpleClassDescriptor(clazz, ID_EXTERNALIZABLE_CLASS);
classCache.add(descriptor);
return descriptor;
}
case ID_EXTERNALIZER_CLASS: {
final String className = readString();
int idx = classCache.size();
classCache.add(null);
final Class> clazz = classResolver.resolveClass(this, className, 0L);
final Externalizer externalizer = (Externalizer) readObject();
final SimpleClassDescriptor descriptor = new ExternalizerClassDescriptor(clazz, externalizer);
classCache.set(idx, descriptor);
return descriptor;
}
case ID_ENUM_TYPE_CLASS: {
final SimpleClassDescriptor descriptor = new SimpleClassDescriptor(classResolver.resolveClass(this, readString(), 0L), ID_ENUM_TYPE_CLASS);
classCache.add(descriptor);
return descriptor;
}
case ID_OBJECT_ARRAY_TYPE_CLASS: {
final ClassDescriptor elementType = doReadClassDescriptor(readUnsignedByte(), true);
final SimpleClassDescriptor arrayDescriptor = new SimpleClassDescriptor(Array.newInstance(elementType.getType(), 0).getClass(), ID_OBJECT_ARRAY_TYPE_CLASS);
classCache.add(arrayDescriptor);
return arrayDescriptor;
}
case ID_CC_ARRAY_LIST: {
return ClassDescriptors.CC_ARRAY_LIST;
}
case ID_CC_LINKED_LIST: {
return ClassDescriptors.CC_LINKED_LIST;
}
case ID_CC_HASH_SET: {
return ClassDescriptors.CC_HASH_SET;
}
case ID_CC_LINKED_HASH_SET: {
return ClassDescriptors.CC_LINKED_HASH_SET;
}
case ID_CC_TREE_SET: {
return ClassDescriptors.CC_TREE_SET;
}
case ID_CC_IDENTITY_HASH_MAP: {
return ClassDescriptors.CC_IDENTITY_HASH_MAP;
}
case ID_CC_HASH_MAP: {
return ClassDescriptors.CC_HASH_MAP;
}
case ID_CC_HASHTABLE: {
return ClassDescriptors.CC_HASHTABLE;
}
case ID_CC_LINKED_HASH_MAP: {
return ClassDescriptors.CC_LINKED_HASH_MAP;
}
case ID_CC_TREE_MAP: {
return ClassDescriptors.CC_TREE_MAP;
}
case ID_CC_ENUM_SET: {
return ClassDescriptors.CC_ENUM_SET;
}
case ID_CC_ENUM_MAP: {
return ClassDescriptors.CC_ENUM_MAP;
}
case ID_ABSTRACT_COLLECTION: {
return ClassDescriptors.ABSTRACT_COLLECTION;
}
case ID_ABSTRACT_LIST: {
return ClassDescriptors.ABSTRACT_LIST;
}
case ID_ABSTRACT_QUEUE: {
return ClassDescriptors.ABSTRACT_QUEUE;
}
case ID_ABSTRACT_SEQUENTIAL_LIST: {
return ClassDescriptors.ABSTRACT_SEQUENTIAL_LIST;
}
case ID_ABSTRACT_SET: {
return ClassDescriptors.ABSTRACT_SET;
}
case ID_CC_CONCURRENT_HASH_MAP: {
return ClassDescriptors.CONCURRENT_HASH_MAP;
}
case ID_CC_COPY_ON_WRITE_ARRAY_LIST: {
return ClassDescriptors.COPY_ON_WRITE_ARRAY_LIST;
}
case ID_CC_COPY_ON_WRITE_ARRAY_SET: {
return ClassDescriptors.COPY_ON_WRITE_ARRAY_SET;
}
case ID_CC_VECTOR: {
return ClassDescriptors.VECTOR;
}
case ID_CC_STACK: {
return ClassDescriptors.STACK;
}
case ID_CC_ARRAY_DEQUE: {
return ClassDescriptors.ARRAY_DEQUE;
}
case ID_CC_NCOPIES: {
return ClassDescriptors.NCOPIES;
}
case ID_UNMODIFIABLE_COLLECTION: {
return ClassDescriptors.UNMODIFIABLE_COLLECTION;
}
case ID_UNMODIFIABLE_SET: {
return ClassDescriptors.UNMODIFIABLE_SET;
}
case ID_UNMODIFIABLE_LIST: {
return ClassDescriptors.UNMODIFIABLE_LIST;
}
case ID_UNMODIFIABLE_MAP: {
return ClassDescriptors.UNMODIFIABLE_MAP;
}
case ID_UNMODIFIABLE_SORTED_SET: {
return ClassDescriptors.UNMODIFIABLE_SORTED_SET;
}
case ID_UNMODIFIABLE_SORTED_MAP: {
return ClassDescriptors.UNMODIFIABLE_SORTED_MAP;
}
case ID_UNMODIFIABLE_MAP_ENTRY_SET: {
return ClassDescriptors.UNMODIFIABLE_MAP_ENTRY_SET;
}
case ID_SINGLETON_MAP_OBJECT: {
return ClassDescriptors.SINGLETON_MAP;
}
case ID_SINGLETON_SET_OBJECT: {
return ClassDescriptors.SINGLETON_SET;
}
case ID_SINGLETON_LIST_OBJECT: {
return ClassDescriptors.SINGLETON_LIST;
}
case ID_EMPTY_MAP_OBJECT: {
return ClassDescriptors.EMPTY_MAP;
}
case ID_EMPTY_SET_OBJECT: {
return ClassDescriptors.EMPTY_SET;
}
case ID_EMPTY_LIST_OBJECT: {
return ClassDescriptors.EMPTY_LIST;
}
case ID_REVERSE_ORDER_OBJECT: {
return ClassDescriptors.REVERSE_ORDER;
}
case ID_REVERSE_ORDER2_OBJECT: {
return ClassDescriptors.REVERSE_ORDER2;
}
case ID_PAIR: {
return ClassDescriptors.PAIR;
}
case ID_STRING_CLASS: {
return ClassDescriptors.STRING_DESCRIPTOR;
}
case ID_OBJECT_CLASS: {
return ClassDescriptors.OBJECT_DESCRIPTOR;
}
case ID_CLASS_CLASS: {
return ClassDescriptors.CLASS_DESCRIPTOR;
}
case ID_ENUM_CLASS: {
return ClassDescriptors.ENUM_DESCRIPTOR;
}
case ID_BOOLEAN_ARRAY_CLASS: {
return ClassDescriptors.BOOLEAN_ARRAY;
}
case ID_BYTE_ARRAY_CLASS: {
return ClassDescriptors.BYTE_ARRAY;
}
case ID_SHORT_ARRAY_CLASS: {
return ClassDescriptors.SHORT_ARRAY;
}
case ID_INT_ARRAY_CLASS: {
return ClassDescriptors.INT_ARRAY;
}
case ID_LONG_ARRAY_CLASS: {
return ClassDescriptors.LONG_ARRAY;
}
case ID_CHAR_ARRAY_CLASS: {
return ClassDescriptors.CHAR_ARRAY;
}
case ID_FLOAT_ARRAY_CLASS: {
return ClassDescriptors.FLOAT_ARRAY;
}
case ID_DOUBLE_ARRAY_CLASS: {
return ClassDescriptors.DOUBLE_ARRAY;
}
case ID_PRIM_BOOLEAN: {
return ClassDescriptors.BOOLEAN;
}
case ID_PRIM_BYTE: {
return ClassDescriptors.BYTE;
}
case ID_PRIM_CHAR: {
return ClassDescriptors.CHAR;
}
case ID_PRIM_DOUBLE: {
return ClassDescriptors.DOUBLE;
}
case ID_PRIM_FLOAT: {
return ClassDescriptors.FLOAT;
}
case ID_PRIM_INT: {
return ClassDescriptors.INT;
}
case ID_PRIM_LONG: {
return ClassDescriptors.LONG;
}
case ID_PRIM_SHORT: {
return ClassDescriptors.SHORT;
}
case ID_VOID: {
return ClassDescriptors.VOID;
}
case ID_BOOLEAN_CLASS: {
return ClassDescriptors.BOOLEAN_OBJ;
}
case ID_BYTE_CLASS: {
return ClassDescriptors.BYTE_OBJ;
}
case ID_SHORT_CLASS: {
return ClassDescriptors.SHORT_OBJ;
}
case ID_INTEGER_CLASS: {
return ClassDescriptors.INTEGER_OBJ;
}
case ID_LONG_CLASS: {
return ClassDescriptors.LONG_OBJ;
}
case ID_CHARACTER_CLASS: {
return ClassDescriptors.CHARACTER_OBJ;
}
case ID_FLOAT_CLASS: {
return ClassDescriptors.FLOAT_OBJ;
}
case ID_DOUBLE_CLASS: {
return ClassDescriptors.DOUBLE_OBJ;
}
case ID_VOID_CLASS: {
return ClassDescriptors.VOID_OBJ;
}
default: {
throw new InvalidClassException("Unexpected class ID " + classType);
}
}
}
protected String readString() throws IOException {
final int length = readInt();
return UTFUtils.readUTFBytes(this, length);
}
public void start(final ByteInput byteInput) throws IOException {
super.start(byteInput);
int version = readUnsignedByte();
if (version < MIN_VERSION || version > configuredVersion || version > MAX_VERSION) {
throw new IOException("Unsupported protocol version " + version);
}
this.version = version;
}
protected Object doReadNewObject(final int streamClassType, final boolean unshared, final boolean discardMissing) throws ClassNotFoundException, IOException {
final ClassDescriptor descriptor = doReadClassDescriptor(streamClassType, ! discardMissing);
try {
final int classType = descriptor.getTypeID();
final List instanceCache = this.instanceCache;
switch (classType) {
case ID_PROXY_CLASS: {
final Class> type = descriptor.getType();
final Class> nonSerializableSuperclass;
if (descriptor instanceof SerializableClassDescriptor) {
nonSerializableSuperclass = ((SerializableClassDescriptor) descriptor).getNonSerializableSuperclass(serializabilityChecker);
} else {
nonSerializableSuperclass = Object.class;
}
final Object obj = registry.lookup(type).callNonInitConstructor(nonSerializableSuperclass);
final int idx = instanceCache.size();
instanceCache.add(obj);
// force a cast for safety
UnsafeHolder.unsafe.putObject(obj, proxyInvocationHandlerOffset, InvocationHandler.class.cast(doReadNestedObject(unshared, "[proxy invocation handler]")));
final Object resolvedObject = objectResolver.readResolve(obj);
if (unshared) {
instanceCache.set(idx, UNRESOLVED);
} else if (obj != resolvedObject) {
instanceCache.set(idx, resolvedObject);
}
return resolvedObject;
}
case ID_WRITE_OBJECT_CLASS:
case ID_SERIALIZABLE_CLASS: {
final SerializableClassDescriptor serializableClassDescriptor = (SerializableClassDescriptor) descriptor;
final SerializableClass serializableClass = serializableClassDescriptor.getSerializableClass();
final Object obj;
if(serializableClass == null || serializableClass.isRecord()) {
obj = null;
} else if (!serializableClass.hasNoInitConstructor(serializableClassDescriptor.getNonSerializableSuperclass(serializabilityChecker))) {
throw new NotSerializableException(serializableClass.getSubjectClass().getName());
} else {
obj = serializableClass.callNonInitConstructor(serializableClassDescriptor.getNonSerializableSuperclass(serializabilityChecker));
}
final int idx = instanceCache.size();
instanceCache.add(obj);
Object finalObject = doInitSerializable(obj, serializableClassDescriptor, discardMissing);
finalObject = finalObject == null ? null : objectResolver.readResolve(serializableClass.hasReadResolve() ? serializableClass.callReadResolve(finalObject) : finalObject);
if (unshared) {
instanceCache.set(idx, UNRESOLVED);
} else if (obj != finalObject) {
instanceCache.set(idx, finalObject);
}
return finalObject;
}
case ID_EXTERNALIZABLE_CLASS: {
final Class> type = descriptor.getType();
final SerializableClass serializableClass = registry.lookup(type);
final BlockUnmarshaller blockUnmarshaller = getBlockUnmarshaller();
final Externalizable obj;
if (serializableClass.hasObjectInputConstructor()) {
obj = (Externalizable) serializableClass.callObjectInputConstructor(blockUnmarshaller);
} else if (serializableClass.hasPublicNoArgConstructor()) {
obj = (Externalizable) serializableClass.callNoArgConstructor();
} else {
throw new InvalidClassException(type.getName(), "Class is non-public or has no public no-arg constructor");
}
final int idx = instanceCache.size();
instanceCache.add(obj);
obj.readExternal(blockUnmarshaller);
blockUnmarshaller.readToEndBlockData();
blockUnmarshaller.unblock();
final Object resolvedObject = objectResolver.readResolve(serializableClass.hasReadResolve() ? serializableClass.callReadResolve(obj) : obj);
if (unshared) {
instanceCache.set(idx, UNRESOLVED);
} else if (obj != resolvedObject) {
instanceCache.set(idx, resolvedObject);
}
return resolvedObject;
}
case ID_EXTERNALIZER_CLASS: {
final int idx = instanceCache.size();
instanceCache.add(null);
Externalizer externalizer = ((ExternalizerClassDescriptor) descriptor).getExternalizer();
final Class> type = descriptor.getType();
final SerializableClass serializableClass = registry.lookup(type);
final Object obj;
final BlockUnmarshaller blockUnmarshaller = getBlockUnmarshaller();
obj = externalizer.createExternal(type, blockUnmarshaller);
instanceCache.set(idx, obj);
blockUnmarshaller.readToEndBlockData();
blockUnmarshaller.unblock();
final Object resolvedObject = objectResolver.readResolve(serializableClass.hasReadResolve() ? serializableClass.callReadResolve(obj) : obj);
if (unshared) {
instanceCache.set(idx, UNRESOLVED);
} else if (obj != resolvedObject) {
instanceCache.set(idx, resolvedObject);
}
return resolvedObject;
}
case ID_ENUM_TYPE_CLASS: {
final String name = readString();
final Enum obj = resolveEnumConstant(descriptor, name);
final int idx = instanceCache.size();
instanceCache.add(obj);
final Object resolvedObject = objectResolver.readResolve(obj);
if (unshared) {
instanceCache.set(idx, UNRESOLVED);
} else if (obj != resolvedObject) {
instanceCache.set(idx, resolvedObject);
}
return resolvedObject;
}
case ID_OBJECT_ARRAY_TYPE_CLASS: {
return doReadObjectArray(readInt(), descriptor.getType().getComponentType(), unshared, discardMissing);
}
case ID_STRING_CLASS: {
// v1 string
final String obj = readString();
final Object resolvedObject = objectResolver.readResolve(obj);
instanceCache.add(unshared ? UNRESOLVED : resolvedObject);
return resolvedObject;
}
case ID_CLASS_CLASS: {
final ClassDescriptor nestedDescriptor = doReadClassDescriptor(readUnsignedByte(), true);
// Classes are not resolved and may not be unshared!
final Class> obj = nestedDescriptor.getType();
return obj;
}
case ID_BOOLEAN_ARRAY_CLASS: {
return doReadBooleanArray(readInt(), unshared);
}
case ID_BYTE_ARRAY_CLASS: {
return doReadByteArray(readInt(), unshared);
}
case ID_SHORT_ARRAY_CLASS: {
return doReadShortArray(readInt(), unshared);
}
case ID_INT_ARRAY_CLASS: {
return doReadIntArray(readInt(), unshared);
}
case ID_LONG_ARRAY_CLASS: {
return doReadLongArray(readInt(), unshared);
}
case ID_CHAR_ARRAY_CLASS: {
return doReadCharArray(readInt(), unshared);
}
case ID_FLOAT_ARRAY_CLASS: {
return doReadFloatArray(readInt(), unshared);
}
case ID_DOUBLE_ARRAY_CLASS: {
return doReadDoubleArray(readInt(), unshared);
}
case ID_BOOLEAN_CLASS: {
return objectResolver.readResolve(Boolean.valueOf(readBoolean()));
}
case ID_BYTE_CLASS: {
return objectResolver.readResolve(Byte.valueOf(readByte()));
}
case ID_SHORT_CLASS: {
return objectResolver.readResolve(Short.valueOf(readShort()));
}
case ID_INTEGER_CLASS: {
return objectResolver.readResolve(Integer.valueOf(readInt()));
}
case ID_LONG_CLASS: {
return objectResolver.readResolve(Long.valueOf(readLong()));
}
case ID_CHARACTER_CLASS: {
return objectResolver.readResolve(Character.valueOf(readChar()));
}
case ID_FLOAT_CLASS: {
return objectResolver.readResolve(Float.valueOf(readFloat()));
}
case ID_DOUBLE_CLASS: {
return objectResolver.readResolve(Double.valueOf(readDouble()));
}
case ID_OBJECT_CLASS:
case ID_PLAIN_CLASS: {
throw new NotSerializableException("(remote)" + descriptor.getType().getName());
}
default: {
throw new InvalidObjectException("Unexpected class type " + classType);
}
}
} catch (IOException e) {
TraceInformation.addIncompleteObjectInformation(e, descriptor.getType());
exceptionListener.handleUnmarshallingException(e, descriptor.getType());
throw e;
} catch (ClassNotFoundException e) {
TraceInformation.addIncompleteObjectInformation(e, descriptor.getType());
exceptionListener.handleUnmarshallingException(e, descriptor.getType());
throw e;
} catch (RuntimeException e) {
TraceInformation.addIncompleteObjectInformation(e, descriptor.getType());
exceptionListener.handleUnmarshallingException(e, descriptor.getType());
throw e;
}
}
private Object doReadDoubleArray(final int cnt, final boolean unshared) throws IOException {
final double[] array = new double[cnt];
for (int i = 0; i < cnt; i ++) {
array[i] = readDouble();
}
final Object resolvedObject = objectResolver.readResolve(array);
instanceCache.add(unshared ? UNRESOLVED : resolvedObject);
return resolvedObject;
}
private Object doReadFloatArray(final int cnt, final boolean unshared) throws IOException {
final float[] array = new float[cnt];
for (int i = 0; i < cnt; i ++) {
array[i] = readFloat();
}
final Object resolvedObject = objectResolver.readResolve(array);
instanceCache.add(unshared ? UNRESOLVED : resolvedObject);
return resolvedObject;
}
private Object doReadCharArray(final int cnt, final boolean unshared) throws IOException {
final char[] array = new char[cnt];
for (int i = 0; i < cnt; i ++) {
array[i] = readChar();
}
final Object resolvedObject = objectResolver.readResolve(array);
instanceCache.add(unshared ? UNRESOLVED : resolvedObject);
return resolvedObject;
}
private Object doReadLongArray(final int cnt, final boolean unshared) throws IOException {
final long[] array = new long[cnt];
for (int i = 0; i < cnt; i ++) {
array[i] = readLong();
}
final Object resolvedObject = objectResolver.readResolve(array);
instanceCache.add(unshared ? UNRESOLVED : resolvedObject);
return resolvedObject;
}
private Object doReadIntArray(final int cnt, final boolean unshared) throws IOException {
final int[] array = new int[cnt];
for (int i = 0; i < cnt; i ++) {
array[i] = readInt();
}
final Object resolvedObject = objectResolver.readResolve(array);
instanceCache.add(unshared ? UNRESOLVED : resolvedObject);
return resolvedObject;
}
private Object doReadShortArray(final int cnt, final boolean unshared) throws IOException {
final short[] array = new short[cnt];
for (int i = 0; i < cnt; i ++) {
array[i] = readShort();
}
final Object resolvedObject = objectResolver.readResolve(array);
instanceCache.add(unshared ? UNRESOLVED : resolvedObject);
return resolvedObject;
}
private Object doReadByteArray(final int cnt, final boolean unshared) throws IOException {
final byte[] array = new byte[cnt];
readFully(array, 0, array.length);
final Object resolvedObject = objectResolver.readResolve(array);
instanceCache.add(unshared ? UNRESOLVED : resolvedObject);
return resolvedObject;
}
private Object doReadBooleanArray(final int cnt, final boolean unshared) throws IOException {
final boolean[] array = new boolean[cnt];
int v;
int bc = cnt & ~7;
for (int i = 0; i < bc; ) {
v = readByte();
array[i++] = (v & 1) != 0;
array[i++] = (v & 2) != 0;
array[i++] = (v & 4) != 0;
array[i++] = (v & 8) != 0;
array[i++] = (v & 16) != 0;
array[i++] = (v & 32) != 0;
array[i++] = (v & 64) != 0;
array[i++] = (v & 128) != 0;
}
if (bc < cnt) {
v = readByte();
switch (cnt & 7) {
case 7:
array[bc + 6] = (v & 64) != 0;
case 6:
array[bc + 5] = (v & 32) != 0;
case 5:
array[bc + 4] = (v & 16) != 0;
case 4:
array[bc + 3] = (v & 8) != 0;
case 3:
array[bc + 2] = (v & 4) != 0;
case 2:
array[bc + 1] = (v & 2) != 0;
case 1:
array[bc] = (v & 1) != 0;
}
}
final Object resolvedObject = objectResolver.readResolve(array);
instanceCache.add(unshared ? UNRESOLVED : resolvedObject);
return resolvedObject;
}
private Object doReadObjectArray(final int cnt, final Class> type, final boolean unshared, final boolean discardMissing) throws ClassNotFoundException, IOException {
final Object[] array = (Object[]) replace(Array.newInstance(type, cnt));
final int idx = instanceCache.size();
instanceCache.add(array);
for (int i = 0; i < cnt; i ++) {
array[i] = doReadCollectionObject(unshared, i, cnt, discardMissing);
}
final Object resolvedObject = objectResolver.readResolve(array);
if (unshared) {
instanceCache.set(idx, UNRESOLVED);
} else if (array != resolvedObject) {
instanceCache.set(idx, resolvedObject);
}
return resolvedObject;
}
private Object doReadArray(final int cnt, final boolean unshared, final boolean discardMissing) throws ClassNotFoundException, IOException {
final int leadByte = readUnsignedByte();
switch (leadByte) {
case ID_PRIM_BOOLEAN: {
return doReadBooleanArray(cnt, unshared);
}
case ID_PRIM_BYTE: {
return doReadByteArray(cnt, unshared);
}
case ID_PRIM_CHAR: {
return doReadCharArray(cnt, unshared);
}
case ID_PRIM_DOUBLE: {
return doReadDoubleArray(cnt, unshared);
}
case ID_PRIM_FLOAT: {
return doReadFloatArray(cnt, unshared);
}
case ID_PRIM_INT: {
return doReadIntArray(cnt, unshared);
}
case ID_PRIM_LONG: {
return doReadLongArray(cnt, unshared);
}
case ID_PRIM_SHORT: {
return doReadShortArray(cnt, unshared);
}
default: {
return doReadObjectArray(cnt, doReadClassDescriptor(leadByte, true).getType(), unshared, discardMissing);
}
}
}
private Object doReadRecord(SerializableClass info, boolean discardMissing) throws IOException, ClassNotFoundException {
final Object[] values = new Object[info.getFields().length];
for (SerializableField serializableField : info.getFields()) {
switch (serializableField.getKind()) {
case BOOLEAN:
values[serializableField.getRecordComponentIndex()] = readBoolean();
break;
case BYTE:
values[serializableField.getRecordComponentIndex()] = readByte();
break;
case CHAR:
values[serializableField.getRecordComponentIndex()] = readChar();
break;
case DOUBLE:
values[serializableField.getRecordComponentIndex()] = readDouble();
break;
case FLOAT:
values[serializableField.getRecordComponentIndex()] = readFloat();
break;
case INT:
values[serializableField.getRecordComponentIndex()] = readInt();
break;
case LONG:
values[serializableField.getRecordComponentIndex()] = readLong();
break;
case SHORT:
values[serializableField.getRecordComponentIndex()] = readShort();
break;
case OBJECT:
values[serializableField.getRecordComponentIndex()] = doReadObject(serializableField.isUnshared(), true);
break;
}
}
return info.invokeRecordCanonicalConstructor(values);
}
@SuppressWarnings({"unchecked"})
private static Enum resolveEnumConstant(final ClassDescriptor descriptor, final String name) {
return Enum.valueOf((Class extends Enum>)descriptor.getType(), name);
}
private Object doInitSerializable(Object obj, final SerializableClassDescriptor descriptor, final boolean discardMissing) throws IOException, ClassNotFoundException {
final Class> type = descriptor.getType();
final ClassDescriptor superDescriptor = descriptor.getSuperClassDescriptor();
if (superDescriptor instanceof SerializableClassDescriptor) {
final SerializableClassDescriptor serializableSuperDescriptor = (SerializableClassDescriptor) superDescriptor;
doInitSerializable(obj, serializableSuperDescriptor, discardMissing);
}
final int typeId = descriptor.getTypeID();
final BlockUnmarshaller blockUnmarshaller = getBlockUnmarshaller();
if (type == null) {
if (descriptor instanceof SerializableGapClassDescriptor) {
// skip
return obj;
}
// consume this class' data silently
discardFields(descriptor);
if (typeId == ID_WRITE_OBJECT_CLASS) {
blockUnmarshaller.readToEndBlockData();
blockUnmarshaller.unblock();
}
return obj;
}
final SerializableClass info = registry.lookup(type);
if (descriptor instanceof SerializableGapClassDescriptor) {
if (obj != null && info.hasReadObjectNoData()) {
info.callReadObjectNoData(obj);
}
} else if (info.hasReadObject()) {
final RiverObjectInputStream objectInputStream = getObjectInputStream();
final SerializableClassDescriptor oldDescriptor = objectInputStream.swapClass(descriptor);
final Object oldObj = objectInputStream.swapCurrent(obj);
final int restoreState = objectInputStream.start();
boolean ok = false;
try {
if (typeId == ID_WRITE_OBJECT_CLASS) {
// read fields
if (obj != null) info.callReadObject(obj, objectInputStream);
objectInputStream.finish(restoreState);
blockUnmarshaller.readToEndBlockData();
blockUnmarshaller.unblock();
} else { // typeid == ID_SERIALIZABLE_CLASS
// no user data to read - mark the OIS so that it calls endOfBlock after reading fields!
objectInputStream.noCustomData();
if (obj != null) info.callReadObject(obj, objectInputStream);
objectInputStream.finish(restoreState);
blockUnmarshaller.restore(objectInputStream.getRestoreIdx());
}
objectInputStream.swapCurrent(oldObj);
objectInputStream.swapClass(oldDescriptor);
ok = true;
} finally {
if (! ok) {
objectInputStream.fullReset();
}
}
} else {
if (info.isRecord()) {
obj = doReadRecord(info, discardMissing);
} else if (obj != null) {
readFields(obj, descriptor, discardMissing);
} else {
discardFields(descriptor);
}
if (typeId == ID_WRITE_OBJECT_CLASS) {
// useless user data
blockUnmarshaller.readToEndBlockData();
blockUnmarshaller.unblock();
}
}
return obj;
}
protected void readFields(final Object obj, final SerializableClassDescriptor descriptor, final boolean discardMissing) throws IOException, ClassNotFoundException {
for (SerializableField serializableField : descriptor.getFields()) {
try {
if (! serializableField.isAccessible()) {
// missing; consume stream data only
switch (serializableField.getKind()) {
case BOOLEAN: {
readBoolean();
break;
}
case BYTE: {
readByte();
break;
}
case CHAR: {
readChar();
break;
}
case DOUBLE: {
readDouble();
break;
}
case FLOAT: {
readFloat();
break;
}
case INT: {
readInt();
break;
}
case LONG: {
readLong();
break;
}
case OBJECT: {
doReadObject(serializableField.isUnshared(), true);
break;
}
case SHORT: {
readShort();
break;
}
}
} else {
switch (serializableField.getKind()) {
case BOOLEAN: {
serializableField.setBoolean(obj, readBoolean());
break;
}
case BYTE: {
serializableField.setByte(obj, readByte());
break;
}
case CHAR: {
serializableField.setChar(obj, readChar());
break;
}
case DOUBLE: {
serializableField.setDouble(obj, readDouble());
break;
}
case FLOAT: {
serializableField.setFloat(obj, readFloat());
break;
}
case INT: {
serializableField.setInt(obj, readInt());
break;
}
case LONG: {
serializableField.setLong(obj, readLong());
break;
}
case OBJECT: {
serializableField.setObject(obj, doReadObject(serializableField.isUnshared(), discardMissing));
break;
}
case SHORT: {
serializableField.setShort(obj, readShort());
break;
}
}
}
} catch (IOException | ClassNotFoundException | RuntimeException e) {
TraceInformation.addFieldInformation(e, descriptor.getSerializableClass(), serializableField);
TraceInformation.addObjectInformation(e, obj);
throw e;
}
}
}
protected void discardFields(final SerializableClassDescriptor descriptor) throws IOException {
for (SerializableField serializableField : descriptor.getFields()) {
try {
switch (serializableField.getKind()) {
case BOOLEAN: {
readBoolean();
break;
}
case BYTE: {
readByte();
break;
}
case CHAR: {
readChar();
break;
}
case DOUBLE: {
readDouble();
break;
}
case FLOAT: {
readFloat();
break;
}
case INT: {
readInt();
break;
}
case LONG: {
readLong();
break;
}
case OBJECT: {
doReadObject(serializableField.isUnshared(), true);
break;
}
case SHORT: {
readShort();
break;
}
}
} catch (IOException e) {
TraceInformation.addFieldInformation(e, descriptor.getSerializableClass(), serializableField);
throw e;
} catch (ClassNotFoundException e) {
TraceInformation.addFieldInformation(e, descriptor.getSerializableClass(), serializableField);
throw new IOException("Failed to discard field data", e);
} catch (RuntimeException e) {
TraceInformation.addFieldInformation(e, descriptor.getSerializableClass(), serializableField);
throw e;
}
}
}
void addValidation(final ObjectInputValidation validation, final int prio) {
final Validator validator = new Validator(prio, validatorSeq++, validation);
final SortedSet validators = this.validators;
(validators == null ? this.validators = new TreeSet() : validators).add(validator);
}
public String readUTF() throws IOException {
final int len = readInt();
return UTFUtils.readUTFBytes(this, len);
}
private Object replace(Object object) {
return object == null ? null : objectPreResolver.readResolve(object);
}
}