org.apache.geode.internal.InternalDataSerializer.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.geode.internal.InternalDataSerializer.java

Source

/*
 * 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.geode.internal;

import org.apache.commons.lang.StringUtils;
import org.apache.geode.CancelException;
import org.apache.geode.CanonicalInstantiator;
import org.apache.geode.DataSerializable;
import org.apache.geode.DataSerializer;
import org.apache.geode.GemFireIOException;
import org.apache.geode.GemFireRethrowable;
import org.apache.geode.Instantiator;
import org.apache.geode.InternalGemFireError;
import org.apache.geode.SerializationException;
import org.apache.geode.SystemFailure;
import org.apache.geode.ToDataException;
import org.apache.geode.cache.CacheClosedException;
import org.apache.geode.cache.execute.Function;
import org.apache.geode.distributed.internal.DMStats;
import org.apache.geode.distributed.internal.DistributionConfig;
import org.apache.geode.distributed.internal.DistributionManager;
import org.apache.geode.distributed.internal.InternalDistributedSystem;
import org.apache.geode.distributed.internal.LonerDistributionManager;
import org.apache.geode.distributed.internal.SerialDistributionMessage;
import org.apache.geode.i18n.StringId;
import org.apache.geode.internal.cache.EnumListenerEvent;
import org.apache.geode.internal.cache.EventID;
import org.apache.geode.internal.cache.GemFireCacheImpl;
import org.apache.geode.internal.cache.InternalCache;
import org.apache.geode.internal.cache.PoolManagerImpl;
import org.apache.geode.internal.cache.tier.sockets.CacheClientNotifier;
import org.apache.geode.internal.cache.tier.sockets.CacheServerHelper;
import org.apache.geode.internal.cache.tier.sockets.ClientDataSerializerMessage;
import org.apache.geode.internal.cache.tier.sockets.ClientProxyMembershipID;
import org.apache.geode.internal.cache.tier.sockets.OldClientSupportService;
import org.apache.geode.internal.cache.tier.sockets.Part;
import org.apache.geode.internal.i18n.LocalizedStrings;
import org.apache.geode.internal.logging.LogService;
import org.apache.geode.internal.logging.log4j.LocalizedMessage;
import org.apache.geode.internal.logging.log4j.LogMarker;
import org.apache.geode.internal.util.concurrent.CopyOnWriteHashMap;
import org.apache.geode.pdx.NonPortableClassException;
import org.apache.geode.pdx.PdxInstance;
import org.apache.geode.pdx.PdxSerializable;
import org.apache.geode.pdx.PdxSerializer;
import org.apache.geode.pdx.internal.AutoSerializableManager;
import org.apache.geode.pdx.internal.AutoSerializableManager.AutoClassInfo;
import org.apache.geode.pdx.internal.EnumInfo;
import org.apache.geode.pdx.internal.PdxInputStream;
import org.apache.geode.pdx.internal.PdxInstanceEnum;
import org.apache.geode.pdx.internal.PdxInstanceImpl;
import org.apache.geode.pdx.internal.PdxOutputStream;
import org.apache.geode.pdx.internal.PdxReaderImpl;
import org.apache.geode.pdx.internal.PdxType;
import org.apache.geode.pdx.internal.PdxWriterImpl;
import org.apache.geode.pdx.internal.TypeRegistry;
import org.apache.logging.log4j.Logger;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.EOFException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.NotSerializableException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.io.OutputStream;
import java.io.Serializable;
import java.io.UTFDataFormatException;
import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.InetAddress;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.Stack;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.UUID;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;

/**
 * Contains static methods for data serializing instances of internal GemFire classes. It also
 * contains the implementation of the distribution messaging (and shared memory management) needed
 * to support data serialization.
 *
 * @since GemFire 3.5
 */
public abstract class InternalDataSerializer extends DataSerializer implements DSCODE {
    private static final Logger logger = LogService.getLogger();

    /**
     * Maps Class names to their DataSerializer. This is used to find a DataSerializer during
     * serialization.
     */
    private static final Map<String, DataSerializer> classesToSerializers = new ConcurrentHashMap<>();

    private static final String serializationVersionTxt = System
            .getProperty(DistributionConfig.GEMFIRE_PREFIX + "serializationVersion");

    /**
     * support for old GemFire clients and WAN sites - needed to enable moving from GemFire to Geode
     */
    private static OldClientSupportService oldClientSupportService;

    /**
     * For backward compatibility we must swizzle the package of some classes that had to be moved
     * when GemFire was open- sourced. This preserves backward-compatibility.
     * 
     * @param name the fully qualified class name
     * @return the name of the class in this implementation
     */
    public static String processIncomingClassName(String name) {
        // TCPServer classes are used before a cache exists and support for old clients has been
        // initialized
        String oldPackage = "com.gemstone.org.jgroups.stack.tcpserver";
        String newPackage = "org.apache.geode.distributed.internal.tcpserver";
        if (name.startsWith(oldPackage)) {
            return newPackage + name.substring(oldPackage.length());
        }
        OldClientSupportService svc = getOldClientSupportService();
        if (svc != null) {
            return svc.processIncomingClassName(name);
        }
        return name;
    }

    /**
     * For backward compatibility we must swizzle the package of some classes that had to be moved
     * when GemFire was open- sourced. This preserves backward-compatibility.
     * 
     * @param name the fully qualified class name
     * @param out the consumer of the serialized object
     * @return the name of the class in this implementation
     */
    public static String processOutgoingClassName(String name, DataOutput out) {
        // TCPServer classes are used before a cache exists and support for old clients has been
        // initialized
        String oldPackage = "com.gemstone.org.jgroups.stack.tcpserver";
        String newPackage = "org.apache.geode.distributed.internal.tcpserver";
        if (name.startsWith(newPackage)) {
            return oldPackage + name.substring(newPackage.length());
        }
        OldClientSupportService svc = getOldClientSupportService();
        if (svc != null) {
            return svc.processOutgoingClassName(name, out);
        }
        return name;
    }

    /**
     * Any time new serialization format is added then a new enum needs to be added here.
     * 
     * @since GemFire 6.6.2
     */
    private enum SERIALIZATION_VERSION {
        vINVALID,
        // includes 6.6.0.x and 6.6.1.x. Note that no serialization changes were made in 6.6 until 6.6.2
        v660,
        // 6.6.2.x or later NOTE if you add a new constant make sure and update "latestVersion".
        v662
    }

    /**
     * Change this constant to be the last one in SERIALIZATION_VERSION
     */
    private static final SERIALIZATION_VERSION latestVersion = SERIALIZATION_VERSION.v662;

    private static SERIALIZATION_VERSION calculateSerializationVersion() {
        if (serializationVersionTxt == null || serializationVersionTxt.isEmpty()) {
            return latestVersion;
        } else if (serializationVersionTxt.startsWith("6.6.0") || serializationVersionTxt.startsWith("6.6.1")) {
            return SERIALIZATION_VERSION.v660;
        } else if (serializationVersionTxt.startsWith("6.6.2")) {
            return SERIALIZATION_VERSION.v662;
        } else {
            return SERIALIZATION_VERSION.vINVALID;
        }
    }

    private static final SERIALIZATION_VERSION serializationVersion = calculateSerializationVersion();

    public static boolean is662SerializationEnabled() {
        return serializationVersion.ordinal() >= SERIALIZATION_VERSION.v662.ordinal();
    }

    public static void checkSerializationVersion() {
        if (serializationVersion == SERIALIZATION_VERSION.vINVALID) {
            throw new IllegalArgumentException("The system property \"gemfire.serializationVersion\" was set to \""
                    + serializationVersionTxt
                    + "\" which is not a valid serialization version. Valid versions must start with \"6.6.0\", \"6.6.1\", or \"6.6.2\"");
        }
    }

    static {
        initializeWellKnownSerializers();
    }

    private static void initializeWellKnownSerializers() {
        // ArrayBlockingQueue does not have zero-arg constructor
        // LinkedBlockingQueue does have zero-arg constructor but no way to get capacity

        classesToSerializers.put("java.lang.String", new WellKnownPdxDS() {
            @Override
            public boolean toData(Object o, DataOutput out) throws IOException {
                try {
                    writeString((String) o, out);
                } catch (UTFDataFormatException ex) {
                    // See bug 30428
                    String s = "While writing a String of length " + ((String) o).length();
                    UTFDataFormatException ex2 = new UTFDataFormatException(s);
                    ex2.initCause(ex);
                    throw ex2;
                }
                return true;
            }
        });
        classesToSerializers.put("java.net.InetAddress", new WellKnownDS() {
            @Override
            public boolean toData(Object o, DataOutput out) throws IOException {
                InetAddress address = (InetAddress) o;
                out.writeByte(INET_ADDRESS);
                writeInetAddress(address, out);
                return true;
            }
        });
        classesToSerializers.put("java.net.Inet4Address", new WellKnownDS() {
            @Override
            public boolean toData(Object o, DataOutput out) throws IOException {
                InetAddress address = (InetAddress) o;
                out.writeByte(INET_ADDRESS);
                writeInetAddress(address, out);
                return true;
            }
        });
        classesToSerializers.put("java.net.Inet6Address", new WellKnownDS() {
            @Override
            public boolean toData(Object o, DataOutput out) throws IOException {
                InetAddress address = (InetAddress) o;
                out.writeByte(INET_ADDRESS);
                writeInetAddress(address, out);
                return true;
            }
        });
        classesToSerializers.put("java.lang.Class", new WellKnownDS() {
            @Override
            public boolean toData(Object o, DataOutput out) throws IOException {
                Class c = (Class) o;
                if (c.isPrimitive()) {
                    writePrimitiveClass(c, out);
                } else {
                    out.writeByte(CLASS);
                    writeClass(c, out);
                }
                return true;
            }
        });
        classesToSerializers.put("java.lang.Boolean", new WellKnownPdxDS() {
            @Override
            public boolean toData(Object o, DataOutput out) throws IOException {
                Boolean value = (Boolean) o;
                out.writeByte(BOOLEAN);
                writeBoolean(value, out);
                return true;
            }
        });
        classesToSerializers.put("java.lang.Character", new WellKnownPdxDS() {
            @Override
            public boolean toData(Object o, DataOutput out) throws IOException {
                Character value = (Character) o;
                out.writeByte(CHARACTER);
                writeCharacter(value, out);
                return true;
            }
        });
        classesToSerializers.put("java.lang.Byte", new WellKnownPdxDS() {
            @Override
            public boolean toData(Object o, DataOutput out) throws IOException {
                Byte value = (Byte) o;
                out.writeByte(BYTE);
                writeByte(value, out);
                return true;
            }
        });
        classesToSerializers.put("java.lang.Short", new WellKnownPdxDS() {
            @Override
            public boolean toData(Object o, DataOutput out) throws IOException {
                Short value = (Short) o;
                out.writeByte(SHORT);
                writeShort(value, out);
                return true;
            }
        });
        classesToSerializers.put("java.lang.Integer", new WellKnownPdxDS() {
            @Override
            public boolean toData(Object o, DataOutput out) throws IOException {
                Integer value = (Integer) o;
                out.writeByte(INTEGER);
                writeInteger(value, out);
                return true;
            }
        });
        classesToSerializers.put("java.lang.Long", new WellKnownPdxDS() {
            @Override
            public boolean toData(Object o, DataOutput out) throws IOException {
                Long value = (Long) o;
                out.writeByte(LONG);
                writeLong(value, out);
                return true;
            }
        });
        classesToSerializers.put("java.lang.Float", new WellKnownPdxDS() {
            @Override
            public boolean toData(Object o, DataOutput out) throws IOException {
                Float value = (Float) o;
                out.writeByte(FLOAT);
                writeFloat(value, out);
                return true;
            }
        });
        classesToSerializers.put("java.lang.Double", new WellKnownPdxDS() {
            @Override
            public boolean toData(Object o, DataOutput out) throws IOException {
                Double value = (Double) o;
                out.writeByte(DOUBLE);
                writeDouble(value, out);
                return true;
            }
        });
        classesToSerializers.put("[Z", // boolean[]
                new WellKnownPdxDS() {
                    @Override
                    public boolean toData(Object o, DataOutput out) throws IOException {
                        out.writeByte(BOOLEAN_ARRAY);
                        writeBooleanArray((boolean[]) o, out);
                        return true;
                    }
                });
        classesToSerializers.put("[B", // byte[]
                new WellKnownPdxDS() {
                    @Override
                    public boolean toData(Object o, DataOutput out) throws IOException {
                        byte[] array = (byte[]) o;
                        out.writeByte(BYTE_ARRAY);
                        writeByteArray(array, out);
                        return true;
                    }
                });
        classesToSerializers.put("[C", // char[]
                new WellKnownPdxDS() {
                    @Override
                    public boolean toData(Object o, DataOutput out) throws IOException {
                        out.writeByte(CHAR_ARRAY);
                        writeCharArray((char[]) o, out);
                        return true;
                    }
                });
        classesToSerializers.put("[D", // double[]
                new WellKnownPdxDS() {
                    @Override
                    public boolean toData(Object o, DataOutput out) throws IOException {
                        double[] array = (double[]) o;
                        out.writeByte(DOUBLE_ARRAY);
                        writeDoubleArray(array, out);
                        return true;
                    }
                });
        classesToSerializers.put("[F", // float[]
                new WellKnownPdxDS() {
                    @Override
                    public boolean toData(Object o, DataOutput out) throws IOException {
                        float[] array = (float[]) o;
                        out.writeByte(FLOAT_ARRAY);
                        writeFloatArray(array, out);
                        return true;
                    }
                });
        classesToSerializers.put("[I", // int[]
                new WellKnownPdxDS() {
                    @Override
                    public boolean toData(Object o, DataOutput out) throws IOException {
                        int[] array = (int[]) o;
                        out.writeByte(INT_ARRAY);
                        writeIntArray(array, out);
                        return true;
                    }
                });
        classesToSerializers.put("[J", // long[]
                new WellKnownPdxDS() {
                    @Override
                    public boolean toData(Object o, DataOutput out) throws IOException {
                        long[] array = (long[]) o;
                        out.writeByte(LONG_ARRAY);
                        writeLongArray(array, out);
                        return true;
                    }
                });
        classesToSerializers.put("[S", // short[]
                new WellKnownPdxDS() {
                    @Override
                    public boolean toData(Object o, DataOutput out) throws IOException {
                        short[] array = (short[]) o;
                        out.writeByte(SHORT_ARRAY);
                        writeShortArray(array, out);
                        return true;
                    }
                });
        classesToSerializers.put("[Ljava.lang.String;", // String[]
                new WellKnownPdxDS() {
                    @Override
                    public boolean toData(Object o, DataOutput out) throws IOException {
                        String[] array = (String[]) o;
                        out.writeByte(STRING_ARRAY);
                        writeStringArray(array, out);
                        return true;
                    }
                });
        classesToSerializers.put(TimeUnit.NANOSECONDS.getClass().getName(), new WellKnownDS() {
            @Override
            public boolean toData(Object o, DataOutput out) throws IOException {
                out.writeByte(TIME_UNIT);
                out.writeByte(TIME_UNIT_NANOSECONDS);
                return true;
            }
        });
        classesToSerializers.put(TimeUnit.MICROSECONDS.getClass().getName(), new WellKnownDS() {
            @Override
            public boolean toData(Object o, DataOutput out) throws IOException {
                out.writeByte(TIME_UNIT);
                out.writeByte(TIME_UNIT_MICROSECONDS);
                return true;
            }
        });
        classesToSerializers.put(TimeUnit.MILLISECONDS.getClass().getName(), new WellKnownDS() {
            @Override
            public boolean toData(Object o, DataOutput out) throws IOException {
                out.writeByte(TIME_UNIT);
                out.writeByte(TIME_UNIT_MILLISECONDS);
                return true;
            }
        });
        classesToSerializers.put(TimeUnit.SECONDS.getClass().getName(), new WellKnownDS() {
            @Override
            public boolean toData(Object o, DataOutput out) throws IOException {
                out.writeByte(TIME_UNIT);
                out.writeByte(TIME_UNIT_SECONDS);
                return true;
            }
        });
        classesToSerializers.put("java.util.Date", new WellKnownPdxDS() {
            @Override
            public boolean toData(Object o, DataOutput out) throws IOException {
                Date date = (Date) o;
                out.writeByte(DATE);
                writeDate(date, out);
                return true;
            }
        });
        classesToSerializers.put("java.io.File", new WellKnownDS() {
            @Override
            public boolean toData(Object o, DataOutput out) throws IOException {
                File file = (File) o;
                out.writeByte(FILE);
                writeFile(file, out);
                return true;
            }
        });
        classesToSerializers.put("java.util.ArrayList", new WellKnownPdxDS() {
            @Override
            public boolean toData(Object o, DataOutput out) throws IOException {
                ArrayList list = (ArrayList) o;
                out.writeByte(ARRAY_LIST);
                writeArrayList(list, out);
                return true;
            }
        });
        classesToSerializers.put("java.util.LinkedList", new WellKnownDS() {
            @Override
            public boolean toData(Object o, DataOutput out) throws IOException {
                LinkedList list = (LinkedList) o;
                out.writeByte(LINKED_LIST);
                writeLinkedList(list, out);
                return true;
            }
        });
        classesToSerializers.put("java.util.Vector", new WellKnownPdxDS() {
            @Override
            public boolean toData(Object o, DataOutput out) throws IOException {
                out.writeByte(VECTOR);
                writeVector((Vector) o, out);
                return true;
            }
        });
        classesToSerializers.put("java.util.Stack", new WellKnownDS() {
            @Override
            public boolean toData(Object o, DataOutput out) throws IOException {
                out.writeByte(STACK);
                writeStack((Stack) o, out);
                return true;
            }
        });
        classesToSerializers.put("java.util.HashSet", new WellKnownPdxDS() {
            @Override
            public boolean toData(Object o, DataOutput out) throws IOException {
                HashSet list = (HashSet) o;
                out.writeByte(HASH_SET);
                writeHashSet(list, out);
                return true;
            }
        });
        classesToSerializers.put("java.util.LinkedHashSet", new WellKnownPdxDS() {
            @Override
            public boolean toData(Object o, DataOutput out) throws IOException {
                out.writeByte(LINKED_HASH_SET);
                writeLinkedHashSet((LinkedHashSet) o, out);
                return true;
            }
        });
        classesToSerializers.put("java.util.HashMap", new WellKnownPdxDS() {
            @Override
            public boolean toData(Object o, DataOutput out) throws IOException {
                HashMap list = (HashMap) o;
                out.writeByte(HASH_MAP);
                writeHashMap(list, out);
                return true;
            }
        });
        classesToSerializers.put("java.util.IdentityHashMap", new WellKnownDS() {
            @Override
            public boolean toData(Object o, DataOutput out) throws IOException {
                out.writeByte(IDENTITY_HASH_MAP);
                writeIdentityHashMap((IdentityHashMap) o, out);
                return true;
            }
        });
        classesToSerializers.put("java.util.Hashtable", new WellKnownPdxDS() {
            @Override
            public boolean toData(Object o, DataOutput out) throws IOException {
                out.writeByte(HASH_TABLE);
                writeHashtable((Hashtable) o, out);
                return true;
            }
        });
        classesToSerializers.put("java.util.Properties", new WellKnownDS() {
            @Override
            public boolean toData(Object o, DataOutput out) throws IOException {
                Properties props = (Properties) o;
                out.writeByte(PROPERTIES);
                writeProperties(props, out);
                return true;
            }
        });
        classesToSerializers.put("java.util.TreeMap", new WellKnownDS() {
            @Override
            public boolean toData(Object o, DataOutput out) throws IOException {
                out.writeByte(TREE_MAP);
                writeTreeMap((TreeMap) o, out);
                return true;
            }
        });
        classesToSerializers.put("java.util.TreeSet", new WellKnownDS() {
            @Override
            public boolean toData(Object o, DataOutput out) throws IOException {
                out.writeByte(TREE_SET);
                writeTreeSet((TreeSet) o, out);
                return true;
            }
        });
        if (is662SerializationEnabled()) {
            classesToSerializers.put("java.math.BigInteger", new WellKnownDS() {
                @Override
                public boolean toData(Object o, DataOutput out) throws IOException {
                    out.writeByte(BIG_INTEGER);
                    writeBigInteger((BigInteger) o, out);
                    return true;
                }
            });
            classesToSerializers.put("java.math.BigDecimal", new WellKnownDS() {
                @Override
                public boolean toData(Object o, DataOutput out) throws IOException {
                    out.writeByte(BIG_DECIMAL);
                    writeBigDecimal((BigDecimal) o, out);
                    return true;
                }
            });
            classesToSerializers.put("java.util.UUID", new WellKnownDS() {
                @Override
                public boolean toData(Object o, DataOutput out) throws IOException {
                    out.writeByte(UUID);
                    writeUUID((UUID) o, out);
                    return true;
                }
            });
            classesToSerializers.put("java.sql.Timestamp", new WellKnownDS() {
                @Override
                public boolean toData(Object o, DataOutput out) throws IOException {
                    out.writeByte(TIMESTAMP);
                    writeTimestamp((Timestamp) o, out);
                    return true;
                }
            });
        }
    }

