| /*========================================================================= |
| * Copyright (c) 2010-2014 Pivotal Software, Inc. All Rights Reserved. |
| * This product is protected by U.S. and international copyright |
| * and intellectual property laws. Pivotal products are covered by |
| * one or more patents listed at http://www.pivotal.io/patents. |
| *========================================================================= |
| */ |
| package com.gemstone.gemfire.internal; |
| |
| 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; |
| |
| import org.apache.logging.log4j.Logger; |
| |
| import com.gemstone.gemfire.CancelException; |
| import com.gemstone.gemfire.CanonicalInstantiator; |
| import com.gemstone.gemfire.DataSerializable; |
| import com.gemstone.gemfire.DataSerializer; |
| import com.gemstone.gemfire.GemFireIOException; |
| import com.gemstone.gemfire.GemFireRethrowable; |
| import com.gemstone.gemfire.Instantiator; |
| import com.gemstone.gemfire.InternalGemFireError; |
| import com.gemstone.gemfire.InternalGemFireException; |
| import com.gemstone.gemfire.SerializationException; |
| import com.gemstone.gemfire.SystemFailure; |
| import com.gemstone.gemfire.ToDataException; |
| import com.gemstone.gemfire.cache.Cache; |
| import com.gemstone.gemfire.cache.CacheClosedException; |
| import com.gemstone.gemfire.cache.execute.Function; |
| import com.gemstone.gemfire.distributed.internal.DMStats; |
| import com.gemstone.gemfire.distributed.internal.DistributionManager; |
| import com.gemstone.gemfire.distributed.internal.InternalDistributedSystem; |
| import com.gemstone.gemfire.distributed.internal.LonerDistributionManager; |
| import com.gemstone.gemfire.distributed.internal.PooledDistributionMessage; |
| import com.gemstone.gemfire.internal.cache.EnumListenerEvent; |
| import com.gemstone.gemfire.internal.cache.EventID; |
| import com.gemstone.gemfire.internal.cache.GemFireCacheImpl; |
| import com.gemstone.gemfire.internal.cache.PoolManagerImpl; |
| import com.gemstone.gemfire.internal.cache.tier.sockets.CacheClientNotifier; |
| import com.gemstone.gemfire.internal.cache.tier.sockets.CacheServerHelper; |
| import com.gemstone.gemfire.internal.cache.tier.sockets.ClientDataSerializerMessage; |
| import com.gemstone.gemfire.internal.cache.tier.sockets.ClientProxyMembershipID; |
| import com.gemstone.gemfire.internal.cache.tier.sockets.Part; |
| import com.gemstone.gemfire.internal.i18n.LocalizedStrings; |
| import com.gemstone.gemfire.internal.logging.LogService; |
| import com.gemstone.gemfire.internal.logging.log4j.LocalizedMessage; |
| import com.gemstone.gemfire.internal.logging.log4j.LogMarker; |
| import com.gemstone.gemfire.internal.util.concurrent.CopyOnWriteHashMap; |
| import com.gemstone.gemfire.pdx.NonPortableClassException; |
| import com.gemstone.gemfire.pdx.PdxInstance; |
| import com.gemstone.gemfire.pdx.PdxSerializable; |
| import com.gemstone.gemfire.pdx.PdxSerializer; |
| import com.gemstone.gemfire.pdx.internal.AutoSerializableManager; |
| import com.gemstone.gemfire.pdx.internal.AutoSerializableManager.AutoClassInfo; |
| import com.gemstone.gemfire.pdx.internal.EnumInfo; |
| import com.gemstone.gemfire.pdx.internal.PdxInputStream; |
| import com.gemstone.gemfire.pdx.internal.PdxInstanceEnum; |
| import com.gemstone.gemfire.pdx.internal.PdxInstanceImpl; |
| import com.gemstone.gemfire.pdx.internal.PdxOutputStream; |
| import com.gemstone.gemfire.pdx.internal.PdxReaderImpl; |
| import com.gemstone.gemfire.pdx.internal.PdxType; |
| import com.gemstone.gemfire.pdx.internal.PdxWriterImpl; |
| import com.gemstone.gemfire.pdx.internal.TypeRegistry; |
| import com.gemstone.org.jgroups.util.StreamableFixedID; |
| import com.gemstone.org.jgroups.util.StringId; |
| import com.gemstone.org.jgroups.util.VersionedStreamable; |
| |
| /** |
| * 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. |
| * |
| * @author David Whitlock |
| * @since 3.5 |
| */ |
| public abstract class InternalDataSerializer extends DataSerializer implements DSCODE { |
| private static final Logger logger = LogService.getLogger(); |
| |
| private static final Set loggedClasses = new HashSet(); |
| /** |
| * Maps Class names to their DataSerializer. This is used to |
| * find a DataSerializer during serialization. |
| */ |
| private static final ConcurrentHashMap<String, DataSerializer> classesToSerializers = new ConcurrentHashMap<String, DataSerializer>(); |
| |
| // used by sqlFire |
| public static ConcurrentHashMap<String, DataSerializer> getClassesToSerializers() { |
| return classesToSerializers; |
| } |
| |
| |
| private static final String serializationVersionTxt = System.getProperty("gemfire.serializationVersion"); |
| /** |
| * Any time new serialization format is added then a new enum needs to be added here. |
| * @author darrel |
| * @since 6.6.2 |
| */ |
| private static enum SERIALIZATION_VERSION { |
| vINVALID, |
| v660, // includes 6.6.0.x and 6.6.1.x. Note that no serialization changes were made in 6.6 until 6.6.2 |
| v662 // 6.6.2.x or later |
| // NOTE if you add a new constant make sure and update "latestVersion". |
| } |
| /** |
| * 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.equals("")) { |
| 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final boolean toData(Object o, DataOutput out) |
| throws IOException { |
| out.writeByte(HASH_TABLE); |
| writeHashtable((Hashtable) o, out); |
| return true; |
| }}); |
| // We can't add this here because it would cause writeObject to not be compatible with previous releases |
| // classesToSerializers.put("java.util.concurrent.ConcurrentHashMap", |
| // new WellKnownDS() { |
| // @Override |
| // public final boolean toData(Object o, DataOutput out) |
| // throws IOException { |
| // out.writeByte(CONCURRENT_HASH_MAP); |
| // writeConcurrentHashMap((ConcurrentHashMap<?, ?>) o, out); |
| // return true; |
| // }}); |
| classesToSerializers.put("java.util.Properties", |
| new WellKnownDS() { |
| @Override |
| public final 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 final 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 final 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 final 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 final 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 final 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 final boolean toData(Object o, DataOutput out) |
| throws IOException { |
| out.writeByte(TIMESTAMP); |
| writeTimestamp((Timestamp) o, out); |
| return true; |
| }}); |
| } |
| // @todo add: LinkedHashMap (hard to do because it might not be insertion ordered) |
| } |
| |
| /** Maps the id of a serializer to its <code>DataSerializer</code>. |
| */ |
| 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<String, SerializerAttributesHolder>(); |
| |
| /** |
| * 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<Integer, SerializerAttributesHolder>(); |
| |
| /** |
| * 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<String, SerializerAttributesHolder>(); |
| |
| /** <code>RegistrationListener</code>s that receive callbacks when |
| * <code>DataSerializer</code>s and <code>Instantiator</code>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(); |
| |
| //////////////////// Static Methods //////////////////// |
| |
| /** |
| * Convert the given unsigned byte to an int. |
| * The returned value will be in the range [0..255] inclusive |
| */ |
| private static final int ubyteToInt(byte ub) { |
| return ub & 0xFF; |
| } |
| |
| /** |
| * Instantiates an instance of <code>DataSerializer</code> |
| * |
| * @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 ex) { |
| 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 ex) { |
| throw new IllegalArgumentException(LocalizedStrings.DataSerializer_COULD_NOT_INSTANTIATE_AN_INSTANCE_OF_0.toLocalizedString(c.getName())); |
| |
| } catch (InstantiationException ex) { |
| RuntimeException ex2 = new IllegalArgumentException(LocalizedStrings.DataSerializer_COULD_NOT_INSTANTIATE_AN_INSTANCE_OF_0.toLocalizedString(c.getName())); |
| ex2.initCause(ex); |
| throw ex2; |
| |
| } catch (InvocationTargetException ex) { |
| RuntimeException ex2 = new IllegalArgumentException(LocalizedStrings.DataSerializer_WHILE_INSTANTIATING_AN_INSTANCE_OF_0.toLocalizedString(c.getName())); |
| ex2.initCause(ex); |
| throw ex2; |
| } |
| |
| 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</code> instance with the data |
| * serialization framework. |
| * |
| * @param distribute |
| * Should the registered <code>DataSerializer</code> 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 (int i = 0; i < classes.length; i++) { |
| if (classes[i] == 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 (classes[i].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 = Integer.valueOf(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(), Integer.valueOf(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 |
| GemFireCacheImpl 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 irelevent of distribute |
| // bridge servers send it all the clients irelevent of |
| // originator VM |
| sendRegistrationMessageToClients(s); |
| |
| fireNewDataSerializer(s); |
| |
| return s; |
| } |
| |
| /** |
| * Marks a <code>DataSerializer</code> 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</code> className for registration with the |
| * data serialization framework. Does not necessarily load the classes into |
| * this VM. |
| * |
| * @param className |
| * @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 (className == null || className.trim().equals("")) { |
| 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(), |
| Integer.valueOf(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; |
| |
| public SerializerAttributesHolder () { |
| } |
| |
| public 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; |
| } |
| |
| 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) |
| { |
| Cache 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 e) { |
| 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(){ |
| GemFireCacheImpl cache = GemFireCacheImpl.getInstance(); |
| if(cache == null){ |
| // A cache has not yet created |
| return null; |
| } |
| return new EventID(InternalDistributedSystem.getAnyInstance()); |
| } |
| /** |
| * Unregisters a <code>Serializer</code> that was previously |
| * registered with the data serialization framework. |
| */ |
| public static void unregister(int id) { |
| final Integer idx = Integer.valueOf(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 (int i = 0; i < classes.length; i++) { |
| classesToSerializers.remove(classes[i].getName(), s); |
| supportedClassesToHolders.remove(classes[i].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</code> for the given class. If |
| * no class has been registered, <code>null</code> is returned. |
| * Remember that it is okay to return <code>null</code> in this |
| * case. This method is invoked when writing an object. If a |
| * serializer isn't available, then its the user's fault. |
| */ |
| public 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 cnfe) { |
| logger.info(LogMarker.SERIALIZER, LocalizedMessage.create(LocalizedStrings.InternalDataSerializer_COULD_NOT_LOAD_DATASERIALIZER_CLASS_0, dsClass)); |
| } |
| } |
| } |
| return ds; |
| } |
| |
| /** |
| * Returns the <code>DataSerializer</code> with the given id. |
| */ |
| public static DataSerializer getSerializer(int id) { |
| final Integer idx = Integer.valueOf(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) { |
| //SerializerAttributesHolder sah = idsToHolders.get(idx); |
| 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 cnfe) { |
| 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); |
| Iterator it = idsToSerializers.values().iterator(); |
| while (it.hasNext()) { |
| Object v = it.next(); |
| 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 = null; |
| 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 cnfe) { |
| 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<InternalDataSerializer.SerializerAttributesHolder>( |
| size); |
| |
| Iterator it = idsToSerializers.values().iterator(); |
| while (it.hasNext()) { |
| Object v = it.next(); |
| 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())); |
| } |
| } |
| |
| Iterator<Entry<String, SerializerAttributesHolder>> iterator = dsClassesToHolders |
| .entrySet().iterator(); |
| while (iterator.hasNext()) { |
| SerializerAttributesHolder v = iterator.next().getValue(); |
| coll.add(v); |
| } |
| |
| return coll.toArray(new SerializerAttributesHolder[coll.size()]); |
| } |
| |
| /** |
| * Persist this class's map to out |
| */ |
| public static void saveRegistrations(DataOutput out) throws IOException { |
| Iterator it = idsToSerializers.values().iterator(); |
| while (it.hasNext()) { |
| Object v = it.next(); |
| 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 cnfe) { |
| 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. |
| * @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 ex) { |
| skip = true; |
| } |
| if (skip) { |
| continue; |
| } |
| register(dsClass, /*dsId,*/ true); |
| } |
| } |
| |
| /** |
| * Adds a <code>RegistrationListener</code> that will receive |
| * callbacks when <code>DataSerializer</code>s and |
| * <code>Instantiator</code>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</code> 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</code>s that a new |
| * <code>DataSerializer</code> has been registered |
| * |
| * @see InternalDataSerializer.RegistrationListener#newDataSerializer |
| */ |
| private static void fireNewDataSerializer(DataSerializer ds) { |
| Iterator iter = listeners.iterator(); |
| while (iter.hasNext()) { |
| RegistrationListener listener = (RegistrationListener) iter.next(); |
| listener.newDataSerializer(ds); |
| } |
| } |
| |
| /** |
| * Alerts all <code>RegistrationListener</code>s that a new |
| * <code>Instantiator</code> has been registered |
| * |
| * @see InternalDataSerializer.RegistrationListener#newInstantiator |
| */ |
| static void fireNewInstantiator(Instantiator instantiator) { |
| Iterator iter = listeners.iterator(); |
| while (iter.hasNext()) { |
| RegistrationListener listener = (RegistrationListener) iter.next(); |
| 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); |
| } |
| } |
| |
| ///////////////// START DataSerializer Implementation Methods /////////// |
| |
| public static final void writeDSFID(DataSerializableFixedID o, DataOutput out) |
| throws IOException |
| { |
| int dsfid = o.getDSFID(); |
| if (dsfid == DataSerializableFixedID.ILLEGAL) { |
| throw new IllegalStateException(LocalizedStrings.InternalDataSerializer_ATTEMPTED_TO_SERIALIZE_ILLEGAL_DSFID.toLocalizedString()); |
| } |
| if (dsfidToClassMap != null && logger.isTraceEnabled(LogMarker.DEBUG_DSFID)) { |
| logger.trace(LogMarker.DEBUG_DSFID, "writeDSFID {} class={}", dsfid, o.getClass()); |
| if (dsfid != DataSerializableFixedID.NO_FIXED_ID) { |
| // 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(Integer.valueOf(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 <= 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 if (dsfid == DataSerializableFixedID.NO_FIXED_ID) { |
| out.writeByte(DS_NO_FIXED_ID); |
| DataSerializer.writeClass(o.getClass(), out); |
| } else { |
| out.writeByte(DS_FIXED_ID_INT); |
| out.writeInt(dsfid); |
| } |
| try { |
| invokeToData(o, out); |
| } catch (IOException io) { |
| // Note: this is not a user code toData but one from our |
| // internal code since only GemFire product code implements DSFID |
| throw io; |
| } catch (CancelException ex) { |
| //Serializing a PDX can result in a cache closed exception. Just rethrow |
| throw ex; |
| } catch (ToDataException ex) { |
| throw ex; |
| } catch (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("toData failed on dsfid=" + dsfid+" msg:"+t.getMessage(), t); |
| } |
| } |
| |
| public static final void writeStreamableFixedID(StreamableFixedID o, DataOutput out) |
| throws IOException |
| { |
| int dsfid = o.getDSFID(); |
| if (dsfid == DataSerializableFixedID.ILLEGAL) { |
| throw new IllegalStateException(LocalizedStrings.InternalDataSerializer_ATTEMPTED_TO_SERIALIZE_ILLEGAL_DSFID.toLocalizedString()); |
| } |
| if (dsfidToClassMap != null && logger.isTraceEnabled(LogMarker.DEBUG_DSFID)) { |
| logger.trace(LogMarker.DEBUG_DSFID, "writeDSFID {} class={}", dsfid, o.getClass()); |
| if (dsfid != DataSerializableFixedID.NO_FIXED_ID) { |
| // 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(Integer.valueOf(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 <= 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 if (dsfid == DataSerializableFixedID.NO_FIXED_ID) { |
| out.writeByte(DS_NO_FIXED_ID); |
| DataSerializer.writeClass(o.getClass(), out); |
| } else { |
| out.writeByte(DS_FIXED_ID_INT); |
| out.writeInt(dsfid); |
| } |
| try { |
| invokeToData(o, out); |
| } catch (IOException io) { |
| // Note: this is not a user code toData but one from our |
| // internal code since only GemFire product code implements DSFID |
| throw io; |
| } catch (CancelException ex) { |
| //Serializing a PDX can result in a cache closed exception. Just rethrow |
| throw ex; |
| } catch (ToDataException ex) { |
| throw ex; |
| } catch (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("toData failed on dsfid=" + dsfid+" msg:"+t.getMessage(), t); |
| } |
| } |
| |
| /** |
| * Data serializes an instance of a well-known class to the given |
| * <code>DataOutput</code>. |
| * |
| * @return <code>true</code> if <code>o</code> was actually |
| * written to <code>out</code> |
| */ |
| public 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</code>) |
| * to the given <code>DataOutput</code>. |
| * |
| * @return <code>true</code> if <code>o</code> was written to |
| * <code>out</code>. |
| */ |
| 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 = false; |
| 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 (ToDataException ex) { |
| throw ex; |
| } catch (CancelException ex) { |
| //Serializing a PDX can result in a cache closed exception. Just rethrow |
| throw ex; |
| } catch (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("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(new Object[]{Integer.valueOf(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 { |
| // TODO once .NET is enhanced to support inline enums then it should be compatible. |
| checkPdxCompatible(o, ensurePdxCompatibility); |
| writeGemFireEnum((Enum<?>)o, out); |
| } |
| return true; |
| } else { |
| PdxSerializer pdxSerializer = TypeRegistry.getPdxSerializer(); |
| if (pdxSerializer != null) { |
| return writePdx(out, null, o, pdxSerializer); |
| } |
| // DSDataOutput out2 = new DSDataOutput(out); |
| |
| // for (int i = 0; i < serializers.length; i++) { |
| // final DataSerializer myserializer = |
| // (DataSerializer) serializers[i]; |
| // out2.setSerializerId(myserializer.getId()); |
| // if (myserializer.toData(o, out2)) { |
| // if (!out2.hasWritten()) { |
| // String s = "Serializer " + serializer + " serialized a " + |
| // o.getClass().getName() + ", but it did not write " + |
| // "any data"; |
| // throw new IOException(s); |
| // } |
| // return true; |
| |
| // } else { |
| // if (out2.hasWritten()) { |
| // String s = "Serializer " + myserializer + |
| // " did not serialize a " + o.getClass().getName() + |
| // ", but it wrote data"; |
| // throw new IOException(s); |
| // } |
| // } |
| // } |
| return false; |
| } |
| } |
| |
| |
| 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) { |
| GemFireCacheImpl gfc = GemFireCacheImpl.getForPdx("PDX registry is unavailable because the Cache has been closed."); |
| TypeRegistry tr = gfc.getPdxRegistry(); |
| |
| PdxWriterImpl writer; |
| { |
| PdxOutputStream os; |
| if (out instanceof HeapDataOutputStream) { |
| os = new PdxOutputStream((HeapDataOutputStream) out); |
| } else { |
| os = new PdxOutputStream(); |
| } |
| 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 ex) { |
| throw ex; |
| } catch (CancelException ex) { |
| //Serializing a PDX can result in a cache closed exception. Just rethrow |
| throw ex; |
| } catch (NonPortableClassException ex) { |
| throw ex; |
| } catch (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(gfc).incPdxSerialization(bytesWritten); |
| if (!(out instanceof HeapDataOutputStream)) { |
| writer.sendTo(out); |
| } |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| public 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. |
| * |
| * TODO - this is a hack. We should have different flavor of |
| * write object for user objects that calls the serializer. Other |
| * kinds of write object shouldn't even get to the pdx serializer. |
| */ |
| private static boolean isGemfireObject(Object o) { |
| return ((o instanceof Function) // fixes 43691 |
| || o.getClass().getName().startsWith("com.gemstone.")) |
| && !(o instanceof PdxSerializerObject); |
| } |
| |
| /** |
| * Reads an object that was serialized by a customer ("user") |
| * <code>DataSerializer</code> from the given <code>DataInput</code>. |
| * |
| * @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[] { Integer.valueOf(serializerId) })); |
| } |
| |
| return serializer.fromData(in); |
| } |
| |
| /** |
| * Checks to make sure a <code>DataOutput</code> is not |
| * <code>null</code>. |
| * |
| * @throws NullPointerException |
| * If <code>out</code> is <code>null</code> |
| */ |
| public static void checkOut(DataOutput out) { |
| if (out == null) { |
| String s = "Null DataOutput"; |
| throw new NullPointerException(s); |
| } |
| } |
| /** |
| * Checks to make sure a <code>DataInput</code> is not |
| * <code>null</code>. |
| * |
| * @throws NullPointerException |
| * If <code>in</code> is <code>null</code> |
| */ |
| public static void checkIn(DataInput in) { |
| if (in == null) { |
| String s = "Null DataInput"; |
| throw new NullPointerException(s); |
| } |
| } |
| |
| |
| |
| /** |
| * Writes a <code>Set</code> to a <code>DataOutput</code>. |
| * |
| * <P> |
| * |
| * This method is internal because its semantics (that is, its |
| * ability to write any kind of <code>Set</code>) are different from |
| * the <code>write</code>XXX methods of the external |
| * <code>DataSerializer</code>. |
| * |
| * @throws IOException |
| * A problem occurs while writing to <code>out</code> |
| * |
| * @see #readSet |
| * |
| * @since 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</code> from a <code>DataInput</code>. |
| * |
| * @throws IOException |
| * A problem occurs while writing to <code>out</code> |
| * @throws ClassNotFoundException |
| * The class of one of the <Code>HashSet</code>'s |
| * elements cannot be found. |
| * |
| * @see #writeSet |
| * |
| * @since 4.0 |
| */ |
| public static Set readSet(DataInput in) |
| throws IOException, ClassNotFoundException { |
| return readHashSet(in); |
| } |
| |
| /** |
| * Reads a <code>Set</code> from a <code>DataInput</code> into the given |
| * non-null collection. Returns true if collection read is non-null else |
| * returns false. |
| * |
| * @throws IOException |
| * A problem occurs while reading from <code>in</code> |
| * @throws ClassNotFoundException |
| * The class of one of the <Code>Set</code>'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) { |
| E element; |
| for (int index = 0; index < size; ++index) { |
| element = DataSerializer.<E> 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 (Iterator it=set.iterator(); it.hasNext(); ) { |
| Long l = (Long)it.next(); |
| if (hasLongIDs) { |
| out.writeLong(l.longValue()); |
| } 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(Long.valueOf(l)); |
| } |
| return result; |
| } |
| } |
| |
| /** |
| * write a set of Long objects |
| * @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 (Iterator it=list.iterator(); it.hasNext(); ) { |
| Long l = (Long)it.next(); |
| if (hasLongIDs) { |
| out.writeLong(l.longValue()); |
| } 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(Long.valueOf(l)); |
| } |
| return result; |
| } |
| } |
| |
| |
| |
| /** |
| * Writes the type code for a primitive type Class |
| * to <code>DataOutput</code>. |
| */ |
| public static final 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 final 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(Byte.valueOf(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</code> from a <code>DataInput</code>. |
| * |
| * @throws IOException |
| * A problem occurs while writing to <code>out</code> |
| * |
| * @see #writeTimeUnit |
| */ |
| public 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(Byte.valueOf(type))); |
| } |
| |
| if (logger.isTraceEnabled(LogMarker.SERIALIZER)) { |
| logger.trace(LogMarker.SERIALIZER, "Read TimeUnit: {}", unit); |
| } |
| |
| return unit; |
| } |
| |
| public 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); |
| } |
| public 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; |
| } |
| |
| public 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); |
| } |
| public 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; |
| } |
| |
| public 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); |
| } |
| public 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; |
| } |
| |
| public 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); |
| } |
| public 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 HashSet seenClassNames = DEBUG_DSFID ? new HashSet(): null; |
| private static final ConcurrentMap dsfidToClassMap = logger.isTraceEnabled(LogMarker.DEBUG_DSFID) ? new ConcurrentHashMap(): null; |
| |
| public static final 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</code>s to a |
| * <code>DataOutput</code>. |
| * |
| * @throws IOException |
| * A problem occurs while writing to <code>out</code> |
| * |
| * @see DataSerializer#readCharArray |
| * @since 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 final boolean isSerializedNull(byte[] serializedForm) { |
| return serializedForm.length == 1 && serializedForm[0] == NULL; |
| } |
| |
| public static final 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 (o instanceof StreamableFixedID) { |
| if (isDebugEnabled_SERIALIZER) { |
| logger.trace(LogMarker.SERIALIZER, "Writing JGroups StreamableFixedID: {}", o); |
| } |
| StreamableFixedID sf = (StreamableFixedID)o; |
| writeStreamableFixedID(sf, out); |
| } else if (o instanceof VersionedStreamable) { |
| if (isDebugEnabled_SERIALIZER) { |
| logger.trace(LogMarker.SERIALIZER, "Writing JGroups VersionedStreamable: {}", o); |
| } |
| VersionedStreamable vs = (VersionedStreamable)o; |
| Class c = o.getClass(); |
| out.writeByte(DATA_SERIALIZABLE); |
| DataSerializer.writeClass(c, out); |
| invokeToData(vs, 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); |
| // @todo darrel: make a subinterface of DataSerializable |
| // named InstantiatedDataSerializable |
| // which adds one method which returns the instantiator code. |
| // This would allow the serialization side to not need a map lookup (instead they just call the method) |
| // which also helps the DataSerializable case to no longer do the lookup. |
| // We could also use it to get rid of the need for static register calls |
| // but that would mean that when we find one of these that we check to see |
| // if that class had been registered and do so if not. |
| // So from the customer's point of view this would be easier; just implement |
| // a method that returns an int. |
| |
| 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); |
| // if (DEBUG_DSFID) { |
| // if (logger.infoEnabled()) { |
| // boolean alreadySeen; |
| // synchronized (seenClassNames) { |
| // alreadySeen = seenClassNames.add(c.getName()); |
| // } |
| // if (alreadySeen) { |
| // // this class should be made a DSFID if it is a product class |
| // logger.info("DataSerialized class " + c.getName(), new RuntimeException("CALLSTACK")); |
| // } |
| // } |
| // } |
| 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())); |
| } |
| |
| // if (out instanceof DSDataOutput) { |
| // // Unwrap the DSDataOutput to avoid one layer of |
| // // delegation. This also prevents us from having to flush |
| // // the ObjectOutputStream. |
| // out = ((DSDataOutput) out).out; |
| // } |
| writeSerializableObject(o, out); |
| } |
| } |
| |
| private static boolean disallowJavaSerialization() { |
| Boolean v = DISALLOW_JAVA_SERIALIZATION.get(); |
| return v != null && v; |
| } |
| |
| /** |
| * @throws IOException |
| * @since 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); |
| } |
| GemFireCacheImpl gfc = GemFireCacheImpl.getForPdx("PDX registry is unavailable because the Cache has been closed."); |
| TypeRegistry tr = gfc.getPdxRegistry(); |
| |
| Object result = tr.getEnumById(enumId); |
| if (result instanceof PdxInstance) { |
| getDMStats(gfc).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 { |
| GemFireCacheImpl gfc = GemFireCacheImpl.getInstance(); |
| if (gfc != null && gfc.getPdxReadSerializedByAnyGemFireServices()) { |
| String className = DataSerializer.readString(in); |
| String enumName = DataSerializer.readString(in); |
| int enumOrdinal = InternalDataSerializer.readArrayLength(in); |
| getDMStats(gfc).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 |
| * @throws IOException |
| */ |
| public static final 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); |
| } |
| |
| // public void write(byte[] b) throws IOException { |
| // out.write(b); |
| // } |
| |
| // public void write(byte[] b, int off, int len) |
| // throws IOException { |
| // out.write(b, off, len); |
| // } |
| }; |
| } |
| 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 final void invokeToData(Object ds, DataOutput out) throws IOException { |
| boolean isDSFID = (ds instanceof DataSerializableFixedID); |
| boolean isStreamable = (ds instanceof VersionedStreamable); |
| try { |
| boolean invoked = false; |
| Version v = InternalDataSerializer.getVersionForDataStreamOrNull(out); |
| Version[] versions = null; |
| |
| if (v != null && v != Version.CURRENT) { |
| // get versions where DataOutput was upgraded |
| 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 (int i = 0; i < versions.length; i++) { |
| // if peer version is less than the greatest upgraded version |
| if (v.compareTo(versions[i]) < 0) { |
| ds.getClass() |
| .getMethod( |
| "toDataPre_" + versions[i].getMethodSuffix(), |
| new Class[] { DataOutput.class }).invoke(ds, out); |
| invoked = true; |
| break; |
| } |
| } |
| } |
| } |
| |
| if (!invoked) { |
| if (isDSFID) { |
| ((DataSerializableFixedID)ds).toData(out); |
| } else if (isStreamable) { |
| ((VersionedStreamable)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 (ToDataException ex) { |
| throw ex; |
| } catch (CancelException ex) { |
| //Serializing a PDX can result in a cache closed exception. Just rethrow |
| throw ex; |
| } catch (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("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 final void invokeFromData(Object ds, DataInput in) throws IOException, ClassNotFoundException { |
| try { |
| boolean invoked = false; |
| Version v = InternalDataSerializer.getVersionForDataStreamOrNull(in); |
| Version[] versions = null; |
| if (v != null && v != Version.CURRENT) { |
| // get versions where DataOutput was upgraded |
| if (ds instanceof SerializationVersions) { |
| SerializationVersions vds = (SerializationVersions) ds; |
| versions = vds.getSerializationVersions(); |
| } else if (ds instanceof VersionedStreamable) { |
| VersionedStreamable vs = (VersionedStreamable)ds; |
| short[] ordinals = vs.getSerializationVersions(); |
| if (ordinals==null || ordinals.length == 0) { |
| versions = null; |
| } else { |
| versions = new Version[ordinals.length]; |
| for (int i=0; i<ordinals.length; i++) { |
| versions[i] = Version.fromOrdinalOrCurrent(ordinals[i]); |
| } |
| } |
| } |
| // 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 (int i = 0; i < versions.length; i++) { |
| // if peer version is less than the greatest upgraded version |
| if (v.compareTo(versions[i]) < 0) { |
| ds.getClass() |
| .getMethod( |
| "fromDataPre" + "_" + versions[i].getMethodSuffix(), |
| new Class[] { DataInput.class }).invoke(ds, in); |
| invoked = true; |
| break; |
| } |
| } |
| } |
| } |
| if (!invoked) { |
| if (ds instanceof DataSerializableFixedID) { |
| ((DataSerializableFixedID)ds).fromData(in); |
| } else if (ds instanceof VersionedStreamable) { |
| ((VersionedStreamable)ds).fromData(in); |
| } else { |
| ((DataSerializable)ds).fromData(in); |
| } |
| } |
| } catch (EOFException ex) { |
| // client went away - ignore |
| throw ex; |
| } catch (ClassNotFoundException ex) { |
| throw ex; |
| } catch (CacheClosedException cce) { |
| throw cce; |
| } catch (Exception ex) { |
| SerializationException ex2 = new SerializationException(LocalizedStrings.DataSerializer_COULD_NOT_CREATE_AN_INSTANCE_OF_0.toLocalizedString(ds.getClass().getName()), ex); |
| throw ex2; |
| } |
| } |
| |
| |
| private static final 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 || (o instanceof VersionedStreamable)); |
| 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) { |
| SerializationException ex2 = new SerializationException(LocalizedStrings.DataSerializer_COULD_NOT_CREATE_AN_INSTANCE_OF_0.toLocalizedString(c.getName()), ex); |
| throw ex2; |
| } |
| } |
| private static final 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) { |
| SerializationException ex2 = new SerializationException(LocalizedStrings.DataSerializer_COULD_NOT_CREATE_AN_INSTANCE_OF_0.toLocalizedString(c.getName()), ex); |
| throw ex2; |
| } |
| } |
| |
| /** |
| * Get the {@link Version} of the peer or disk store that created this |
| * {@link DataInput}. |
| */ |
| public static final 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 final 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 final 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 final 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; |
| } |
| } |
| |
| public static final byte NULL_ARRAY = -1; // array is null |
| /** |
| * @since 5.7 |
| */ |
| private static final byte SHORT_ARRAY_LEN = -2; // array len encoded as unsigned short in next 2 bytes |
| /** |
| * @since 5.7 |
| */ |
| public static final byte INT_ARRAY_LEN = -3; // array len encoded as int in next 4 bytes |
| 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; |
| } |
| } |
| |
| /** |
| * Reads and discards an array of <code>byte</code>s from a |
| * <code>DataInput</code>. |
| * |
| * @throws IOException |
| * A problem occurs while writing to <code>out</code> |
| * |
| * @see #writeByteArray(byte[], DataOutput) |
| */ |
| public static void skipByteArray(DataInput in) |
| throws IOException { |
| |
| InternalDataSerializer.checkIn(in); |
| |
| int length = InternalDataSerializer.readArrayLength(in); |
| if (length != -1) { |
| in.skipBytes(length); |
| if (logger.isTraceEnabled(LogMarker.SERIALIZER)) { |
| logger.trace(LogMarker.SERIALIZER, "Skipped byte array of length {}", length); |
| } |
| } |
| } |
| |
| public static final 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"); |
| } |
| } |
| |
| /** |
| * Reads an instance of <code>String</code> from a |
| * <code>DataInput</code> given the header byte already being read. |
| * The return value may be <code>null</code>. |
| * |
| * @throws IOException |
| * A problem occurs while reading from <code>in</code> |
| * |
| * @since 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; |
| |
| public static void registerDVDDeserializer(DataSerializer dvddeslzr) { |
| dvddeserializer = dvddeslzr; |
| } |
| |
| /** |
| * Just like readObject but make sure and pdx deserialized is not |
| * a PdxInstance. |
| * @since 6.6.2 |
| */ |
| public static final <T> T readNonPdxInstanceObject(final DataInput in) |
| throws IOException, ClassNotFoundException { |
| boolean wouldReadSerialized = PdxInstanceImpl.getPdxReadSerialized(); |
| if (!wouldReadSerialized) { |
| return DataSerializer.<T>readObject(in); |
| } else { |
| PdxInstanceImpl.setPdxReadSerialized(false); |
| try { |
| return DataSerializer.<T>readObject(in); |
| } finally { |
| PdxInstanceImpl.setPdxReadSerialized(true); |
| } |
| } |
| } |
| |
| public static final 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 SQLF_DVD_ARR: |
| return dvddeserializer.fromData(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 = null; |
| 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 enfOfStream) { |
| // InputStream.read() should return -1 on EOF |
| return -1; |
| } |
| } |
| |
| // public int read(byte[] b, int off, int len) |
| // throws IOException { |
| // // @todo davidw Do read() and readFully() have the |
| // // same semantics in this case? |
| // in.readFully(b, off, len); |
| // return len; |
| // } |
| |
| // public long skip(long n) throws IOException { |
| // // @todo davidw Is casting the right thing to do? |
| // return in.skipBytes((int) n); |
| // } |
| }; |
| } |
| |
| 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 final Object readUserDataSerializable(final DataInput in, int classId) |
| throws IOException, ClassNotFoundException { |
| 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) { |
| SerializationException ex2 = new SerializationException(LocalizedStrings.DataSerializer_COULD_NOT_DESERIALIZE_AN_INSTANCE_OF_0.toLocalizedString(instantiator.getInstantiatedClass().getName()), ex); |
| throw ex2; |
| } |
| } |
| } |
| |
| private static final ThreadLocal<Boolean> pdxSerializationInProgress = new ThreadLocal<Boolean>(); |
| 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 final static boolean writePdx(DataOutput out, GemFireCacheImpl gfc, |
| Object pdx, PdxSerializer pdxSerializer) throws IOException { |
| TypeRegistry tr = null; |
| if (gfc != null) { |
| tr = gfc.getPdxRegistry(); |
| } |
| |
| PdxWriterImpl writer; |
| { |
| PdxOutputStream os; |
| if (out instanceof HeapDataOutputStream) { |
| os = new PdxOutputStream((HeapDataOutputStream) out); |
| } else { |
| os = new PdxOutputStream(); |
| } |
| 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 ex) { |
| throw ex; |
| } catch (CancelException ex) { |
| //Serializing a PDX can result in a cache closed exception. Just rethrow |
| throw ex; |
| } catch (NonPortableClassException ex) { |
| throw ex; |
| } catch (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(gfc).incPdxSerialization(bytesWritten); |
| if (!(out instanceof HeapDataOutputStream)) { |
| writer.sendTo(out); |
| } |
| return true; |
| } |
| |
| public static DMStats getDMStats(GemFireCacheImpl gfc) { |
| if (gfc != null) { |
| return gfc.getDistributionManager().getStats(); |
| } else { |
| DMStats result = InternalDistributedSystem.getDMStats(); |
| if (result == null) { |
| result = new LonerDistributionManager.DummyDMStats(); |
| } |
| return result; |
| } |
| } |
| |
| private static final Object readPdxSerializable(final DataInput in) |
| throws IOException, ClassNotFoundException { |
| |
| int len = in.readInt(); |
| int typeId = in.readInt(); |
| |
| GemFireCacheImpl gfc = GemFireCacheImpl.getForPdx("PDX registry is unavailable because the Cache has been closed."); |
| PdxType pdxType = gfc.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(gfc); |
| dmStats.incPdxDeserialization(len+9); |
| |
| // check if PdxInstance needs to be returned. |
| if (pdxType.getNoDomainClass() || gfc.getPdxReadSerializedByAnyGemFireServices()) { |
| // if (logger.isDebugEnabled()) { |
| // gfc.getLogger().info("returning PdxInstance", new Exception("stack trace")); |
| // } |
| dmStats.incPdxInstanceCreations(); |
| return new PdxInstanceImpl(pdxType, in, len); |
| } else { |
| // if (logger.isDebugEnabled()) { |
| // gfc.getLogger().info("returning domain object", new Exception("stack trace")); |
| // } |
| // return domain object. |
| 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 final PdxInstance readPdxInstance(final byte[] dataBytes, GemFireCacheImpl gfc) { |
| 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 = gfc.getPdxRegistry().getType(typeId); |
| //gfc.getLogger().info("logger.isDebugEnabled(): pdxType="+ pdxType); |
| 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 = gfc.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; |
| } |
| |
| ///////////////////////////// START Test only methods ///////////////////////////// |
| public static int getLoadedDataSerializers() { |
| return idsToSerializers.size(); |
| } |
| |
| public final static Map getDsClassesToHoldersMap() { |
| return dsClassesToHolders; |
| } |
| |
| public final static Map getIdsToHoldersMap() { |
| return idsToHolders; |
| } |
| |
| public final static Map getSupportedClassesToHoldersMap() { |
| return supportedClassesToHolders; |
| } |
| ///////////////////////////// END Test only methods ///////////////////////////// |
| |
| |
| ///////////////// END DataSerializer Implementation Methods /////////// |
| |
| /////////////////////// Inner Classes /////////////////////// |
| |
| /** |
| * A marker object for <Code>DataSerializer</code>s that have not |
| * been registered. Using this marker object allows us to |
| * asynchronously send <Code>DataSerializer</code> 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. |
| */ |
| static abstract class Marker { |
| /** The DataSerializer that is filled in upon registration */ |
| protected DataSerializer serializer = null; |
| /** set to true once setSerializer is called. */ |
| protected 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</code>s that have not |
| * been registered. Using this marker object allows us to |
| * asynchronously send <Code>DataSerializer</code> 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. |
| * @since 5.7 |
| */ |
| 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. |
| */ |
| static int WAIT_MS = Integer.getInteger("gemfire.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</code> is returned. |
| */ |
| @Override |
| DataSerializer getSerializer() { |
| boolean firstTime = true; |
| long endTime = 0; |
| synchronized (this) { |
| 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 |
| // if (!this.hasBeenSet) { |
| // logger.info("logger.isDebugEnabled() getSerializer had to wait for " + remainingMs + "ms", |
| // new Exception("STACK")); |
| // } |
| } 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 ex) { |
| Thread.currentThread().interrupt(); |
| // Just return null, let it fail |
| return null; |
| } |
| } |
| return this.serializer; |
| } |
| } |
| } |
| |
| /** |
| * A marker object for <Code>DataSerializer</code>s that is in the process |
| * of being registered. |
| * It is possible for getSerializer to return <code>null</code> |
| * |
| * @since 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</code> 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 ex) { |
| 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</code> being |
| * registered. |
| */ |
| public static final class RegistrationMessage extends PooledDistributionMessage { |
| /** The id of the <code>DataSerializer</code> that was |
| * registered |
| * since 5.7 an int instead of a byte |
| */ |
| private int id; |
| |
| /** The eventId of the <codE>DataSerializer</code> that was |
| * registered */ |
| protected EventID eventId; |
| |
| /** The name of the <code>DataSerializer</code> class */ |
| private String className; |
| /** The versions in which this message was modified */ |
| private static final Version[] dsfidVersions = new Version[]{}; |
| |
| /** |
| * Constructor for <code>DataSerializable</code> |
| */ |
| public RegistrationMessage() { |
| |
| } |
| |
| /** |
| * Creates a new <code>RegistrationMessage</code> that broadcasts |
| * that the given <code>DataSerializer</code> was registered. |
| */ |
| public RegistrationMessage(DataSerializer s) { |
| this.className = s.getClass().getName(); |
| this.id = s.getId(); |
| this.eventId = (EventID)s.getEventId(); |
| } |
| |
| public 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 = null; |
| 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 = null; |
| 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)); |
| return; |
| } 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)); |
| return; |
| } |
| } 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)); |
| return; |
| } 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)); |
| return; |
| } |
| } |
| } |
| |
| 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(new Object[]{Integer.valueOf(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</code> is {@linkplain |
| * Instantiator#register(Instantiator) registered}. |
| */ |
| public void newInstantiator(Instantiator instantiator); |
| |
| /** |
| * Invoked when a new <code>DataSerializer</code> is {@linkplain |
| * DataSerializer#register(Class) registered}. |
| */ |
| public void newDataSerializer(DataSerializer ds); |
| |
| } |
| |
| /** |
| * An <code>ObjectInputStream</code> whose {@link #resolveClass} |
| * method loads classes from the current context class loader. |
| */ |
| private static class DSObjectInputStream extends ObjectInputStream { |
| |
| /** |
| * Creates a new <code>DSObjectInputStream</code> that delegates |
| * its behavior to a given <code>InputStream</code>. |
| */ |
| public DSObjectInputStream(InputStream stream) throws IOException { |
| super(stream); |
| } |
| |
| @Override |
| protected Class resolveClass(ObjectStreamClass desc) |
| throws IOException, ClassNotFoundException { |
| |
| String className = desc.getName(); |
| try { |
| return getCachedClass(className); |
| |
| } catch (ClassNotFoundException ex) { |
| 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); |
| } |
| } |
| } |
| |
| // /** |
| // * A <code>DataOutput</code> that writes special header information |
| // * before it writes any other data. It is passed to a |
| // * <code>DataSerializer</code>'s {@link |
| // * DataSerializer#toData(Object, DataOutput)} method to ensure |
| // * that the stream has the correct format. |
| // */ |
| // private static class DSDataOutput implements DataOutput { |
| // /** Has the header information been written? */ |
| // private boolean headerWritten = false; |
| |
| // /** The id of serializer that is writing to this output */ |
| // private byte serializerId; |
| |
| // /** The output stream to which this DSDataOutput writes */ |
| // protected DataOutput out; |
| |
| // ////////////////////// Constructors ////////////////////// |
| |
| // /** |
| // * Creates a new <code>DSDataOutput</code> that write to the |
| // * given output stream. |
| // */ |
| // DSDataOutput(DataOutput out) { |
| // this.out = out; |
| // } |
| |
| // ///////////////////// Instance Methods //////////////////// |
| |
| // /** |
| // * Sets the id of the serializer that will possibly write to |
| // * this stream. |
| // */ |
| // void setSerializerId(byte id) { |
| // this.serializerId = id; |
| // } |
| |
| // /** |
| // * Returns whether or not any data hass been written to this |
| // * stream. |
| // */ |
| // boolean hasWritten() { |
| // return this.headerWritten; |
| // } |
| |
| // /** |
| // * Write the {@link #USER_CLASS} "class id" followed by the id |
| // * of the serializer. |
| // */ |
| // private void writeHeader() throws IOException { |
| // if (!headerWritten) { |
| // out.writeByte(USER_CLASS); |
| // out.writeByte(serializerId); |
| // this.headerWritten = true; |
| // } |
| // } |
| |
| // public void write(int b) throws IOException { |
| // writeHeader(); |
| // out.write(b); |
| // } |
| |
| // public void write(byte[] b) throws IOException { |
| // writeHeader(); |
| // out.write(b); |
| // } |
| |
| // public void write(byte[] b, int off, int len) |
| // throws IOException { |
| // writeHeader(); |
| // out.write(b, off, len); |
| // } |
| |
| // public void writeBoolean(boolean v) throws IOException { |
| // writeHeader(); |
| // out.writeBoolean(v); |
| // } |
| |
| // public void writeByte(int v) throws IOException { |
| // writeHeader(); |
| // out.writeByte(v); |
| // } |
| |
| // public void writeShort(int v) throws IOException { |
| // writeHeader(); |
| // out.writeShort(v); |
| // } |
| |
| // public void writeChar(int v) throws IOException { |
| // writeHeader(); |
| // out.writeChar(v); |
| // } |
| |
| // public void writeInt(int v) throws IOException { |
| // writeHeader(); |
| // out.writeInt(v); |
| // } |
| |
| // public void writeLong(long v) throws IOException { |
| // writeHeader(); |
| // out.writeLong(v); |
| // } |
| |
| // public void writeFloat(float v) throws IOException { |
| // writeHeader(); |
| // out.writeFloat(v); |
| // } |
| |
| // public void writeDouble(double v) throws IOException { |
| // writeHeader(); |
| // out.writeDouble(v); |
| // } |
| |
| // public void writeBytes(String s) throws IOException { |
| // writeHeader(); |
| // out.writeBytes(s); |
| // } |
| |
| // public void writeChars(String s) throws IOException { |
| // writeHeader(); |
| // out.writeChars(s); |
| // } |
| |
| // public void writeUTF(String str) throws IOException { |
| // writeHeader(); |
| // out.writeUTF(str); |
| // } |
| |
| // } |
| /** |
| * Used to implement serialization code for the well known classes we support |
| * in DataSerializer. |
| * @since 5.7 |
| */ |
| protected static abstract class WellKnownDS extends DataSerializer { |
| @Override |
| public final 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 final Class[] getSupportedClasses() { |
| // illegal for a customer to return null but we can do it since we never register |
| // this serializer. |
| return null; |
| } |
| @Override |
| public final 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. |
| * @author darrel |
| * |
| */ |
| protected static abstract 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); |
| } |
| } |
| } |
| |
| private static final byte INT_VL = 126; // Variable Length long encoded as int |
| // in next 4 bytes |
| private static final byte LONG_VL = 127; // Variable Length long encoded as |
| // long in next 8 bytes |
| 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 respresented 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 respresented 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 |
| */ |
| public 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 |
| */ |
| public 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 e) { |
| return 0; |
| } |
| return out.size(); |
| } |
| |
| public static final boolean LOAD_CLASS_EACH_TIME = Boolean.getBoolean("gemfire.loadClassOnEveryDeserialization"); |
| private static final CopyOnWriteHashMap<String, WeakReference<Class<?>>> classCache = LOAD_CLASS_EACH_TIME ? null : new CopyOnWriteHashMap<String, WeakReference<Class<?>>>(); |
| private static final Object cacheAccessLock = new Object(); |
| |
| public static Class<?> getCachedClass(String className) throws ClassNotFoundException { |
| 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<Class<?>>(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 JarClassLoader |
| classCache.clear(); |
| } |
| } |
| } |