    /**
     * Maps the id of a serializer to its {@code DataSerializer}.
     */
    private static final ConcurrentMap/* <Integer, DataSerializer|Marker> */ idsToSerializers = new ConcurrentHashMap();

    /**
     * Contains the classnames of the data serializers (and not the supported classes) not yet loaded
     * into the vm as keys and their corresponding holder instances as values.
     */
    private static final ConcurrentHashMap<String, SerializerAttributesHolder> dsClassesToHolders = new ConcurrentHashMap<>();

    /**
     * Contains the id of the data serializers not yet loaded into the vm as keys and their
     * corresponding holder instances as values.
     */
    private static final ConcurrentHashMap<Integer, SerializerAttributesHolder> idsToHolders = new ConcurrentHashMap<>();

    /**
     * Contains the classnames of supported classes as keys and their corresponding
     * SerializerAttributesHolder instances as values. This applies only to the data serializers which
     * have not been loaded into the vm.
     */
    private static final ConcurrentHashMap<String, SerializerAttributesHolder> supportedClassesToHolders = new ConcurrentHashMap<>();

    /**
     * {@code RegistrationListener}s that receive callbacks when {@code DataSerializer}s and
     * {@code Instantiator}s are registered. Note: copy-on-write access used for this set
     */
    private static volatile Set listeners = new HashSet();

    private static final Object listenersSync = new Object();

    /**
     * Convert the given unsigned byte to an int. The returned value will be in the range [0..255]
     * inclusive
     */
    private static int ubyteToInt(byte ub) {
        return ub & 0xFF;
    }

    public static void setOldClientSupportService(final OldClientSupportService svc) {
        oldClientSupportService = svc;
    }

    public static OldClientSupportService getOldClientSupportService() {
        return oldClientSupportService;
    }

    /**
     * Instantiates an instance of {@code DataSerializer}
     *
     * @throws IllegalArgumentException If the class can't be instantiated
     *
     * @see DataSerializer#register(Class)
     */
    private static DataSerializer newInstance(Class c) {
        if (!DataSerializer.class.isAssignableFrom(c)) {
            throw new IllegalArgumentException(LocalizedStrings.DataSerializer_0_DOES_NOT_EXTEND_DATASERIALIZER
                    .toLocalizedString(c.getName()));
        }

        Constructor init;
        try {
            init = c.getDeclaredConstructor(new Class[0]);

        } catch (NoSuchMethodException ignored) {
            StringId s = LocalizedStrings.DataSerializer_CLASS_0_DOES_NOT_HAVE_A_ZEROARGUMENT_CONSTRUCTOR;
            Object[] args = new Object[] { c.getName() };
            if (c.getDeclaringClass() != null) {
                s = LocalizedStrings.DataSerializer_CLASS_0_DOES_NOT_HAVE_A_ZEROARGUMENT_CONSTRUCTOR_IT_IS_AN_INNER_CLASS_OF_1_SHOULD_IT_BE_A_STATIC_INNER_CLASS;
                args = new Object[] { c.getName(), c.getDeclaringClass() };
            }
            throw new IllegalArgumentException(s.toLocalizedString(args));
        }

        DataSerializer s;
        try {
            init.setAccessible(true);
            s = (DataSerializer) init.newInstance(new Object[0]);

        } catch (IllegalAccessException ignored) {
            throw new IllegalArgumentException(
                    LocalizedStrings.DataSerializer_COULD_NOT_INSTANTIATE_AN_INSTANCE_OF_0
                            .toLocalizedString(c.getName()));

        } catch (InstantiationException ex) {
            throw new IllegalArgumentException(
                    LocalizedStrings.DataSerializer_COULD_NOT_INSTANTIATE_AN_INSTANCE_OF_0
                            .toLocalizedString(c.getName()),
                    ex);

        } catch (InvocationTargetException ex) {
            throw new IllegalArgumentException(LocalizedStrings.DataSerializer_WHILE_INSTANTIATING_AN_INSTANCE_OF_0
                    .toLocalizedString(c.getName()), ex);
        }

        return s;
    }

    public static DataSerializer register(Class c, boolean distribute, EventID eventId,
            ClientProxyMembershipID context) {
        DataSerializer s = newInstance(c);
        // This method is only called when server connection and CacheClientUpdaterThread
        s.setEventId(eventId);
        s.setContext(context);
        return _register(s, distribute);
    }

    /**
     * Registers a {@code DataSerializer} instance with the data serialization framework.
     *
     * @param distribute Should the registered {@code DataSerializer} be distributed to other members
     *        of the distributed system?
     *
     * @see DataSerializer#register(Class)
     */
    public static DataSerializer register(Class c, boolean distribute) {
        final DataSerializer s = newInstance(c);
        return _register(s, distribute);
    }

    public static DataSerializer _register(DataSerializer s, boolean distribute) {
        final int id = s.getId();
        DataSerializer dsForMarkers = s;
        if (id == 0) {
            throw new IllegalArgumentException(
                    LocalizedStrings.InternalDataSerializer_CANNOT_CREATE_A_DATASERIALIZER_WITH_ID_0
                            .toLocalizedString());
        }
        final Class[] classes = s.getSupportedClasses();
        if (classes == null || classes.length == 0) {
            final StringId msg = LocalizedStrings.InternalDataSerializer_THE_DATASERIALIZER_0_HAS_NO_SUPPORTED_CLASSES_ITS_GETSUPPORTEDCLASSES_METHOD_MUST_RETURN_AT_LEAST_ONE_CLASS;
            throw new IllegalArgumentException(msg.toLocalizedString(s.getClass().getName()));
        }

        for (Class aClass : classes) {
            if (aClass == null) {
                final StringId msg = LocalizedStrings.InternalDataSerializer_THE_DATASERIALIZER_GETSUPPORTEDCLASSES_METHOD_FOR_0_RETURNED_AN_ARRAY_THAT_CONTAINED_A_NULL_ELEMENT;
                throw new IllegalArgumentException(msg.toLocalizedString(s.getClass().getName()));
            } else if (aClass.isArray()) {
                final StringId msg = LocalizedStrings.InternalDataSerializer_THE_DATASERIALIZER_GETSUPPORTEDCLASSES_METHOD_FOR_0_RETURNED_AN_ARRAY_THAT_CONTAINED_AN_ARRAY_CLASS_WHICH_IS_NOT_ALLOWED_SINCE_ARRAYS_HAVE_BUILTIN_SUPPORT;
                throw new IllegalArgumentException(msg.toLocalizedString(s.getClass().getName()));
            }
        }

        final Integer idx = id;
        boolean retry;
        Marker oldMarker = null;
        final Marker m = new InitMarker();
        do {
            retry = false;
            Object oldSerializer = idsToSerializers.putIfAbsent(idx, m);
            if (oldSerializer != null) {
                if (oldSerializer instanceof Marker) {
                    retry = !idsToSerializers.replace(idx, oldSerializer, m);
                    if (!retry) {
                        oldMarker = (Marker) oldSerializer;
                    }
                } else if (oldSerializer.getClass().equals(s.getClass())) {
                    // We've already got one of these registered
                    if (distribute) {
                        sendRegistrationMessage(s);
                    }
                    return (DataSerializer) oldSerializer;
                } else {
                    DataSerializer other = (DataSerializer) oldSerializer;
                    throw new IllegalStateException(
                            LocalizedStrings.InternalDataSerializer_A_DATASERIALIZER_OF_CLASS_0_IS_ALREADY_REGISTERED_WITH_ID_1_SO_THE_DATASERIALIZER_OF_CLASS_2_COULD_NOT_BE_REGISTERED
                                    .toLocalizedString(new Object[] { other.getClass().getName(), other.getId() }));
                }
            }
        } while (retry);

        try {
            for (int i = 0; i < classes.length; i++) {
                DataSerializer oldS = classesToSerializers.putIfAbsent(classes[i].getName(), s);
                if (oldS != null) {
                    if (!s.equals(oldS)) {
                        // cleanup the ones we have already added
                        for (int j = 0; j < i; j++) {
                            classesToSerializers.remove(classes[j].getName(), s);
                        }
                        dsForMarkers = null;
                        String oldMsg;
                        if (oldS.getId() == 0) {
                            oldMsg = "DataSerializer has built-in support for class ";
                        } else {
                            oldMsg = "A DataSerializer of class " + oldS.getClass().getName()
                                    + " is already registered to support class ";
                        }
                        String msg = oldMsg + classes[i].getName() + " so the DataSerializer of class "
                                + s.getClass().getName() + " could not be registered.";
                        if (oldS.getId() == 0) {
                            throw new IllegalArgumentException(msg);
                        } else {
                            throw new IllegalStateException(msg);
                        }
                    }
                }
            }
        } finally {
            if (dsForMarkers == null) {
                idsToSerializers.remove(idx, m);
            } else {
                idsToSerializers.replace(idx, m, dsForMarkers);
            }
            if (oldMarker != null) {
                oldMarker.setSerializer(dsForMarkers);
            }
            m.setSerializer(dsForMarkers);
        }

        // if dataserializer is getting registered for first time
        // its EventID will be null, so generate a new event id
        // the the distributed system is connected
        InternalCache cache = GemFireCacheImpl.getInstance();
        if (cache != null && s.getEventId() == null) {
            s.setEventId(new EventID(cache.getDistributedSystem()));
        }

        if (distribute) {
            // send a message to other peers telling them about a newly-registered
            // dataserializer, it also send event id of the originator along with the
            // dataserializer
            sendRegistrationMessage(s);
            // send it to cache servers if it is a client
            sendRegistrationMessageToServers(s);
        }
        // send it to all cache clients irrelevant of distribute
        // bridge servers send it all the clients irrelevant of
        // originator VM
        sendRegistrationMessageToClients(s);

        fireNewDataSerializer(s);

        return s;
    }

    /**
     * Marks a {@code DataSerializer} className for registration with the data serialization
     * framework. Does not necessarily load the classes into this VM.
     * 
     * @param className Name of the DataSerializer class.
     * @param distribute If true, distribute this data serializer.
     * @param eventId Event id
     * @param proxyId proxy id
     * @see DataSerializer#register(Class)
     */
    public static void register(String className, boolean distribute, EventID eventId,
            ClientProxyMembershipID proxyId, int id) {
        register(className, distribute, new SerializerAttributesHolder(className, eventId, proxyId, id));
    }

    /**
     * Marks a {@code DataSerializer} className for registration with the data serialization
     * framework. Does not necessarily load the classes into this VM.
     * 
     * @param distribute If true, distribute this data serializer.
     * @see DataSerializer#register(Class)
     */
    public static void register(String className, boolean distribute) {
        register(className, distribute, new SerializerAttributesHolder());
    }

    private static void register(String className, boolean distribute, SerializerAttributesHolder holder) {
        if (StringUtils.isBlank(className)) {
            throw new IllegalArgumentException("Class name cannot be null or empty.");
        }

        SerializerAttributesHolder oldValue = dsClassesToHolders.putIfAbsent(className, holder);
        if (oldValue != null) {
            if (oldValue.getId() != 0 && holder.getId() != 0 && oldValue.getId() != holder.getId()) {
                throw new IllegalStateException(
                        LocalizedStrings.InternalDataSerializer_A_DATASERIALIZER_OF_CLASS_0_IS_ALREADY_REGISTERED_WITH_ID_1_SO_THE_DATASERIALIZER_OF_CLASS_2_COULD_NOT_BE_REGISTERED
                                .toLocalizedString(
                                        new Object[] { oldValue.getClass().getName(), oldValue.getId() }));
            }
        }

        idsToHolders.putIfAbsent(holder.getId(), holder);

        Object ds = idsToSerializers.get(holder.getId());
        if (ds instanceof Marker) {
            synchronized (ds) {
                ((Marker) ds).notifyAll();
            }
        }

        if (distribute) {
            sendRegistrationMessageToServers(holder);
        }
    }

    public static void updateSupportedClassesMap(HashMap<Integer, ArrayList<String>> map) {
        for (Entry<Integer, ArrayList<String>> e : map.entrySet()) {
            for (String supportedClassName : e.getValue()) {
                supportedClassesToHolders.putIfAbsent(supportedClassName, idsToHolders.get(e.getKey()));
            }
        }
    }

    public static void updateSupportedClassesMap(String dsClassName, String supportedClassName) {
        supportedClassesToHolders.putIfAbsent(supportedClassName, dsClassesToHolders.get(dsClassName));
    }

    public static class SerializerAttributesHolder {
        private String className = "";
        private EventID eventId = null;
        private ClientProxyMembershipID proxyId = null;
        private int id = 0;

        SerializerAttributesHolder() {
        }

        SerializerAttributesHolder(String name, EventID event, ClientProxyMembershipID proxy, int id) {
            this.className = name;
            this.eventId = event;
            this.proxyId = proxy;
            this.id = id;
        }

        /**
         * 
         * @return String the classname of the data serializer this instance represents.
         */
        public String getClassName() {
            return this.className;
        }

        public EventID getEventId() {
            return this.eventId;
        }

        public ClientProxyMembershipID getProxyId() {
            return this.proxyId;
        }

        public int getId() {
            return this.id;
        }

        @Override
        public String toString() {
            return "SerializerAttributesHolder[name=" + this.className + ",id=" + this.id + ",eventId="
                    + this.eventId + ']';
        }
    }

    private static void sendRegistrationMessageToServers(DataSerializer dataSerializer) {
        PoolManagerImpl.allPoolsRegisterDataSerializers(dataSerializer);
    }

    private static void sendRegistrationMessageToServers(SerializerAttributesHolder holder) {
        PoolManagerImpl.allPoolsRegisterDataSerializers(holder);
    }

    private static void sendRegistrationMessageToClients(DataSerializer dataSerializer) {
        InternalCache cache = GemFireCacheImpl.getInstance();
        if (cache == null) {
            // A cache has not yet been created.
            // we can't propagate it to clients
            return;
        }
        byte[][] serializedDataSerializer = new byte[2][];
        try {
            serializedDataSerializer[0] = CacheServerHelper
                    .serialize(dataSerializer.getClass().toString().substring(6));

            byte[] idBytes = new byte[4];
            Part.encodeInt(dataSerializer.getId(), idBytes);
            serializedDataSerializer[1] = idBytes;
        } catch (IOException ignored) {
            if (logger.isTraceEnabled(LogMarker.SERIALIZER)) {
                logger.trace(LogMarker.SERIALIZER,
                        "InternalDataSerializer encountered an IOException while serializing DataSerializer :{}",
                        dataSerializer);
            }
        }
        ClientDataSerializerMessage clientDataSerializerMessage = new ClientDataSerializerMessage(
                EnumListenerEvent.AFTER_REGISTER_DATASERIALIZER, serializedDataSerializer,
                (ClientProxyMembershipID) dataSerializer.getContext(), (EventID) dataSerializer.getEventId(),
                new Class[][] { dataSerializer.getSupportedClasses() });
        // Deliver it to all the clients
        CacheClientNotifier.routeClientMessage(clientDataSerializerMessage);
    }

    public static EventID generateEventId() {
        InternalCache cache = GemFireCacheImpl.getInstance();
        if (cache == null) {
            // A cache has not yet created
            return null;
        }
        return new EventID(InternalDistributedSystem.getAnyInstance());
    }

    /**
     * Unregisters a {@code Serializer} that was previously registered with the data serialization
     * framework.
     */
    public static void unregister(int id) {
        final Integer idx = id;
        Object o = idsToSerializers.remove(idx);
        if (o != null) {
            if (o instanceof InitMarker) {
                o = ((Marker) o).getSerializer();
            }
        }
        if (o instanceof DataSerializer) {
            DataSerializer s = (DataSerializer) o;
            Class[] classes = s.getSupportedClasses();
            for (Class aClass : classes) {
                classesToSerializers.remove(aClass.getName(), s);
                supportedClassesToHolders.remove(aClass.getName());
            }
            dsClassesToHolders.remove(s.getClass().getName());
            idsToHolders.remove(idx);
        }
    }

    // testHook used to clean up any registered DataSerializers
    public static void reinitialize() {
        idsToSerializers.clear();
        classesToSerializers.clear();
        supportedClassesToHolders.clear();
        dsClassesToHolders.clear();
        idsToHolders.clear();
        initializeWellKnownSerializers();
    }

    /**
     * Returns the {@code DataSerializer} for the given class. If no class has been registered,
     * {@code null} is returned. Remember that it is okay to return {@code null} in this case. This
     * method is invoked when writing an object. If a serializer isn't available, then its the user's
     * fault.
     */
    private static DataSerializer getSerializer(Class c) {
        DataSerializer ds = classesToSerializers.get(c.getName());
        if (ds == null) {
            SerializerAttributesHolder sah = supportedClassesToHolders.get(c.getName());
            if (sah != null) {
                Class dsClass = null;
                try {
                    dsClass = getCachedClass(sah.getClassName());

                    DataSerializer serializer = register(dsClass, false);
                    dsClassesToHolders.remove(dsClass.getName());
                    idsToHolders.remove(serializer.getId());
                    for (Class clazz : serializer.getSupportedClasses()) {
                        supportedClassesToHolders.remove(clazz.getName());
                    }
                    return serializer;
                } catch (ClassNotFoundException ignored) {
                    logger.info(LogMarker.SERIALIZER,
                            LocalizedMessage.create(
                                    LocalizedStrings.InternalDataSerializer_COULD_NOT_LOAD_DATASERIALIZER_CLASS_0,
                                    dsClass));
                }
            }
        }
        return ds;
    }

    /**
     * Returns the {@code DataSerializer} with the given id.
     */
    public static DataSerializer getSerializer(int id) {
        final Integer idx = id;
        final GetMarker marker = new GetMarker();
        DataSerializer result = null;
        boolean timedOut = false;
        SerializerAttributesHolder sah = idsToHolders.get(idx);
        while (result == null && !timedOut && sah == null) {
            Object o = idsToSerializers.putIfAbsent(idx, marker);
            if (o == null) {
                result = marker.getSerializer();
                if (result == null) {
                    // timed out
                    timedOut = true;
                    idsToSerializers.remove(idx, marker);
                }
            } else if (o instanceof Marker) {
                result = ((Marker) o).getSerializer();
            } else {
                result = (DataSerializer) o;
            }
        }
        if (result == null) {
            if (sah != null) {
                Class dsClass = null;
                try {
                    dsClass = getCachedClass(sah.getClassName());

                    DataSerializer ds = register(dsClass, false);
                    dsClassesToHolders.remove(sah.getClassName());
                    idsToHolders.remove(id);
                    for (Class clazz : ds.getSupportedClasses()) {
                        supportedClassesToHolders.remove(clazz.getName());
                    }
                    return ds;
                } catch (ClassNotFoundException ignored) {
                    logger.info(LogMarker.SERIALIZER,
                            LocalizedMessage.create(
                                    LocalizedStrings.InternalDataSerializer_COULD_NOT_LOAD_DATASERIALIZER_CLASS_0,
                                    dsClass));
                }
            }
        }
        return result;
    }

    /**
     * Returns all of the currently registered serializers
     */
    public static DataSerializer[] getSerializers() {
        final int size = idsToSerializers.size();
        Collection coll = new ArrayList(size);
        for (Object v : idsToSerializers.values()) {
            if (v instanceof InitMarker) {
                v = ((Marker) v).getSerializer();
            }
            if (v instanceof DataSerializer) {
                coll.add(v);
            }
        }

        Iterator<Entry<String, SerializerAttributesHolder>> iterator = dsClassesToHolders.entrySet().iterator();
        while (iterator.hasNext()) {
            Entry<String, SerializerAttributesHolder> entry = iterator.next();
            String name = entry.getKey();
            SerializerAttributesHolder holder = entry.getValue();
            try {
                Class cl = getCachedClass(name);
                DataSerializer ds;
                if (holder.getEventId() != null) {
                    ds = register(cl, false, holder.getEventId(), holder.getProxyId());
                } else {
                    ds = register(cl, false);
                }
                coll.add(ds);
                iterator.remove();
                idsToHolders.remove(ds.getId());
                for (Class clazz : ds.getSupportedClasses()) {
                    supportedClassesToHolders.remove(clazz.getName());
                }
            } catch (ClassNotFoundException ignored) {
                logger.info(LogMarker.SERIALIZER, LocalizedMessage.create(
                        LocalizedStrings.InternalDataSerializer_COULD_NOT_LOAD_DATASERIALIZER_CLASS_0, name));
            }
        }

        return (DataSerializer[]) coll.toArray(new DataSerializer[coll.size()]);
    }

    /**
     * Returns all the data serializers in this vm. This method, unlike {@link #getSerializers()},
     * does not force loading of the data serializers which were not loaded in the vm earlier.
     * 
     * @return Array of {@link SerializerAttributesHolder}
     */
    public static SerializerAttributesHolder[] getSerializersForDistribution() {

        final int size = idsToSerializers.size() + dsClassesToHolders.size();
        Collection<SerializerAttributesHolder> coll = new ArrayList<>(size);

        for (Object v : idsToSerializers.values()) {
            if (v instanceof InitMarker) {
                v = ((Marker) v).getSerializer();
            }
            if (v instanceof DataSerializer) {
                DataSerializer s = (DataSerializer) v;
                coll.add(new SerializerAttributesHolder(s.getClass().getName(), (EventID) s.getEventId(),
                        (ClientProxyMembershipID) s.getContext(), s.getId()));
            }
        }

        for (final Entry<String, SerializerAttributesHolder> stringSerializerAttributesHolderEntry : dsClassesToHolders
                .entrySet()) {
            SerializerAttributesHolder v = stringSerializerAttributesHolderEntry.getValue();
            coll.add(v);
        }

        return coll.toArray(new SerializerAttributesHolder[coll.size()]);
    }

    /**
     * Persist this class's map to out TODO: saveRegistrations is unused
     */
    public static void saveRegistrations(DataOutput out) throws IOException {
        for (Object v : idsToSerializers.values()) {
            if (v instanceof InitMarker) {
                v = ((Marker) v).getSerializer();
            }
            if (v instanceof DataSerializer) {
                DataSerializer ds = (DataSerializer) v;
                out.writeInt(ds.getId()); // since 5.7 an int instead of a byte
                DataSerializer.writeClass(ds.getClass(), out);
            }
        }
        if (!dsClassesToHolders.isEmpty()) {
            Iterator<Entry<String, SerializerAttributesHolder>> iterator = dsClassesToHolders.entrySet().iterator();
            Class dsClass = null;
            while (iterator.hasNext()) {
                try {
                    dsClass = getCachedClass(iterator.next().getKey());
                } catch (ClassNotFoundException ignored) {
                    logger.info(LogMarker.SERIALIZER,
                            LocalizedMessage.create(
                                    LocalizedStrings.InternalDataSerializer_COULD_NOT_LOAD_DATASERIALIZER_CLASS_0,
                                    dsClass));
                    continue;
                }
                DataSerializer ds = register(dsClass, false);
                iterator.remove();
                idsToHolders.remove(ds.getId());
                for (Class clazz : ds.getSupportedClasses()) {
                    supportedClassesToHolders.remove(clazz.getName());
                }

                out.writeInt(ds.getId()); // since 5.7 an int instead of a byte
                DataSerializer.writeClass(ds.getClass(), out);
            }
        }
        // We know that DataSerializer's id must be > 0 so write a zero
        // to mark the end of the ds list.
        out.writeInt(0); // since 5.7 an int instead of a byte
    }

    /**
     * Read the data from in and register it with this class. TODO: loadRegistrations is unused
     * 
     * @throws IllegalArgumentException if a registration fails
     */
    public static void loadRegistrations(DataInput in) throws IOException {
        while (in.readInt() != 0) {
            Class dsClass = null;
            boolean skip = false;
            try {
                dsClass = DataSerializer.readClass(in);
            } catch (ClassNotFoundException ignored) {
                skip = true;
            }
            if (skip) {
                continue;
            }
            register(dsClass, /* dsId, */ true);
        }
    }

    /**
     * Adds a {@code RegistrationListener} that will receive callbacks when {@code DataSerializer}s
     * and {@code Instantiator}s are registered.
     */
    public static void addRegistrationListener(RegistrationListener l) {
        synchronized (listenersSync) {
            Set newSet = new HashSet(listeners);
            newSet.add(l);
            listeners = newSet;
        }
    }

    /**
     * Removes a {@code RegistrationListener} so that it no longer receives callbacks.
     */
    public static void removeRegistrationListener(RegistrationListener l) {
        synchronized (listenersSync) {
            Set newSet = new HashSet(listeners);
            newSet.remove(l);
            listeners = newSet;
        }
    }

    /**
     * Alerts all {@code RegistrationListener}s that a new {@code DataSerializer} has been registered
     *
     * @see InternalDataSerializer.RegistrationListener#newDataSerializer
     */
    private static void fireNewDataSerializer(DataSerializer ds) {
        for (Object listener1 : listeners) {
            RegistrationListener listener = (RegistrationListener) listener1;
            listener.newDataSerializer(ds);
        }
    }

    /**
     * Alerts all {@code RegistrationListener}s that a new {@code Instantiator} has been registered
     *
     * @see InternalDataSerializer.RegistrationListener#newInstantiator
     */
    static void fireNewInstantiator(Instantiator instantiator) {
        for (Object listener1 : listeners) {
            RegistrationListener listener = (RegistrationListener) listener1;
            listener.newInstantiator(instantiator);
        }
    }

    /**
     * If we are connected to a distributed system, send a message to other members telling them about
     * a newly-registered serializer.
     */
    private static void sendRegistrationMessage(DataSerializer s) {
        InternalDistributedSystem system = InternalDistributedSystem.getConnectedInstance();
        if (system != null) {
            RegistrationMessage m = new RegistrationMessage(s);
            system.getDistributionManager().putOutgoing(m);
        }
    }

    // Writes just the header of a DataSerializableFixedID to out.
    public static void writeDSFIDHeader(int dsfid, DataOutput out) throws IOException {
        if (dsfid == DataSerializableFixedID.ILLEGAL) {
            throw new IllegalStateException(
                    LocalizedStrings.InternalDataSerializer_ATTEMPTED_TO_SERIALIZE_ILLEGAL_DSFID
                            .toLocalizedString());
        }
        if (dsfid <= Byte.MAX_VALUE && dsfid >= Byte.MIN_VALUE) {
            out.writeByte(DS_FIXED_ID_BYTE);
            out.writeByte(dsfid);
        } else if (dsfid <= Short.MAX_VALUE && dsfid >= Short.MIN_VALUE) {
            out.writeByte(DS_FIXED_ID_SHORT);
            out.writeShort(dsfid);
        } else {
            out.writeByte(DS_FIXED_ID_INT);
            out.writeInt(dsfid);
        }
    }

    public static void writeDSFID(DataSerializableFixedID o, DataOutput out) throws IOException {
        int dsfid = o.getDSFID();
        if (dsfidToClassMap != null && logger.isTraceEnabled(LogMarker.DEBUG_DSFID)) {
            logger.trace(LogMarker.DEBUG_DSFID, "writeDSFID {} class={}", dsfid, o.getClass());
            if (dsfid != DataSerializableFixedID.NO_FIXED_ID && dsfid != DataSerializableFixedID.ILLEGAL) {
                // consistency check to make sure that the same DSFID is not used
                // for two different classes
                String newClassName = o.getClass().getName();
                String existingClassName = (String) dsfidToClassMap.putIfAbsent(dsfid, newClassName);
                if (existingClassName != null && !existingClassName.equals(newClassName)) {
                    logger.trace(LogMarker.DEBUG_DSFID, "dsfid={} is used for class {} and class {}", dsfid,
                            existingClassName, newClassName);
                }
            }
        }
        if (dsfid == DataSerializableFixedID.NO_FIXED_ID) {
            out.writeByte(DS_NO_FIXED_ID);
            DataSerializer.writeClass(o.getClass(), out);
        } else {
            writeDSFIDHeader(dsfid, out);
        }
        try {
            invokeToData(o, out);
        } catch (IOException | CancelException | ToDataException | GemFireRethrowable io) {
            // Note: this is not a user code toData but one from our
            // internal code since only GemFire product code implements DSFID

            // Serializing a PDX can result in a cache closed exception. Just rethrow

            throw io;
        } catch (VirtualMachineError err) {
            SystemFailure.initiateFailure(err);
            // If this ever returns, rethrow the error. We're poisoned
            // now, so don't let this thread continue.
            throw err;
        } catch (Throwable t) {
            // Whenever you catch Error or Throwable, you must also
            // catch VirtualMachineError (see above). However, there is
            // _still_ a possibility that you are dealing with a cascading
            // error condition, so you also need to check to see if the JVM
            // is still usable:
            SystemFailure.checkFailure();
            throw new ToDataException("toData failed on dsfid=" + dsfid + " msg:" + t.getMessage(), t);
        }
    }

    /**
     * Data serializes an instance of a well-known class to the given {@code DataOutput}.
     *
     * @return {@code true} if {@code o} was actually written to {@code out}
     */
    private static boolean writeWellKnownObject(Object o, DataOutput out, boolean ensurePdxCompatibility)
            throws IOException {
        return writeUserObject(o, out, ensurePdxCompatibility);
    }

    /**
     * Data serializes an instance of a "user class" (that is, a class that can be handled by a
     * registered {@code DataSerializer}) to the given {@code DataOutput}.
     *
     * @return {@code true} if {@code o} was written to {@code out}.
     */
    private static boolean writeUserObject(Object o, DataOutput out, boolean ensurePdxCompatibility)
            throws IOException {

        final Class<?> c = o.getClass();
        final DataSerializer serializer = InternalDataSerializer.getSerializer(c);
        if (serializer != null) {
            int id = serializer.getId();
            if (id != 0) {
                checkPdxCompatible(o, ensurePdxCompatibility);
                // id will be 0 if it is a WellKnowDS
                if (id <= Byte.MAX_VALUE && id >= Byte.MIN_VALUE) {
                    out.writeByte(USER_CLASS);
                    out.writeByte((byte) id);
                } else if (id <= Short.MAX_VALUE && id >= Short.MIN_VALUE) {
                    out.writeByte(USER_CLASS_2);
                    out.writeShort(id);
                } else {
                    out.writeByte(USER_CLASS_4);
                    out.writeInt(id);
                }
            } else {
                if (ensurePdxCompatibility) {
                    if (!(serializer instanceof WellKnownPdxDS)) {
                        checkPdxCompatible(o, ensurePdxCompatibility);
                    }
                }
            }
            boolean toDataResult;
            try {
                toDataResult = serializer.toData(o, out);
            } catch (IOException io) {
                if (serializer instanceof WellKnownDS) {
                    // this is not user code so throw IOException
                    throw io; // see bug 44659
                } else {
                    // We no longer rethrow IOException here
                    // because if user code throws an IOException we want
                    // to create a ToDataException to report it as a problem
                    // with the plugin code.
                    throw new ToDataException("toData failed on DataSerializer with id=" + id + " for class " + c,
                            io);
                }
            } catch (CancelException | ToDataException | GemFireRethrowable ex) {
                // Serializing a PDX can result in a cache closed exception. Just rethrow
                throw ex;
            } catch (VirtualMachineError err) {
                SystemFailure.initiateFailure(err);
                // If this ever returns, rethrow the error. We're poisoned
                // now, so don't let this thread continue.
                throw err;
            } catch (Throwable t) {
                // Whenever you catch Error or Throwable, you must also
                // catch VirtualMachineError (see above). However, there is
                // _still_ a possibility that you are dealing with a cascading
                // error condition, so you also need to check to see if the JVM
                // is still usable:
                SystemFailure.checkFailure();
                throw new ToDataException("toData failed on DataSerializer with id=" + id + " for class " + c, t);
            }
            if (toDataResult) {
                return true;
            } else {
                throw new ToDataException(
                        LocalizedStrings.DataSerializer_SERIALIZER_0_A_1_SAID_THAT_IT_COULD_SERIALIZE_AN_INSTANCE_OF_2_BUT_ITS_TODATA_METHOD_RETURNED_FALSE
                                .toLocalizedString(serializer.getId(), serializer.getClass().getName(),
                                        o.getClass().getName()));
            }
            // Do byte[][] and Object[] here to fix bug 44060
        } else if (o instanceof byte[][]) {
            byte[][] byteArrays = (byte[][]) o;
            out.writeByte(ARRAY_OF_BYTE_ARRAYS);
            writeArrayOfByteArrays(byteArrays, out);
            return true;
        } else if (o instanceof Object[]) {
            Object[] array = (Object[]) o;
            out.writeByte(OBJECT_ARRAY);
            writeObjectArray(array, out, ensurePdxCompatibility);
            return true;
        } else if (is662SerializationEnabled()
                && (o.getClass().isEnum()/* for bug 52271 */ || (o.getClass().getSuperclass() != null
                        && o.getClass().getSuperclass().isEnum()))) {
            if (isPdxSerializationInProgress()) {
                writePdxEnum((Enum<?>) o, out);
            } else {
                checkPdxCompatible(o, ensurePdxCompatibility);
                writeGemFireEnum((Enum<?>) o, out);
            }
            return true;
        } else {
            PdxSerializer pdxSerializer = TypeRegistry.getPdxSerializer();
            return pdxSerializer != null && writePdx(out, null, o, pdxSerializer);
        }
    }

    public static boolean autoSerialized(Object o, DataOutput out) throws IOException {
        AutoSerializableManager asm = TypeRegistry.getAutoSerializableManager();
        if (asm != null) {
            AutoClassInfo aci = asm.getExistingClassInfo(o.getClass());
            if (aci != null) {
                InternalCache internalCache = GemFireCacheImpl
                        .getForPdx("PDX registry is unavailable because the Cache has been closed.");
                TypeRegistry tr = internalCache.getPdxRegistry();

                PdxOutputStream os;
                if (out instanceof HeapDataOutputStream) {
                    os = new PdxOutputStream((HeapDataOutputStream) out);
                } else {
                    os = new PdxOutputStream();
                }
                PdxWriterImpl writer = new PdxWriterImpl(tr, o, aci, os);

                try {
                    if (is662SerializationEnabled()) {
                        boolean alreadyInProgress = isPdxSerializationInProgress();
                        if (!alreadyInProgress) {
                            setPdxSerializationInProgress(true);
                            try {
                                asm.writeData(writer, o, aci);
                            } finally {
                                setPdxSerializationInProgress(false);
                            }
                        } else {
                            asm.writeData(writer, o, aci);
                        }
                    } else {
                        asm.writeData(writer, o, aci);
                    }
                } catch (ToDataException | CancelException | NonPortableClassException | GemFireRethrowable ex) {
                    throw ex;
                } catch (VirtualMachineError err) {
                    SystemFailure.initiateFailure(err);
                    // If this ever returns, rethrow the error. We're poisoned
                    // now, so don't let this thread continue.
                    throw err;
                } catch (Throwable t) {
                    // Whenever you catch Error or Throwable, you must also
                    // catch VirtualMachineError (see above). However, there is
                    // _still_ a possibility that you are dealing with a cascading
                    // error condition, so you also need to check to see if the JVM
                    // is still usable:
                    SystemFailure.checkFailure();
                    throw new ToDataException("PdxSerializer failed when calling toData on " + o.getClass(), t);
                }
                int bytesWritten = writer.completeByteStreamGeneration();
                getDMStats(internalCache).incPdxSerialization(bytesWritten);
                if (!(out instanceof HeapDataOutputStream)) {
                    writer.sendTo(out);
                }
                return true;
            }
        }
        return false;
    }

    private static void checkPdxCompatible(Object o, boolean ensurePdxCompatibility) {
        if (ensurePdxCompatibility) {
            throw new NonPortableClassException(
                    "Instances of " + o.getClass() + " are not compatible with non-java PDX.");
        }
    }

    /**
     * Test to see if the object is in the gemfire package, to see if we should pass it on to a users
     * custom serializater.
     */
    private static boolean isGemfireObject(Object o) {
        return (o instanceof Function // fixes 43691
                || o.getClass().getName().startsWith("org.apache.")
                || o.getClass().getName().startsWith("org.apache.geode")) && !(o instanceof PdxSerializerObject);
    }

    /**
     * Reads an object that was serialized by a customer ("user") {@code DataSerializer} from the
     * given {@code DataInput}.
     *
     * @throws IOException If the serializer that can deserialize the object is not registered.
     */
    private static Object readUserObject(DataInput in, int serializerId)
            throws IOException, ClassNotFoundException {
        DataSerializer serializer = InternalDataSerializer.getSerializer(serializerId);

        if (serializer == null) {
            throw new IOException(LocalizedStrings.DataSerializer_SERIALIZER_0_IS_NOT_REGISTERED
                    .toLocalizedString(new Object[] { serializerId }));
        }

        return serializer.fromData(in);
    }

    /**
     * Checks to make sure a {@code DataOutput} is not {@code null}.
     *
     * @throws NullPointerException If {@code out} is {@code null}
     */
    public static void checkOut(DataOutput out) {
        if (out == null) {
            String s = "Null DataOutput";
            throw new NullPointerException(s);
        }
    }

    /**
     * Checks to make sure a {@code DataInput} is not {@code null}.
     *
     * @throws NullPointerException If {@code in} is {@code null}
     */
    public static void checkIn(DataInput in) {
        if (in == null) {
            String s = "Null DataInput";
            throw new NullPointerException(s);
        }
    }

    /**
     * Writes a {@code Set} to a {@code DataOutput}.
     * <P>
     * This method is internal because its semantics (that is, its ability to write any kind of
     * {@code Set}) are different from the {@code write}XXX methods of the external
     * {@code DataSerializer}.
     *
     * @throws IOException A problem occurs while writing to {@code out}
     *
     * @see #readSet
     *
     * @since GemFire 4.0
     */
    public static void writeSet(Collection<?> set, DataOutput out) throws IOException {
        checkOut(out);

        int size;
        if (set == null) {
            size = -1;
        } else {
            size = set.size();
        }
        writeArrayLength(size, out);
        if (logger.isTraceEnabled(LogMarker.SERIALIZER)) {
            logger.trace(LogMarker.SERIALIZER, "Writing HashSet with {} elements: {}", size, set);
        }
        if (size > 0) {
            for (Object element : set) {
                writeObject(element, out);
            }
        }
    }

    /**
     * Reads a {@code Set} from a {@code DataInput}.
     *
     * @throws IOException A problem occurs while writing to {@code out}
     * @throws ClassNotFoundException The class of one of the {@code HashSet}'s elements cannot be
     *         found.
     *
     * @see #writeSet
     *
     * @since GemFire 4.0
     */
    public static Set readSet(DataInput in) throws IOException, ClassNotFoundException {
        return readHashSet(in);
    }

    /**
     * Reads a {@code Set} from a {@code DataInput} into the given non-null collection. Returns true
     * if collection read is non-null else returns false. TODO: readCollection is unused
     * 
     * @throws IOException A problem occurs while reading from {@code in}
     * @throws ClassNotFoundException The class of one of the {@code Set}'s elements cannot be found.
     * 
     * @see #writeSet
     */
    public static <E> boolean readCollection(DataInput in, Collection<E> c)
            throws IOException, ClassNotFoundException {

        checkIn(in);

        final int size = readArrayLength(in);
        if (size >= 0) {
            for (int index = 0; index < size; ++index) {
                E element = DataSerializer.readObject(in);
                c.add(element);
            }

            if (logger.isTraceEnabled(LogMarker.SERIALIZER)) {
                logger.trace(LogMarker.SERIALIZER, "Read Collection with {} elements: {}", size, c);
            }
            return true;
        }
        return false;
    }

    /**
     * write a set of Long objects
     * 
     * @param set the set of Long objects
     * @param hasLongIDs if false, write only ints, not longs
     * @param out the output stream
     */
    public static void writeSetOfLongs(Set set, boolean hasLongIDs, DataOutput out) throws IOException {
        if (set == null) {
            out.writeInt(-1);
        } else {
            out.writeInt(set.size());
            out.writeBoolean(hasLongIDs);
            for (Object aSet : set) {
                Long l = (Long) aSet;
                if (hasLongIDs) {
                    out.writeLong(l);
                } else {
                    out.writeInt((int) l.longValue());
                }
            }
        }
    }

    /** read a set of Long objects */
    public static Set<Long> readSetOfLongs(DataInput in) throws IOException {
        int size = in.readInt();
        if (size < 0) {
            return null;
        } else {
            Set result = new HashSet(size);
            boolean longIDs = in.readBoolean();
            for (int i = 0; i < size; i++) {
                long l = longIDs ? in.readLong() : in.readInt();
                result.add(l);
            }
            return result;
        }
    }

    /**
     * write a set of Long objects TODO: writeListOfLongs is unused
     * 
     * @param list the set of Long objects
     * @param hasLongIDs if false, write only ints, not longs
     * @param out the output stream
     */
    public static void writeListOfLongs(List list, boolean hasLongIDs, DataOutput out) throws IOException {
        if (list == null) {
            out.writeInt(-1);
        } else {
            out.writeInt(list.size());
            out.writeBoolean(hasLongIDs);
            for (Object aList : list) {
                Long l = (Long) aList;
                if (hasLongIDs) {
                    out.writeLong(l);
                } else {
                    out.writeInt((int) l.longValue());
                }
            }
        }
    }

    /** read a set of Long objects */
    public static List<Long> readListOfLongs(DataInput in) throws IOException {
        int size = in.readInt();
        if (size < 0) {
            return null;
        } else {
            List result = new LinkedList();
            boolean longIDs = in.readBoolean();
            for (int i = 0; i < size; i++) {
                long l = longIDs ? in.readLong() : in.readInt();
                result.add(l);
            }
            return result;
        }
    }

    /**
     * Writes the type code for a primitive type Class to {@code DataOutput}.
     */
    public static void writePrimitiveClass(Class c, DataOutput out) throws IOException {
        if (c == Boolean.TYPE) {
            out.writeByte(BOOLEAN_TYPE);
        } else if (c == Character.TYPE) {
            out.writeByte(CHARACTER_TYPE);
        } else if (c == Byte.TYPE) {
            out.writeByte(BYTE_TYPE);
        } else if (c == Short.TYPE) {
            out.writeByte(SHORT_TYPE);
        } else if (c == Integer.TYPE) {
            out.writeByte(INTEGER_TYPE);
        } else if (c == Long.TYPE) {
            out.writeByte(LONG_TYPE);
        } else if (c == Float.TYPE) {
            out.writeByte(FLOAT_TYPE);
        } else if (c == Double.TYPE) {
            out.writeByte(DOUBLE_TYPE);
        } else if (c == Void.TYPE) {
            out.writeByte(VOID_TYPE);
        } else if (c == null) {
            out.writeByte(NULL);
        } else {
            throw new InternalGemFireError(LocalizedStrings.InternalDataSerializer_UNKNOWN_PRIMITIVE_TYPE_0
                    .toLocalizedString(c.getName()));
        }
    }

    public static Class decodePrimitiveClass(byte typeCode) {
        switch (typeCode) {
        case BOOLEAN_TYPE:
            return Boolean.TYPE;
        case CHARACTER_TYPE:
            return Character.TYPE;
        case BYTE_TYPE:
            return Byte.TYPE;
        case SHORT_TYPE:
            return Short.TYPE;
        case INTEGER_TYPE:
            return Integer.TYPE;
        case LONG_TYPE:
            return Long.TYPE;
        case FLOAT_TYPE:
            return Float.TYPE;
        case DOUBLE_TYPE:
            return Double.TYPE;
        case VOID_TYPE:
            return Void.TYPE;
        case NULL:
            return null;
        default:
            throw new InternalGemFireError(
                    LocalizedStrings.InternalDataSerializer_UNEXPECTED_TYPECODE_0.toLocalizedString(typeCode));
        }
    }

    private static final byte TIME_UNIT_NANOSECONDS = -1;
    private static final byte TIME_UNIT_MICROSECONDS = -2;
    private static final byte TIME_UNIT_MILLISECONDS = -3;
    private static final byte TIME_UNIT_SECONDS = -4;

    /**
     * Reads a {@code TimeUnit} from a {@code DataInput}.
     *
     * @throws IOException A problem occurs while writing to {@code out}
     */
    private static TimeUnit readTimeUnit(DataInput in) throws IOException {
        InternalDataSerializer.checkIn(in);

        byte type = in.readByte();

        TimeUnit unit;
        switch (type) {
        case TIME_UNIT_NANOSECONDS:
            unit = TimeUnit.NANOSECONDS;
            break;
        case TIME_UNIT_MICROSECONDS:
            unit = TimeUnit.MICROSECONDS;
            break;
        case TIME_UNIT_MILLISECONDS:
            unit = TimeUnit.MILLISECONDS;
            break;
        case TIME_UNIT_SECONDS:
            unit = TimeUnit.SECONDS;
            break;
        default:
            throw new IOException(LocalizedStrings.DataSerializer_UNKNOWN_TIMEUNIT_TYPE_0.toLocalizedString(type));
        }

        if (logger.isTraceEnabled(LogMarker.SERIALIZER)) {
            logger.trace(LogMarker.SERIALIZER, "Read TimeUnit: {}", unit);
        }

        return unit;
    }

    private static void writeTimestamp(Timestamp o, DataOutput out) throws IOException {
        InternalDataSerializer.checkOut(out);

        if (logger.isTraceEnabled(LogMarker.SERIALIZER)) {
            logger.trace(LogMarker.SERIALIZER, "Writing Timestamp: {}", o);
        }
        DataSerializer.writePrimitiveLong(o.getTime(), out);
    }

    private static Timestamp readTimestamp(DataInput in) throws IOException {
        InternalDataSerializer.checkIn(in);
        Timestamp result = new Timestamp(DataSerializer.readPrimitiveLong(in));
        if (logger.isTraceEnabled(LogMarker.SERIALIZER)) {
            logger.trace(LogMarker.SERIALIZER, "Read Timestamp: {}", result);
        }
        return result;
    }

    private static void writeUUID(java.util.UUID o, DataOutput out) throws IOException {
        InternalDataSerializer.checkOut(out);

        if (logger.isTraceEnabled(LogMarker.SERIALIZER)) {
            logger.trace(LogMarker.SERIALIZER, "Writing UUID: {}", o);
        }
        DataSerializer.writePrimitiveLong(o.getMostSignificantBits(), out);
        DataSerializer.writePrimitiveLong(o.getLeastSignificantBits(), out);
    }

    private static UUID readUUID(DataInput in) throws IOException {
        InternalDataSerializer.checkIn(in);
        long mb = DataSerializer.readPrimitiveLong(in);
        long lb = DataSerializer.readPrimitiveLong(in);
        UUID result = new UUID(mb, lb);
        if (logger.isTraceEnabled(LogMarker.SERIALIZER)) {
            logger.trace(LogMarker.SERIALIZER, "Read UUID: {}", result);
        }
        return result;
    }

    private static void writeBigDecimal(BigDecimal o, DataOutput out) throws IOException {
        InternalDataSerializer.checkOut(out);

        if (logger.isTraceEnabled(LogMarker.SERIALIZER)) {
            logger.trace(LogMarker.SERIALIZER, "Writing BigDecimal: {}", o);
        }
        DataSerializer.writeString(o.toString(), out);
    }

    private static BigDecimal readBigDecimal(DataInput in) throws IOException {
        InternalDataSerializer.checkIn(in);
        BigDecimal result = new BigDecimal(DataSerializer.readString(in));
        if (logger.isTraceEnabled(LogMarker.SERIALIZER)) {
            logger.trace(LogMarker.SERIALIZER, "Read BigDecimal: {}", result);
        }
        return result;
    }

    private static void writeBigInteger(BigInteger o, DataOutput out) throws IOException {
        InternalDataSerializer.checkOut(out);

        if (logger.isTraceEnabled(LogMarker.SERIALIZER)) {
            logger.trace(LogMarker.SERIALIZER, "Writing BigInteger: {}", o);
        }
        DataSerializer.writeByteArray(o.toByteArray(), out);
    }

    private static BigInteger readBigInteger(DataInput in) throws IOException {
        InternalDataSerializer.checkIn(in);
        BigInteger result = new BigInteger(DataSerializer.readByteArray(in));
        if (logger.isTraceEnabled(LogMarker.SERIALIZER)) {
            logger.trace(LogMarker.SERIALIZER, "Read BigInteger: {}", result);
        }
        return result;
    }

    private static final ConcurrentMap dsfidToClassMap = logger.isTraceEnabled(LogMarker.DEBUG_DSFID)
            ? new ConcurrentHashMap()
            : null;

    public static void writeUserDataSerializableHeader(int classId, DataOutput out) throws IOException {
        if (classId <= Byte.MAX_VALUE && classId >= Byte.MIN_VALUE) {
            out.writeByte(USER_DATA_SERIALIZABLE);
            out.writeByte(classId);
        } else if (classId <= Short.MAX_VALUE && classId >= Short.MIN_VALUE) {
            out.writeByte(USER_DATA_SERIALIZABLE_2);
            out.writeShort(classId);
        } else {
            out.writeByte(USER_DATA_SERIALIZABLE_4);
            out.writeInt(classId);
        }
    }

    /**
     * Writes given number of characters from array of {@code char}s to a {@code DataOutput}.
     * 
     * @throws IOException A problem occurs while writing to {@code out}
     * 
     * @see DataSerializer#readCharArray
     * @since GemFire 6.6
     */
    public static void writeCharArray(char[] array, int length, DataOutput out) throws IOException {
        checkOut(out);

        if (array == null) {
            length = -1;
        }
        writeArrayLength(length, out);
        if (logger.isTraceEnabled(LogMarker.SERIALIZER)) {
            logger.trace(LogMarker.SERIALIZER, "Writing char array of length {}", length);
        }
        if (length > 0) {
            for (int i = 0; i < length; i++) {
                out.writeChar(array[i]);
            }
        }
    }

    /**
     * returns true if the byte array is the serialized form of a null reference
     * 
     * @param serializedForm the serialized byte array
     */
    public static boolean isSerializedNull(byte[] serializedForm) {
        return serializedForm.length == 1 && serializedForm[0] == NULL;
    }

    public static void basicWriteObject(Object o, DataOutput out, boolean ensurePdxCompatibility)
            throws IOException {
        checkOut(out);

        final boolean isDebugEnabled_SERIALIZER = logger.isTraceEnabled(LogMarker.SERIALIZER);
        if (isDebugEnabled_SERIALIZER) {
            logger.trace(LogMarker.SERIALIZER, "basicWriteObject: {}", o);
        }

        // Handle special objects first
        if (o == null) {
            out.writeByte(NULL);

        } else if (o instanceof DataSerializableFixedID) {
            checkPdxCompatible(o, ensurePdxCompatibility);
            DataSerializableFixedID dsfid = (DataSerializableFixedID) o;
            writeDSFID(dsfid, out);
        } else if (autoSerialized(o, out)) {
            // all done
        } else if (o instanceof DataSerializable.Replaceable) {
            // do this first to fix bug 31609
            // do this before DataSerializable
            Object replacement = ((DataSerializable.Replaceable) o).replace();
            basicWriteObject(replacement, out, ensurePdxCompatibility);

        } else if (o instanceof PdxSerializable) {
            writePdx(out,
                    GemFireCacheImpl.getForPdx("PDX registry is unavailable because the Cache has been closed."), o,
                    null);
        } else if (o instanceof DataSerializable) {
            if (isDebugEnabled_SERIALIZER) {
                logger.trace(LogMarker.SERIALIZER, "Writing DataSerializable: {}", o);
            }
            checkPdxCompatible(o, ensurePdxCompatibility);

            Class c = o.getClass();
            // Is "c" a user class registered with an Instantiator?
            int classId = InternalInstantiator.getClassId(c);
            if (classId != 0) {
                writeUserDataSerializableHeader(classId, out);
            } else {
                out.writeByte(DATA_SERIALIZABLE);
                DataSerializer.writeClass(c, out);
            }
            DataSerializable ds = (DataSerializable) o;
            invokeToData(ds, out);

        } else if (o instanceof Sendable) {
            if (!(o instanceof PdxInstance) || o instanceof PdxInstanceEnum) {
                checkPdxCompatible(o, ensurePdxCompatibility);
            }
            ((Sendable) o).sendTo(out);
        } else if (writeWellKnownObject(o, out, ensurePdxCompatibility)) {
            // Nothing more to do...
        } else {
            checkPdxCompatible(o, ensurePdxCompatibility);
            if (logger.isTraceEnabled(LogMarker.DUMP_SERIALIZED)) {
                logger.trace(LogMarker.DUMP_SERIALIZED, "DataSerializer Serializing an instance of {}",
                        o.getClass().getName());
            }

            /*
             * If the (internally known) ThreadLocal named "DataSerializer.DISALLOW_JAVA_SERIALIZATION" is
             * set, then an exception will be thrown if we try to do standard Java Serialization. This is
             * used to catch Java serialization early for the case where the data is being sent to a
             * non-Java client
             */
            if (disallowJavaSerialization() && o instanceof Serializable) {
                throw new NotSerializableException(
                        LocalizedStrings.DataSerializer_0_IS_NOT_DATASERIALIZABLE_AND_JAVA_SERIALIZATION_IS_DISALLOWED
                                .toLocalizedString(o.getClass().getName()));
            }

            writeSerializableObject(o, out);
        }
    }

    private static boolean disallowJavaSerialization() {
        Boolean v = DISALLOW_JAVA_SERIALIZATION.get();
        return v != null && v;
    }

    /**
     * @since GemFire 6.6.2
     */
    private static void writePdxEnum(Enum<?> e, DataOutput out) throws IOException {
        TypeRegistry tr = GemFireCacheImpl
                .getForPdx("PDX registry is unavailable because the Cache has been closed.").getPdxRegistry();
        int eId = tr.getEnumId(e);
        if (logger.isTraceEnabled(LogMarker.SERIALIZER)) {
            logger.trace(LogMarker.SERIALIZER, "write PdxEnum id={} enum={}", eId, e);
        }
        writePdxEnumId(eId, out);
    }

    public static void writePdxEnumId(int eId, DataOutput out) throws IOException {
        out.writeByte(PDX_ENUM);
        out.writeByte(eId >> 24);
        writeArrayLength(eId & 0xFFFFFF, out);
    }

    /**
     * @throws IOException since 6.6.2
     */
    private static Object readPdxEnum(DataInput in) throws IOException {
        int dsId = in.readByte();
        int tmp = readArrayLength(in);
        int enumId = dsId << 24 | tmp & 0xFFFFFF;
        if (logger.isTraceEnabled(LogMarker.SERIALIZER)) {
            logger.trace(LogMarker.SERIALIZER, "read PdxEnum id={}", enumId);
        }
        InternalCache internalCache = GemFireCacheImpl
                .getForPdx("PDX registry is unavailable because the Cache has been closed.");
        TypeRegistry tr = internalCache.getPdxRegistry();

        Object result = tr.getEnumById(enumId);
        if (result instanceof PdxInstance) {
            getDMStats(internalCache).incPdxInstanceCreations();
        }
        return result;
    }

    private static void writeGemFireEnum(Enum<?> e, DataOutput out) throws IOException {
        boolean isGemFireObject = isGemfireObject(e);
        DataSerializer.writePrimitiveByte(isGemFireObject ? GEMFIRE_ENUM : PDX_INLINE_ENUM, out);
        DataSerializer.writeString(e.getDeclaringClass().getName(), out);
        DataSerializer.writeString(e.name(), out);
        if (!isGemFireObject) {
            InternalDataSerializer.writeArrayLength(e.ordinal(), out);
        }
    }

    @SuppressWarnings("unchecked")
    private static Enum<?> readGemFireEnum(DataInput in) throws IOException, ClassNotFoundException {
        String className = DataSerializer.readString(in);
        String enumName = DataSerializer.readString(in);
        @SuppressWarnings("rawtypes")
        Class c = getCachedClass(className);
        return Enum.valueOf(c, enumName);
    }

    private static Object readPdxInlineEnum(DataInput in) throws IOException, ClassNotFoundException {
        InternalCache internalCache = GemFireCacheImpl.getInstance();
        if (internalCache != null && internalCache.getPdxReadSerializedByAnyGemFireServices()) {
            String className = DataSerializer.readString(in);
            String enumName = DataSerializer.readString(in);
            int enumOrdinal = InternalDataSerializer.readArrayLength(in);
            getDMStats(internalCache).incPdxInstanceCreations();
            return new PdxInstanceEnum(className, enumName, enumOrdinal);
        } else {
            Enum<?> e = readGemFireEnum(in);
            InternalDataSerializer.readArrayLength(in);
            return e;
        }
    }

    /**
     * write an object in java Serializable form with a SERIALIZABLE DSCODE so that it can be
     * deserialized with DataSerializer.readObject()
     * 
     * @param o the object to serialize
     * @param out the data output to serialize to
     */
    public static void writeSerializableObject(Object o, DataOutput out) throws IOException {
        out.writeByte(SERIALIZABLE);
        if (out instanceof ObjectOutputStream) {
            ((ObjectOutputStream) out).writeObject(o);
        } else {
            OutputStream stream;
            if (out instanceof OutputStream) {
                stream = (OutputStream) out;

            } else {
                final DataOutput out2 = out;
                stream = new OutputStream() {
                    @Override
                    public void write(int b) throws IOException {
                        out2.write(b);
                    }
                };
            }
            boolean wasDoNotCopy = false;
            if (out instanceof HeapDataOutputStream) {
                // To fix bug 52197 disable doNotCopy mode
                // while serialize with an ObjectOutputStream.
                // The problem is that ObjectOutputStream keeps
                // an internal byte array that it reuses while serializing.
                wasDoNotCopy = ((HeapDataOutputStream) out).setDoNotCopy(false);
            }
            try {
                ObjectOutput oos = new ObjectOutputStream(stream);
                if (stream instanceof VersionedDataStream) {
                    Version v = ((VersionedDataStream) stream).getVersion();
                    if (v != null && v != Version.CURRENT) {
                        oos = new VersionedObjectOutput(oos, v);
                    }
                }
                oos.writeObject(o);
                // To fix bug 35568 just call flush. We can't call close because
                // it calls close on the wrapped OutputStream.
                oos.flush();
            } finally {
                if (wasDoNotCopy) {
                    ((HeapDataOutputStream) out).setDoNotCopy(true);
                }
            }
        }
    }

    /**
     * For backward compatibility this method should be used to invoke toData on a DSFID or
     * DataSerializable. It will invoke the correct toData method based on the class's version
     * information. This method does not write information about the class of the object. When
     * deserializing use the method invokeFromData to read the contents of the object.
     * 
     * @param ds the object to write
     * @param out the output stream.
     */
    public static void invokeToData(Object ds, DataOutput out) throws IOException {
        boolean isDSFID = ds instanceof DataSerializableFixedID;
        try {
            boolean invoked = false;
            Version v = InternalDataSerializer.getVersionForDataStreamOrNull(out);

            if (v != null && v != Version.CURRENT) {
                // get versions where DataOutput was upgraded
                Version[] versions = null;
                if (ds instanceof SerializationVersions) {
                    SerializationVersions sv = (SerializationVersions) ds;
                    versions = sv.getSerializationVersions();
                }
                // check if the version of the peer or diskstore is different and
                // there has been a change in the message
                if (versions != null && versions.length > 0) {
                    for (Version version : versions) {
                        // if peer version is less than the greatest upgraded version
                        if (v.compareTo(version) < 0) {
                            ds.getClass().getMethod("toDataPre_" + version.getMethodSuffix(),
                                    new Class[] { DataOutput.class }).invoke(ds, out);
                            invoked = true;
                            break;
                        }
                    }
                }
            }

            if (!invoked) {
                if (isDSFID) {
                    ((DataSerializableFixedID) ds).toData(out);
                } else {
                    ((DataSerializable) ds).toData(out);
                }
            }
        } catch (IOException io) {
            // DSFID serialization expects an IOException but otherwise
            // we want to catch it and transform into a ToDataException
            // since it might be in user code and we want to report it
            // as a problem with the plugin code
            if (isDSFID) {
                throw io;
            } else {
                throw new ToDataException("toData failed on DataSerializable " + ds.getClass(), io);
            }
        } catch (CancelException | ToDataException | GemFireRethrowable ex) {
            // Serializing a PDX can result in a cache closed exception. Just rethrow
            throw ex;
        } catch (VirtualMachineError err) {
            SystemFailure.initiateFailure(err);
            // If this ever returns, rethrow the error. We're poisoned
            // now, so don't let this thread continue.
            throw err;
        } catch (Throwable t) {
            // Whenever you catch Error or Throwable, you must also
            // catch VirtualMachineError (see above). However, there is
            // _still_ a possibility that you are dealing with a cascading
            // error condition, so you also need to check to see if the JVM
            // is still usable:
            SystemFailure.checkFailure();
            throw new ToDataException("toData failed on DataSerializable " + ds.getClass(), t);
        }
    }

    /**
     * For backward compatibility this method should be used to invoke fromData on a DSFID or
     * DataSerializable. It will invoke the correct fromData method based on the class's version
     * information. This method does not read information about the class of the object. When
     * serializing use the method invokeToData to write the contents of the object.
     * 
     * @param ds the object to write
     * @param in the input stream.
     */
    public static void invokeFromData(Object ds, DataInput in) throws IOException, ClassNotFoundException {
        try {
            boolean invoked = false;
            Version v = InternalDataSerializer.getVersionForDataStreamOrNull(in);
            if (v != null && v != Version.CURRENT) {
                // get versions where DataOutput was upgraded
                Version[] versions = null;
                if (ds instanceof SerializationVersions) {
                    SerializationVersions vds = (SerializationVersions) ds;
                    versions = vds.getSerializationVersions();
                }
                // check if the version of the peer or diskstore is different and
                // there has been a change in the message
                if (versions != null && versions.length > 0) {
                    for (Version version : versions) {
                        // if peer version is less than the greatest upgraded version
                        if (v.compareTo(version) < 0) {
                            ds.getClass().getMethod("fromDataPre" + '_' + version.getMethodSuffix(),
                                    new Class[] { DataInput.class }).invoke(ds, in);
                            invoked = true;
                            break;
                        }
                    }
                }
            }
            if (!invoked) {
                if (ds instanceof DataSerializableFixedID) {
                    ((DataSerializableFixedID) ds).fromData(in);
                } else {
                    ((DataSerializable) ds).fromData(in);
                }
            }
        } catch (EOFException | ClassNotFoundException | CacheClosedException ex) {
            // client went away - ignore
            throw ex;
        } catch (Exception ex) {
            throw new SerializationException(LocalizedStrings.DataSerializer_COULD_NOT_CREATE_AN_INSTANCE_OF_0
                    .toLocalizedString(ds.getClass().getName()), ex);
        }
    }

    private static Object readDataSerializable(final DataInput in) throws IOException, ClassNotFoundException {
        Class c = readClass(in);
        try {
            Constructor init = c.getConstructor(new Class[0]);
            init.setAccessible(true);
            Object o = init.newInstance(new Object[0]);
            Assert.assertTrue(o instanceof DataSerializable);
            invokeFromData(o, in);

            if (logger.isTraceEnabled(LogMarker.SERIALIZER)) {
                logger.trace(LogMarker.SERIALIZER, "Read DataSerializable {}", o);
            }

            return o;

        } catch (EOFException ex) {
            // client went away - ignore
            throw ex;
        } catch (Exception ex) {
            throw new SerializationException(LocalizedStrings.DataSerializer_COULD_NOT_CREATE_AN_INSTANCE_OF_0
                    .toLocalizedString(c.getName()), ex);
        }
    }

    private static Object readDataSerializableFixedID(final DataInput in)
            throws IOException, ClassNotFoundException {
        Class c = readClass(in);
        try {
            Constructor init = c.getConstructor(new Class[0]);
            init.setAccessible(true);
            Object o = init.newInstance(new Object[0]);

            invokeFromData(o, in);

            if (logger.isTraceEnabled(LogMarker.SERIALIZER)) {
                logger.trace(LogMarker.SERIALIZER, "Read DataSerializableFixedID {}", o);
            }

            return o;

        } catch (Exception ex) {
            throw new SerializationException(LocalizedStrings.DataSerializer_COULD_NOT_CREATE_AN_INSTANCE_OF_0
                    .toLocalizedString(c.getName()), ex);
        }
    }

    /**
     * Get the {@link Version} of the peer or disk store that created this {@link DataInput}.
     */
    public static Version getVersionForDataStream(DataInput in) {
        // check if this is a versioned data input
        if (in instanceof VersionedDataStream) {
            final Version v = ((VersionedDataStream) in).getVersion();
            return v != null ? v : Version.CURRENT;
        } else {
            // assume latest version
            return Version.CURRENT;
        }
    }

    /**
     * Get the {@link Version} of the peer or disk store that created this {@link DataInput}. Returns
     * null if the version is same as this member's.
     */
    public static Version getVersionForDataStreamOrNull(DataInput in) {
        // check if this is a versioned data input
        if (in instanceof VersionedDataStream) {
            return ((VersionedDataStream) in).getVersion();
        } else {
            // assume latest version
            return null;
        }
    }

    /**
     * Get the {@link Version} of the peer or disk store that created this {@link DataOutput}.
     */
    public static Version getVersionForDataStream(DataOutput out) {
        // check if this is a versioned data output
        if (out instanceof VersionedDataStream) {
            final Version v = ((VersionedDataStream) out).getVersion();
            return v != null ? v : Version.CURRENT;
        } else {
            // assume latest version
            return Version.CURRENT;
        }
    }

    /**
     * Get the {@link Version} of the peer or disk store that created this {@link DataOutput}. Returns
     * null if the version is same as this member's.
     */
    public static Version getVersionForDataStreamOrNull(DataOutput out) {
        // check if this is a versioned data output
        if (out instanceof VersionedDataStream) {
            return ((VersionedDataStream) out).getVersion();
        } else {
            // assume latest version
            return null;
        }
    }

    // array is null
    public static final byte NULL_ARRAY = -1;

    /**
     * array len encoded as unsigned short in next 2 bytes
     *
     * @since GemFire 5.7
     */
    private static final byte SHORT_ARRAY_LEN = -2;

    /**
     * array len encoded as int in next 4 bytes
     *
     * @since GemFire 5.7
     */
    public static final byte INT_ARRAY_LEN = -3;

    private static final int MAX_BYTE_ARRAY_LEN = (byte) -4 & 0xFF;

    public static void writeArrayLength(int len, DataOutput out) throws IOException {
        if (len == -1) {
            out.writeByte(NULL_ARRAY);
        } else if (len <= MAX_BYTE_ARRAY_LEN) {
            out.writeByte(len);
        } else if (len <= 0xFFFF) {
            out.writeByte(SHORT_ARRAY_LEN);
            out.writeShort(len);
        } else {
            out.writeByte(INT_ARRAY_LEN);
            out.writeInt(len);
        }
    }

    public static int readArrayLength(DataInput in) throws IOException {
        byte code = in.readByte();
        if (code == NULL_ARRAY) {
            return -1;
        } else {
            int result = ubyteToInt(code);
            if (result > MAX_BYTE_ARRAY_LEN) {
                if (code == SHORT_ARRAY_LEN) {
                    result = in.readUnsignedShort();
                } else if (code == INT_ARRAY_LEN) {
                    result = in.readInt();
                } else {
                    throw new IllegalStateException("unexpected array length code=" + code);
                }
            }
            return result;
        }
    }

    /**
     * Serializes a list of Integers. The argument may be null. Deserialize with readListOfIntegers().
     *
     * TODO: writeListOfIntegers is unused
     */
    public void writeListOfIntegers(List<Integer> list, DataOutput out) throws IOException {
        int size;
        if (list == null) {
            size = -1;
        } else {
            size = list.size();
        }
        InternalDataSerializer.writeArrayLength(size, out);
        if (size > 0) {
            for (int i = 0; i < size; i++) {
                out.writeInt(list.get(i));
            }
        }
    }

    public static Object readDSFID(final DataInput in) throws IOException, ClassNotFoundException {
        checkIn(in);
        byte header = in.readByte();
        if (logger.isTraceEnabled(LogMarker.SERIALIZER)) {
            logger.trace(LogMarker.SERIALIZER, "readDSFID: header={}", header);
        }
        if (header == DS_FIXED_ID_BYTE) {
            return DSFIDFactory.create(in.readByte(), in);
        } else if (header == DS_FIXED_ID_SHORT) {
            return DSFIDFactory.create(in.readShort(), in);
        } else if (header == DS_NO_FIXED_ID) {
            return readDataSerializableFixedID(in);
        } else if (header == DS_FIXED_ID_INT) {
            return DSFIDFactory.create(in.readInt(), in);
        } else {
            throw new IllegalStateException("unexpected byte: " + header + " while reading dsfid");
        }
    }

    public static int readDSFIDHeader(final DataInput in) throws IOException {
        checkIn(in);
        byte header = in.readByte();
        if (header == DS_FIXED_ID_BYTE) {
            return in.readByte();
        } else if (header == DS_FIXED_ID_SHORT) {
            return in.readShort();
        } else if (header == DS_NO_FIXED_ID) {
            // is that correct??
            return Integer.MAX_VALUE;
        } else if (header == DS_FIXED_ID_INT) {
            return in.readInt();
        } else {
            throw new IllegalStateException("unexpected byte: " + header + " while reading dsfid");
        }
    }

    /**
     * Reads an instance of {@code String} from a {@code DataInput} given the header byte already
     * being read. The return value may be {@code null}.
     *
     * @throws IOException A problem occurs while reading from {@code in}
     *
     * @since GemFire 5.7
     */
    public static String readString(DataInput in, byte header) throws IOException {
        if (header == DSCODE.STRING_BYTES) {
            int len = in.readUnsignedShort();
            if (logger.isTraceEnabled(LogMarker.SERIALIZER)) {
                logger.trace(LogMarker.SERIALIZER, "Reading STRING_BYTES of len={}", len);
            }
            byte[] buf = new byte[len];
            in.readFully(buf, 0, len);
            return new String(buf, 0); // intentionally using deprecated constructor
        } else if (header == DSCODE.STRING) {
            if (logger.isTraceEnabled(LogMarker.SERIALIZER)) {
                logger.trace(LogMarker.SERIALIZER, "Reading utf STRING");
            }
            return in.readUTF();
        } else if (header == DSCODE.NULL_STRING) {
            if (logger.isTraceEnabled(LogMarker.SERIALIZER)) {
                logger.trace(LogMarker.SERIALIZER, "Reading NULL_STRING");
            }
            return null;
        } else if (header == DSCODE.HUGE_STRING_BYTES) {
            int len = in.readInt();
            if (logger.isTraceEnabled(LogMarker.SERIALIZER)) {
                logger.trace(LogMarker.SERIALIZER, "Reading HUGE_STRING_BYTES of len={}", len);
            }
            byte[] buf = new byte[len];
            in.readFully(buf, 0, len);
            return new String(buf, 0); // intentionally using deprecated constructor
        } else if (header == DSCODE.HUGE_STRING) {
            int len = in.readInt();
            if (logger.isTraceEnabled(LogMarker.SERIALIZER)) {
                logger.trace(LogMarker.SERIALIZER, "Reading HUGE_STRING of len={}", len);
            }
            char[] buf = new char[len];
            for (int i = 0; i < len; i++) {
                buf[i] = in.readChar();
            }
            return new String(buf);
        } else {
            String s = "Unknown String header " + header;
            throw new IOException(s);
        }
    }

    private static DataSerializer dvddeserializer;

    // TODO: registerDVDDeserializer is unused
    public static void registerDVDDeserializer(DataSerializer dvddeslzr) {
        dvddeserializer = dvddeslzr;
    }

    /**
     * Just like readObject but make sure and pdx deserialized is not a PdxInstance.
     * 
     * @since GemFire 6.6.2
     */
    public static <T> T readNonPdxInstanceObject(final DataInput in) throws IOException, ClassNotFoundException {
        boolean wouldReadSerialized = PdxInstanceImpl.getPdxReadSerialized();
        if (!wouldReadSerialized) {
            return DataSerializer.readObject(in);
        } else {
            PdxInstanceImpl.setPdxReadSerialized(false);
            try {
                return DataSerializer.readObject(in);
            } finally {
                PdxInstanceImpl.setPdxReadSerialized(true);
            }
        }
    }

    public static Object basicReadObject(final DataInput in) throws IOException, ClassNotFoundException {
        checkIn(in);

        // Read the header byte
        byte header = in.readByte();
        if (logger.isTraceEnabled(LogMarker.SERIALIZER)) {
            logger.trace(LogMarker.SERIALIZER, "basicReadObject: header={}", header);
        }
        switch (header) {
        case DS_FIXED_ID_BYTE:
            return DSFIDFactory.create(in.readByte(), in);
        case DS_FIXED_ID_SHORT:
            return DSFIDFactory.create(in.readShort(), in);
        case DS_FIXED_ID_INT:
            return DSFIDFactory.create(in.readInt(), in);
        case DS_NO_FIXED_ID:
            return readDataSerializableFixedID(in);
        case NULL:
            return null;
        case NULL_STRING:
        case STRING:
        case HUGE_STRING:
        case STRING_BYTES:
        case HUGE_STRING_BYTES:
            return readString(in, header);
        case CLASS:
            return readClass(in);
        case DATE:
            return readDate(in);
        case FILE:
            return readFile(in);
        case INET_ADDRESS:
            return readInetAddress(in);
        case BOOLEAN:
            return readBoolean(in);
        case CHARACTER:
            return readCharacter(in);
        case BYTE:
            return readByte(in);
        case SHORT:
            return readShort(in);
        case INTEGER:
            return readInteger(in);
        case LONG:
            return readLong(in);
        case FLOAT:
            return readFloat(in);
        case DOUBLE:
            return readDouble(in);
        case BYTE_ARRAY:
            return readByteArray(in);
        case ARRAY_OF_BYTE_ARRAYS:
            return readArrayOfByteArrays(in);
        case SHORT_ARRAY:
            return readShortArray(in);
        case STRING_ARRAY:
            return readStringArray(in);
        case INT_ARRAY:
            return readIntArray(in);
        case LONG_ARRAY:
            return readLongArray(in);
        case FLOAT_ARRAY:
            return readFloatArray(in);
        case DOUBLE_ARRAY:
            return readDoubleArray(in);
        case BOOLEAN_ARRAY:
            return readBooleanArray(in);
        case CHAR_ARRAY:
            return readCharArray(in);
        case OBJECT_ARRAY:
            return readObjectArray(in);
        case ARRAY_LIST:
            return readArrayList(in);
        case LINKED_LIST:
            return readLinkedList(in);
        case HASH_SET:
            return readHashSet(in);
        case LINKED_HASH_SET:
            return readLinkedHashSet(in);
        case HASH_MAP:
            return readHashMap(in);
        case IDENTITY_HASH_MAP:
            return readIdentityHashMap(in);
        case HASH_TABLE:
            return readHashtable(in);
        case CONCURRENT_HASH_MAP:
            return readConcurrentHashMap(in);
        case PROPERTIES:
            return readProperties(in);
        case TIME_UNIT:
            return readTimeUnit(in);
        case USER_CLASS:
            return readUserObject(in, in.readByte());
        case USER_CLASS_2:
            return readUserObject(in, in.readShort());
        case USER_CLASS_4:
            return readUserObject(in, in.readInt());
        case VECTOR:
            return readVector(in);
        case STACK:
            return readStack(in);
        case TREE_MAP:
            return readTreeMap(in);
        case TREE_SET:
            return readTreeSet(in);
        case BOOLEAN_TYPE:
            return Boolean.TYPE;
        case CHARACTER_TYPE:
            return Character.TYPE;
        case BYTE_TYPE:
            return Byte.TYPE;
        case SHORT_TYPE:
            return Short.TYPE;
        case INTEGER_TYPE:
            return Integer.TYPE;
        case LONG_TYPE:
            return Long.TYPE;
        case FLOAT_TYPE:
            return Float.TYPE;
        case DOUBLE_TYPE:
            return Double.TYPE;
        case VOID_TYPE:
            return Void.TYPE;

        case USER_DATA_SERIALIZABLE:
            return readUserDataSerializable(in, in.readByte());
        case USER_DATA_SERIALIZABLE_2:
            return readUserDataSerializable(in, in.readShort());
        case USER_DATA_SERIALIZABLE_4:
            return readUserDataSerializable(in, in.readInt());

        case DATA_SERIALIZABLE:
            return readDataSerializable(in);

        case SERIALIZABLE: {
            final boolean isDebugEnabled_SERIALIZER = logger.isTraceEnabled(LogMarker.SERIALIZER);
            Object serializableResult;
            if (in instanceof DSObjectInputStream) {
                serializableResult = ((DSObjectInputStream) in).readObject();
            } else {
                InputStream stream;
                if (in instanceof InputStream) {
                    stream = (InputStream) in;
                } else {
                    stream = new InputStream() {
                        @Override
                        public int read() throws IOException {
                            try {
                                return in.readUnsignedByte(); // fix for bug 47249
                            } catch (EOFException ignored) {
                                return -1;
                            }
                        }

                    };
                }

                ObjectInput ois = new DSObjectInputStream(stream);
                if (stream instanceof VersionedDataStream) {
                    Version v = ((VersionedDataStream) stream).getVersion();
                    if (v != null && v != Version.CURRENT) {
                        ois = new VersionedObjectInput(ois, v);
                    }
                }

                serializableResult = ois.readObject();

                if (isDebugEnabled_SERIALIZER) {
                    logger.trace(LogMarker.SERIALIZER, "Read Serializable object: {}", serializableResult);
                }
            }
            if (isDebugEnabled_SERIALIZER) {
                logger.trace(LogMarker.SERIALIZER, "deserialized instanceof {}", serializableResult.getClass());
            }
            return serializableResult;
        }
        case PDX:
            return readPdxSerializable(in);
        case PDX_ENUM:
            return readPdxEnum(in);
        case GEMFIRE_ENUM:
            return readGemFireEnum(in);
        case PDX_INLINE_ENUM:
            return readPdxInlineEnum(in);
        case BIG_INTEGER:
            return readBigInteger(in);
        case BIG_DECIMAL:
            return readBigDecimal(in);
        case UUID:
            return readUUID(in);
        case TIMESTAMP:
            return readTimestamp(in);
        default:
            String s = "Unknown header byte: " + header;
            throw new IOException(s);
        }
    }

    private static Object readUserDataSerializable(final DataInput in, int classId) throws IOException {
        Instantiator instantiator = InternalInstantiator.getInstantiator(classId);
        if (instantiator == null) {
            logger.error(LogMarker.SERIALIZER,
                    LocalizedMessage.create(
                            LocalizedStrings.DataSerializer_NO_INSTANTIATOR_HAS_BEEN_REGISTERED_FOR_CLASS_WITH_ID_0,
                            classId));
            throw new IOException(
                    LocalizedStrings.DataSerializer_NO_INSTANTIATOR_HAS_BEEN_REGISTERED_FOR_CLASS_WITH_ID_0
                            .toLocalizedString(classId));

        } else {
            try {
                DataSerializable ds;
                if (instantiator instanceof CanonicalInstantiator) {
                    CanonicalInstantiator ci = (CanonicalInstantiator) instantiator;
                    ds = ci.newInstance(in);
                } else {
                    ds = instantiator.newInstance();
                }
                ds.fromData(in);
                return ds;

            } catch (Exception ex) {
                throw new SerializationException(
                        LocalizedStrings.DataSerializer_COULD_NOT_DESERIALIZE_AN_INSTANCE_OF_0
                                .toLocalizedString(instantiator.getInstantiatedClass().getName()),
                        ex);
            }
        }
    }

    private static final ThreadLocal<Boolean> pdxSerializationInProgress = new ThreadLocal<>();

    public static boolean isPdxSerializationInProgress() {
        Boolean v = pdxSerializationInProgress.get();
        return v != null && v;
    }

    public static void setPdxSerializationInProgress(boolean v) {
        if (v) {
            pdxSerializationInProgress.set(true);
        } else {
            pdxSerializationInProgress.set(false);
        }
    }

    public static boolean writePdx(DataOutput out, InternalCache internalCache, Object pdx,
            PdxSerializer pdxSerializer) throws IOException {
        TypeRegistry tr = null;
        if (internalCache != null) {
            tr = internalCache.getPdxRegistry();
        }

        PdxOutputStream os;
        if (out instanceof HeapDataOutputStream) {
            os = new PdxOutputStream((HeapDataOutputStream) out);
        } else {
            os = new PdxOutputStream();
        }
        PdxWriterImpl writer = new PdxWriterImpl(tr, pdx, os);

        try {
            if (pdxSerializer != null) {
                // Hack to make sure we don't pass internal objects to the user's
                // serializer
                if (isGemfireObject(pdx)) {
                    return false;
                }
                if (is662SerializationEnabled()) {
                    boolean alreadyInProgress = isPdxSerializationInProgress();
                    if (!alreadyInProgress) {
                        setPdxSerializationInProgress(true);
                        try {
                            if (!pdxSerializer.toData(pdx, writer)) {
                                return false;
                            }
                        } finally {
                            setPdxSerializationInProgress(false);
                        }
                    } else {
                        if (!pdxSerializer.toData(pdx, writer)) {
                            return false;
                        }
                    }
                } else {
                    if (!pdxSerializer.toData(pdx, writer)) {
                        return false;
                    }
                }
            } else {
                if (is662SerializationEnabled()) {
                    boolean alreadyInProgress = isPdxSerializationInProgress();
                    if (!alreadyInProgress) {
                        setPdxSerializationInProgress(true);
                        try {
                            ((PdxSerializable) pdx).toData(writer);
                        } finally {
                            setPdxSerializationInProgress(false);
                        }
                    } else {
                        ((PdxSerializable) pdx).toData(writer);
                    }
                } else {
                    ((PdxSerializable) pdx).toData(writer);
                }
            }
        } catch (ToDataException | CancelException | NonPortableClassException | GemFireRethrowable ex) {
            throw ex;
        } catch (VirtualMachineError err) {
            SystemFailure.initiateFailure(err);
            // If this ever returns, rethrow the error. We're poisoned
            // now, so don't let this thread continue.
            throw err;
        } catch (Throwable t) {
            // Whenever you catch Error or Throwable, you must also
            // catch VirtualMachineError (see above). However, there is
            // _still_ a possibility that you are dealing with a cascading
            // error condition, so you also need to check to see if the JVM
            // is still usable:
            SystemFailure.checkFailure();
            if (pdxSerializer != null) {
                throw new ToDataException("PdxSerializer failed when calling toData on " + pdx.getClass(), t);
            } else {
                throw new ToDataException("toData failed on PdxSerializable " + pdx.getClass(), t);
            }
        }
        int bytesWritten = writer.completeByteStreamGeneration();
        getDMStats(internalCache).incPdxSerialization(bytesWritten);
        if (!(out instanceof HeapDataOutputStream)) {
            writer.sendTo(out);
        }
        return true;
    }

    public static DMStats getDMStats(InternalCache internalCache) {
        if (internalCache != null) {
            return internalCache.getDistributionManager().getStats();
        } else {
            DMStats result = InternalDistributedSystem.getDMStats();
            if (result == null) {
                result = new LonerDistributionManager.DummyDMStats();
            }
            return result;
        }
    }

    private static Object readPdxSerializable(final DataInput in) throws IOException, ClassNotFoundException {
        int len = in.readInt();
        int typeId = in.readInt();

        InternalCache internalCache = GemFireCacheImpl
                .getForPdx("PDX registry is unavailable because the Cache has been closed.");
        PdxType pdxType = internalCache.getPdxRegistry().getType(typeId);
        if (logger.isTraceEnabled(LogMarker.SERIALIZER)) {
            logger.trace(LogMarker.SERIALIZER, "readPdxSerializable pdxType={}", pdxType);
        }
        if (pdxType == null) {
            throw new IllegalStateException("Unknown pdx type=" + typeId);
        }

        DMStats dmStats = getDMStats(internalCache);
        dmStats.incPdxDeserialization(len + 9);

        // check if PdxInstance needs to be returned.
        if (pdxType.getNoDomainClass() || internalCache.getPdxReadSerializedByAnyGemFireServices()) {
            dmStats.incPdxInstanceCreations();
            return new PdxInstanceImpl(pdxType, in, len);
        } else {
            PdxReaderImpl pdxReader = new PdxReaderImpl(pdxType, in, len);
            return pdxReader.getObject();
        }
    }

    /**
     * Reads a PdxInstance from dataBytes and returns it. If the first object read is not pdx encoded
     * returns null.
     */
    public static PdxInstance readPdxInstance(final byte[] dataBytes, InternalCache internalCache) {
        try {
            byte type = dataBytes[0];
            if (type == PDX) {
                PdxInputStream in = new PdxInputStream(dataBytes);
                in.readByte(); // throw away the type byte
                int len = in.readInt();
                int typeId = in.readInt();
                PdxType pdxType = internalCache.getPdxRegistry().getType(typeId);
                if (pdxType == null) {
                    throw new IllegalStateException("Unknown pdx type=" + typeId);
                }

                return new PdxInstanceImpl(pdxType, in, len);
            } else if (type == DSCODE.PDX_ENUM) {
                PdxInputStream in = new PdxInputStream(dataBytes);
                in.readByte(); // throw away the type byte
                int dsId = in.readByte();
                int tmp = readArrayLength(in);
                int enumId = dsId << 24 | tmp & 0xFFFFFF;
                TypeRegistry tr = internalCache.getPdxRegistry();
                EnumInfo ei = tr.getEnumInfoById(enumId);
                if (ei == null) {
                    throw new IllegalStateException("Unknown pdx enum id=" + enumId);
                }
                return ei.getPdxInstance(enumId);
            } else if (type == DSCODE.PDX_INLINE_ENUM) {
                PdxInputStream in = new PdxInputStream(dataBytes);
                in.readByte(); // throw away the type byte
                String className = DataSerializer.readString(in);
                String enumName = DataSerializer.readString(in);
                int enumOrdinal = InternalDataSerializer.readArrayLength(in);
                return new PdxInstanceEnum(className, enumName, enumOrdinal);
            }
        } catch (IOException ignore) {
        }
        return null;
    }

    public static int getLoadedDataSerializers() {
        return idsToSerializers.size();
    }

    public static Map getDsClassesToHoldersMap() {
        return dsClassesToHolders;
    }

    public static Map getIdsToHoldersMap() {
        return idsToHolders;
    }

    public static Map getSupportedClassesToHoldersMap() {
        return supportedClassesToHolders;
    }

    /**
     * A marker object for {@code DataSerializer}s that have not been registered. Using this marker
     * object allows us to asynchronously send {@code DataSerializer} registration updates. If the
     * serialized bytes arrive at a VM before the registration message does, the deserializer will
     * wait an amount of time for the registration message to arrive.
     */
    abstract static class Marker {
        /** The DataSerializer that is filled in upon registration */
        protected DataSerializer serializer = null;

        /** set to true once setSerializer is called. */
        boolean hasBeenSet = false;

        abstract DataSerializer getSerializer();

        /**
         * Sets the serializer associated with this marker. It will notify any threads that are waiting
         * for the serializer to be registered.
         */
        void setSerializer(DataSerializer serializer) {
            synchronized (this) {
                this.hasBeenSet = true;
                this.serializer = serializer;
                this.notifyAll();
            }
        }
    }

    /**
     * A marker object for {@code DataSerializer}s that have not been registered. Using this marker
     * object allows us to asynchronously send {@code DataSerializer} registration updates. If the
     * serialized bytes arrive at a VM before the registration message does, the deserializer will
     * wait an amount of time for the registration message to arrive. Made public for unit test
     * access.
     * 
     * @since GemFire 5.7
     */
    public static class GetMarker extends Marker {
        /**
         * Number of milliseconds to wait. Also used by InternalInstantiator. Note that some tests set
         * this to a small amount to speed up failures. Made public for unit test access.
         */
        public static int WAIT_MS = Integer
                .getInteger(DistributionConfig.GEMFIRE_PREFIX + "InternalDataSerializer.WAIT_MS", 60 * 1000);

        /**
         * Returns the serializer associated with this marker. If the serializer has not been registered
         * yet, then this method will wait until the serializer is registered. If this method has to
         * wait for too long, then {@code null} is returned.
         */
        @Override
        DataSerializer getSerializer() {
            synchronized (this) {
                boolean firstTime = true;
                long endTime = 0;
                while (!this.hasBeenSet) {
                    if (firstTime) {
                        firstTime = false;
                        endTime = System.currentTimeMillis() + WAIT_MS;
                    }
                    try {
                        long remainingMs = endTime - System.currentTimeMillis();
                        if (remainingMs > 0) {
                            this.wait(remainingMs); // spurious wakeup ok
                        } else {
                            // timed out call setSerializer just to make sure that anyone else
                            // also waiting on this marker times out also
                            setSerializer(null);
                            break;
                        }
                    } catch (InterruptedException ignored) {
                        Thread.currentThread().interrupt();
                        // Just return null, let it fail
                        return null;
                    }
                }
                return this.serializer;
            }
        }
    }

    /**
     * A marker object for {@code DataSerializer}s that is in the process of being registered. It is
     * possible for getSerializer to return {@code null}
     * 
     * @since GemFire 5.7
     */
    static class InitMarker extends Marker {
        /*
         * Returns the serializer associated with this marker. If the serializer has not been registered
         * yet, then this method will wait until the serializer is registered. If this method has to
         * wait for too long, then {@code null} is returned.
         */
        /**
         * Returns the serializer associated with this marker. Waits forever (unless interrupted) for it
         * to be initialized. Returns null if this Marker failed to initialize.
         */
        @Override
        DataSerializer getSerializer() {
            synchronized (this) {
                while (!this.hasBeenSet) {
                    try {
                        this.wait(); // spurious wakeup ok
                    } catch (InterruptedException ignored) {
                        Thread.currentThread().interrupt();
                        // Just return null, let it fail
                        return null;
                    }
                }
                return this.serializer;
            }
        }
    }

    /**
     * A distribution message that alerts other members of the distributed cache of a new
     * {@code DataSerializer} being registered.
     */
    public static class RegistrationMessage extends SerialDistributionMessage {
        /**
         * The id of the {@code DataSerializer} that was registered since 5.7 an int instead of a byte
         */
        private int id;

        /**
         * The eventId of the {@code DataSerializer} that was registered
         */
        protected EventID eventId;

        /** The name of the {@code DataSerializer} class */
        private String className;

        /** The versions in which this message was modified */
        private static final Version[] dsfidVersions = new Version[] {};

        /**
         * Constructor for {@code DataSerializable}
         */
        public RegistrationMessage() {
        }

        /**
         * Creates a new {@code RegistrationMessage} that broadcasts that the given
         * {@code DataSerializer} was registered.
         */
        public RegistrationMessage(DataSerializer s) {
            this.className = s.getClass().getName();
            this.id = s.getId();
            this.eventId = (EventID) s.getEventId();
        }

        static String getFullMessage(Throwable t) {
            StringBuffer sb = new StringBuffer();
            getFullMessage(sb, t);
            return sb.toString();
        }

        private static void getFullMessage(StringBuffer sb, Throwable t) {
            if (t.getMessage() != null) {
                sb.append(t.getMessage());
            } else {
                sb.append(t.getClass());
            }
            if (t.getCause() != null) {
                sb.append(" caused by: ");
                getFullMessage(sb, t.getCause());
            }
        }

        @Override
        protected void process(DistributionManager dm) {
            if (CacheClientNotifier.getInstance() != null) {
                // This is a server so we need to send the dataserializer to clients
                // right away. For that we need to load the class as the constructor of
                // ClientDataSerializerMessage requires list of supported classes.
                Class<?> c;
                try {
                    c = getCachedClass(this.className); // fix for bug 41206
                } catch (ClassNotFoundException ex) {
                    // fixes bug 44112
                    logger.warn(
                            "Could not load data serializer class {} so both clients of this server and this server will not have this data serializer. Load failed because: {}",
                            this.className, getFullMessage(ex));
                    return;
                }
                DataSerializer s;
                try {
                    s = newInstance(c);
                } catch (IllegalArgumentException ex) {
                    // fixes bug 44112
                    logger.warn(
                            "Could not create an instance of data serializer for class {} so both clients of this server and this server will not have this data serializer. Create failed because: {}",
                            this.className, getFullMessage(ex));
                    return;
                }
                s.setEventId(this.eventId);
                try {
                    InternalDataSerializer._register(s, false);
                } catch (IllegalArgumentException ex) {
                    logger.warn(
                            "Could not register data serializer for class {} so both clients of this server and this server will not have this data serializer. Registration failed because: {}",
                            this.className, getFullMessage(ex));
                } catch (IllegalStateException ex) {
                    logger.warn(
                            "Could not register data serializer for class {} so both clients of this server and this server will not have this data serializer. Registration failed because: {}",
                            this.className, getFullMessage(ex));
                }
            } else {
                try {
                    InternalDataSerializer.register(this.className, false, this.eventId, null, this.id);
                } catch (IllegalArgumentException ex) {
                    logger.warn(
                            "Could not register data serializer for class {} so it will not be available in this JVM. Registration failed because: {}",
                            this.className, getFullMessage(ex));
                } catch (IllegalStateException ex) {
                    logger.warn(
                            "Could not register data serializer for class {} so it will not be available in this JVM. Registration failed because: {}",
                            this.className, getFullMessage(ex));
                }
            }
        }

        @Override
        public int getDSFID() {
            return IDS_REGISTRATION_MESSAGE;
        }

        @Override
        public void toData(DataOutput out) throws IOException {
            super.toData(out);
            DataSerializer.writeNonPrimitiveClassName(this.className, out);
            out.writeInt(this.id);
            DataSerializer.writeObject(this.eventId, out);
        }

        @Override
        public void fromData(DataInput in) throws IOException, ClassNotFoundException {
            super.fromData(in);
            InternalDataSerializer.checkIn(in);
            this.className = DataSerializer.readNonPrimitiveClassName(in);
            this.id = in.readInt();
            this.eventId = (EventID) DataSerializer.readObject(in);
        }

        @Override
        public String toString() {
            return LocalizedStrings.InternalDataSerializer_REGISTER_DATASERIALIZER_0_OF_CLASS_1
                    .toLocalizedString(this.id, this.className);
        }

        @Override
        public Version[] getSerializationVersions() {
            return dsfidVersions;
        }
    }

    /**
     * A listener whose listener methods are invoked when {@link DataSerializer}s and
     * {@link Instantiator}s are registered. This is part of the fix for bug 31422.
     *
     * @see InternalDataSerializer#addRegistrationListener
     * @see InternalDataSerializer#removeRegistrationListener
     */
    public interface RegistrationListener {

        /**
         * Invoked when a new {@code Instantiator} is {@linkplain Instantiator#register(Instantiator)
         * registered}.
         */
        void newInstantiator(Instantiator instantiator);

        /**
         * Invoked when a new {@code DataSerializer} is {@linkplain DataSerializer#register(Class)
         * registered}.
         */
        void newDataSerializer(DataSerializer ds);
    }

    /**
     * An {@code ObjectInputStream} whose {@link #resolveClass} method loads classes from the current
     * context class loader.
     */
    private static class DSObjectInputStream extends ObjectInputStream {

        /**
         * Creates a new {@code DSObjectInputStream} that delegates its behavior to a given
         * {@code InputStream}.
         */
        DSObjectInputStream(InputStream stream) throws IOException {
            super(stream);
        }

        @Override
        protected Class resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {

            String className = desc.getName();
            OldClientSupportService svc = getOldClientSupportService();
            if (svc != null) {
                className = svc.processIncomingClassName(className);
            }
            try {
                return getCachedClass(className);
            } catch (ClassNotFoundException ignored) {
                return super.resolveClass(desc);
            }
        }

        @Override
        protected Class resolveProxyClass(String[] interfaces) throws IOException, ClassNotFoundException {

            ClassLoader nonPublicLoader = null;
            boolean hasNonPublicInterface = false;

            // define proxy in class loader of non-public
            // interface(s), if any
            Class[] classObjs = new Class[interfaces.length];
            for (int i = 0; i < interfaces.length; i++) {
                Class cl = getCachedClass(interfaces[i]);
                if ((cl.getModifiers() & Modifier.PUBLIC) == 0) {
                    if (hasNonPublicInterface) {
                        if (nonPublicLoader != cl.getClassLoader()) {
                            String s = "conflicting non-public interface class loaders";
                            throw new IllegalAccessError(s);
                        }

                    } else {
                        nonPublicLoader = cl.getClassLoader();
                        hasNonPublicInterface = true;
                    }
                }
                classObjs[i] = cl;
            }

            try {
                if (hasNonPublicInterface) {
                    return Proxy.getProxyClass(nonPublicLoader, classObjs);
                } else {
                    return ClassPathLoader.getLatest().getProxyClass(classObjs);
                }
            } catch (IllegalArgumentException e) {
                throw new ClassNotFoundException(null, e);
            }
        }
    }

    /**
     * Used to implement serialization code for the well known classes we support in DataSerializer.
     * 
     * @since GemFire 5.7
     */
    protected abstract static class WellKnownDS extends DataSerializer {
        @Override
        public int getId() {
            // illegal for a customer to use but since our WellKnownDS is never registered
            // with this id it gives us one to use
            return 0;
        }

        @Override
        public Class[] getSupportedClasses() {
            // illegal for a customer to return null but we can do it since we never register
            // this serializer.
            return null;
        }

        @Override
        public Object fromData(DataInput in) throws IOException, ClassNotFoundException {
            throw new IllegalStateException(LocalizedStrings.SHOULDNT_INVOKE.toLocalizedString());
        }
        // subclasses need to implement toData
    }

    /**
     * Just like a WellKnownDS but its type is compatible with PDX.
     *
     */
    protected abstract static class WellKnownPdxDS extends WellKnownDS {
        // subclasses need to implement toData
    }

    public static void writeObjectArray(Object[] array, DataOutput out, boolean ensureCompatibility)
            throws IOException {
        InternalDataSerializer.checkOut(out);
        int length;
        if (array == null) {
            length = -1;
        } else {
            length = array.length;
        }
        InternalDataSerializer.writeArrayLength(length, out);
        if (logger.isTraceEnabled(LogMarker.SERIALIZER)) {
            logger.trace(LogMarker.SERIALIZER, "Writing Object array of length {}", length);
        }
        if (length >= 0) {
            writeClass(array.getClass().getComponentType(), out);
            for (int i = 0; i < length; i++) {
                basicWriteObject(array[i], out, ensureCompatibility);
            }
        }
    }

    // Variable Length long encoded as int in next 4 bytes
    private static final byte INT_VL = 126;

    // Variable Length long encoded as long in next 8 bytes
    private static final byte LONG_VL = 127;

    private static final int MAX_BYTE_VL = 125;

    /**
     * Write a variable length long the old way (pre 7.0). Use this only in contexts where you might
     * need to communicate with pre 7.0 members or files.
     */
    public static void writeVLOld(long data, DataOutput out) throws IOException {
        if (data < 0) {
            Assert.fail("Data expected to be >=0 is " + data);
        }
        if (data <= MAX_BYTE_VL) {
            out.writeByte((byte) data);
        } else if (data <= 0x7FFF) {
            // set the sign bit to indicate a short
            out.write(((int) data >>> 8 | 0x80) & 0xFF);
            out.write((int) data >>> 0 & 0xFF);
        } else if (data <= Integer.MAX_VALUE) {
            out.writeByte(INT_VL);
            out.writeInt((int) data);
        } else {
            out.writeByte(LONG_VL);
            out.writeLong(data);
        }
    }

    /**
     * Write a variable length long the old way (pre 7.0). Use this only in contexts where you might
     * need to communicate with pre 7.0 members or files.
     */
    public static long readVLOld(DataInput in) throws IOException {
        byte code = in.readByte();
        long result;
        if (code < 0) {
            // mask off sign bit
            result = code & 0x7F;
            result <<= 8;
            result |= in.readByte() & 0xFF;
        } else if (code <= MAX_BYTE_VL) {
            result = code;
        } else if (code == INT_VL) {
            result = in.readInt();
        } else if (code == LONG_VL) {
            result = in.readLong();
        } else {
            throw new IllegalStateException("unexpected variable length code=" + code);
        }
        return result;
    }

    /**
     * Encode a long as a variable length array.
     * 
     * This method is appropriate for unsigned integers. For signed integers, negative values will
     * always consume 10 bytes, so it is recommended to use writeSignedVL instead.
     * 
     * This is taken from the varint encoding in protobufs (BSD licensed). See
     * https://developers.google.com/protocol-buffers/docs/encoding
     */
    public static void writeUnsignedVL(long data, DataOutput out) throws IOException {
        while (true) {
            if ((data & ~0x7FL) == 0) {
                out.writeByte((int) data);
                return;
            } else {
                out.writeByte((int) data & 0x7F | 0x80);
                data >>>= 7;
            }
        }
    }

    /**
     * Decode a long as a variable length array.
     * 
     * This is taken from the varint encoding in protobufs (BSD licensed). See
     * https://developers.google.com/protocol-buffers/docs/encoding
     */
    public static long readUnsignedVL(DataInput in) throws IOException {
        int shift = 0;
        long result = 0;
        while (shift < 64) {
            final byte b = in.readByte();
            result |= (long) (b & 0x7F) << shift;
            if ((b & 0x80) == 0) {
                return result;
            }
            shift += 7;
        }
        throw new GemFireIOException("Malformed variable length integer");
    }

    /**
     * Encode a signed long as a variable length array.
     * 
     * This method is appropriate for signed integers. It uses zig zag encoding to so that negative
     * numbers will be represented more compactly. For unsigned values, writeUnsignedVL will be more
     * efficient.
     */
    public static void writeSignedVL(long data, DataOutput out) throws IOException {
        writeUnsignedVL(encodeZigZag64(data), out);
    }

    /**
     * Decode a signed long as a variable length array.
     * 
     * This method is appropriate for signed integers. It uses zig zag encoding to so that negative
     * numbers will be represented more compactly. For unsigned values, writeUnsignedVL will be more
     * efficient.
     */
    public static long readSignedVL(DataInput in) throws IOException {
        return decodeZigZag64(readUnsignedVL(in));
    }

    /**
     * Decode a ZigZag-encoded 64-bit value. ZigZag encodes signed integers into values that can be
     * efficiently encoded with varint. (Otherwise, negative values must be sign-extended to 64 bits
     * to be varint encoded, thus always taking 10 bytes on the wire.)
     *
     * @param n An unsigned 64-bit integer, stored in a signed int because Java has no explicit
     *        unsigned support.
     * @return A signed 64-bit integer.
     * 
     *         This is taken from the varint encoding in protobufs (BSD licensed). See
     *         https://developers.google.com/protocol-buffers/docs/encoding
     */
    private static long decodeZigZag64(final long n) {
        return n >>> 1 ^ -(n & 1);
    }

    /**
     * Encode a ZigZag-encoded 64-bit value. ZigZag encodes signed integers into values that can be
     * efficiently encoded with varint. (Otherwise, negative values must be sign-extended to 64 bits
     * to be varint encoded, thus always taking 10 bytes on the wire.)
     *
     * @param n A signed 64-bit integer.
     * @return An unsigned 64-bit integer, stored in a signed int because Java has no explicit
     *         unsigned support.
     * 
     *         This is taken from the varint encoding in protobufs (BSD licensed). See
     *         https://developers.google.com/protocol-buffers/docs/encoding
     */
    private static long encodeZigZag64(final long n) {
        // Note: the right-shift must be arithmetic
        return n << 1 ^ n >> 63;
    }

    /* test only method */
    public static int calculateBytesForTSandDSID(int dsid) {
        HeapDataOutputStream out = new HeapDataOutputStream(4 + 8, Version.CURRENT);
        long now = System.currentTimeMillis();
        try {
            writeUnsignedVL(now, out);
            writeUnsignedVL(InternalDataSerializer.encodeZigZag64(dsid), out);
        } catch (IOException ignored) {
            return 0;
        }
        return out.size();
    }

    public static final boolean LOAD_CLASS_EACH_TIME = Boolean
            .getBoolean(DistributionConfig.GEMFIRE_PREFIX + "loadClassOnEveryDeserialization");

    private static final CopyOnWriteHashMap<String, WeakReference<Class<?>>> classCache = LOAD_CLASS_EACH_TIME
            ? null
            : new CopyOnWriteHashMap<>();

    private static final Object cacheAccessLock = new Object();

    public static Class<?> getCachedClass(String p_className) throws ClassNotFoundException {
        String className = processIncomingClassName(p_className);
        if (LOAD_CLASS_EACH_TIME) {
            return ClassPathLoader.getLatest().forName(className);
        } else {
            Class<?> result = getExistingCachedClass(className);
            if (result == null) {
                // Do the forName call outside the sync to fix bug 46172
                result = ClassPathLoader.getLatest().forName(className);
                synchronized (cacheAccessLock) {
                    Class<?> cachedClass = getExistingCachedClass(className);
                    if (cachedClass == null) {
                        classCache.put(className, new WeakReference<>(result));
                    } else {
                        result = cachedClass;
                    }
                }
            }
            return result;
        }
    }

    private static Class<?> getExistingCachedClass(String className) {
        WeakReference<Class<?>> wr = classCache.get(className);
        Class<?> result = null;
        if (wr != null) {
            result = wr.get();
        }
        return result;
    }

    public static void flushClassCache() {
        if (classCache != null) {
            // Not locking classCache during clear as doing so causes a deadlock in the DeployedJar
            classCache.clear();
        }
    }
}