blob: 4626df72055f160d8f11d70b045226d5db98a5d8 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ignite.internal.marshaller.optimized;
import java.io.IOException;
import java.io.ObjectStreamClass;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.ConcurrentMap;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteException;
import org.apache.ignite.internal.util.GridUnsafe;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.marshaller.MarshallerContext;
import static org.apache.ignite.internal.MarshallerPlatformIds.JAVA_ID;
/**
* Miscellaneous utility methods to facilitate {@link OptimizedMarshaller}.
*/
class OptimizedMarshallerUtils {
/** */
static final long HASH_SET_MAP_OFF;
/** */
static final byte JDK = -2;
/** */
static final byte HANDLE = -1;
/** */
static final byte NULL = 0;
/** */
static final byte BYTE = 1;
/** */
static final byte SHORT = 2;
/** */
static final byte INT = 3;
/** */
static final byte LONG = 4;
/** */
static final byte FLOAT = 5;
/** */
static final byte DOUBLE = 6;
/** */
static final byte CHAR = 7;
/** */
static final byte BOOLEAN = 8;
/** */
static final byte BYTE_ARR = 9;
/** */
static final byte SHORT_ARR = 10;
/** */
static final byte INT_ARR = 11;
/** */
static final byte LONG_ARR = 12;
/** */
static final byte FLOAT_ARR = 13;
/** */
static final byte DOUBLE_ARR = 14;
/** */
static final byte CHAR_ARR = 15;
/** */
static final byte BOOLEAN_ARR = 16;
/** */
static final byte OBJ_ARR = 17;
/** */
static final byte STR = 18;
/** */
static final byte UUID = 19;
/** */
static final byte PROPS = 20;
/** */
static final byte ARRAY_LIST = 21;
/** */
static final byte HASH_MAP = 22;
/** */
static final byte HASH_SET = 23;
/** */
static final byte LINKED_LIST = 24;
/** */
static final byte LINKED_HASH_MAP = 25;
/** */
static final byte LINKED_HASH_SET = 26;
/** */
static final byte DATE = 27;
/** */
static final byte CLS = 28;
/** */
static final byte PROXY = 29;
/** */
static final byte ENUM = 100;
/** */
static final byte EXTERNALIZABLE = 101;
/** */
static final byte SERIALIZABLE = 102;
/** UTF-8 character name. */
static final Charset UTF_8 = Charset.forName("UTF-8");
static {
long mapOff;
try {
mapOff = GridUnsafe.objectFieldOffset(HashSet.class.getDeclaredField("map"));
}
catch (NoSuchFieldException ignored) {
try {
// Workaround for legacy IBM JRE.
mapOff = GridUnsafe.objectFieldOffset(HashSet.class.getDeclaredField("backingMap"));
}
catch (NoSuchFieldException e2) {
throw new IgniteException("Initialization failure.", e2);
}
}
HASH_SET_MAP_OFF = mapOff;
}
/**
*/
private OptimizedMarshallerUtils() {
// No-op.
}
/**
* Gets descriptor for provided class.
*
* @param clsMap Class descriptors by class map.
* @param cls Class.
* @param ctx Context.
* @param mapper ID mapper.
* @return Descriptor.
* @throws IOException In case of error.
*/
static OptimizedClassDescriptor classDescriptor(
ConcurrentMap<Class, OptimizedClassDescriptor> clsMap,
Class cls,
MarshallerContext ctx,
OptimizedMarshallerIdMapper mapper)
throws IOException
{
OptimizedClassDescriptor desc = clsMap.get(cls);
if (desc == null) {
int typeId = resolveTypeId(cls.getName(), mapper);
boolean registered;
try {
registered = ctx.registerClassName(JAVA_ID, typeId, cls.getName());
}
catch (Exception e) {
throw new IOException("Failed to register class: " + cls.getName(), e);
}
desc = new OptimizedClassDescriptor(cls, registered ? typeId : 0, clsMap, ctx, mapper);
if (registered) {
OptimizedClassDescriptor old = clsMap.putIfAbsent(cls, desc);
if (old != null)
desc = old;
}
}
return desc;
}
/**
* @param clsName Class name.
* @param mapper Mapper.
* @return Type ID.
*/
private static int resolveTypeId(String clsName, OptimizedMarshallerIdMapper mapper) {
int typeId;
if (mapper != null) {
typeId = mapper.typeId(clsName);
if (typeId == 0)
typeId = clsName.hashCode();
}
else
typeId = clsName.hashCode();
return typeId;
}
/**
* Gets descriptor for provided ID.
*
* @param clsMap Class descriptors by class map.
* @param typeId ID.
* @param ldr Class loader.
* @param ctx Context.
* @param mapper ID mapper.
* @return Descriptor.
* @throws IOException In case of error.
* @throws ClassNotFoundException If class was not found.
*/
static OptimizedClassDescriptor classDescriptor(
ConcurrentMap<Class, OptimizedClassDescriptor> clsMap,
int typeId,
ClassLoader ldr,
MarshallerContext ctx,
OptimizedMarshallerIdMapper mapper) throws IOException, ClassNotFoundException {
Class cls;
try {
cls = ctx.getClass(typeId, ldr);
}
catch (IgniteCheckedException e) {
throw new IOException("Failed to resolve class for ID: " + typeId, e);
}
OptimizedClassDescriptor desc = clsMap.get(cls);
if (desc == null) {
OptimizedClassDescriptor old = clsMap.putIfAbsent(cls, desc =
new OptimizedClassDescriptor(cls, resolveTypeId(cls.getName(), mapper), clsMap, ctx, mapper));
if (old != null)
desc = old;
}
return desc;
}
/**
* Computes the serial version UID value for the given class. The code is taken from {@link
* ObjectStreamClass#computeDefaultSUID(Class)}.
*
* @param cls A class.
* @param fields Fields.
* @return A serial version UID.
* @throws IOException If failed.
*/
@SuppressWarnings("ForLoopReplaceableByForEach")
static short computeSerialVersionUid(Class cls, List<Field> fields) throws IOException {
if (Serializable.class.isAssignableFrom(cls) && !Enum.class.isAssignableFrom(cls)) {
try {
Field field = cls.getDeclaredField("serialVersionUID");
if (field.getType() == long.class) {
int mod = field.getModifiers();
if (Modifier.isStatic(mod) && Modifier.isFinal(mod)) {
field.setAccessible(true);
return (short)field.getLong(null);
}
}
}
catch (NoSuchFieldException ignored) {
// No-op.
}
catch (IllegalAccessException e) {
throw new IOException(e);
}
if (OptimizedMarshaller.USE_DFLT_SUID)
return (short)ObjectStreamClass.lookup(cls).getSerialVersionUID();
}
MessageDigest md;
try {
md = MessageDigest.getInstance("SHA");
}
catch (NoSuchAlgorithmException e) {
throw new IOException("Failed to get digest for SHA.", e);
}
md.update(cls.getName().getBytes(UTF_8));
if (!F.isEmpty(fields)) {
for (int i = 0; i < fields.size(); i++) {
Field f = fields.get(i);
md.update(f.getName().getBytes(UTF_8));
md.update(f.getType().getName().getBytes(UTF_8));
}
}
byte[] hashBytes = md.digest();
long hash = 0;
// Composes a single-long hash from the byte[] hash.
for (int i = Math.min(hashBytes.length, 8) - 1; i >= 0; i--)
hash = (hash << 8) | (hashBytes[i] & 0xFF);
return (short)hash;
}
/**
* Gets byte field value.
*
* @param obj Object.
* @param off Field offset.
* @return Byte value.
*/
static byte getByte(Object obj, long off) {
return GridUnsafe.getByteField(obj, off);
}
/**
* Sets byte field value.
*
* @param obj Object.
* @param off Field offset.
* @param val Value.
*/
static void setByte(Object obj, long off, byte val) {
GridUnsafe.putByteField(obj, off, val);
}
/**
* Gets short field value.
*
* @param obj Object.
* @param off Field offset.
* @return Short value.
*/
static short getShort(Object obj, long off) {
return GridUnsafe.getShortField(obj, off);
}
/**
* Sets short field value.
*
* @param obj Object.
* @param off Field offset.
* @param val Value.
*/
static void setShort(Object obj, long off, short val) {
GridUnsafe.putShortField(obj, off, val);
}
/**
* Gets integer field value.
*
* @param obj Object.
* @param off Field offset.
* @return Integer value.
*/
static int getInt(Object obj, long off) {
return GridUnsafe.getIntField(obj, off);
}
/**
* Sets integer field value.
*
* @param obj Object.
* @param off Field offset.
* @param val Value.
*/
static void setInt(Object obj, long off, int val) {
GridUnsafe.putIntField(obj, off, val);
}
/**
* Gets long field value.
*
* @param obj Object.
* @param off Field offset.
* @return Long value.
*/
static long getLong(Object obj, long off) {
return GridUnsafe.getLongField(obj, off);
}
/**
* Sets long field value.
*
* @param obj Object.
* @param off Field offset.
* @param val Value.
*/
static void setLong(Object obj, long off, long val) {
GridUnsafe.putLongField(obj, off, val);
}
/**
* Gets float field value.
*
* @param obj Object.
* @param off Field offset.
* @return Float value.
*/
static float getFloat(Object obj, long off) {
return GridUnsafe.getFloatField(obj, off);
}
/**
* Sets float field value.
*
* @param obj Object.
* @param off Field offset.
* @param val Value.
*/
static void setFloat(Object obj, long off, float val) {
GridUnsafe.putFloatField(obj, off, val);
}
/**
* Gets double field value.
*
* @param obj Object.
* @param off Field offset.
* @return Double value.
*/
static double getDouble(Object obj, long off) {
return GridUnsafe.getDoubleField(obj, off);
}
/**
* Sets double field value.
*
* @param obj Object.
* @param off Field offset.
* @param val Value.
*/
static void setDouble(Object obj, long off, double val) {
GridUnsafe.putDoubleField(obj, off, val);
}
/**
* Gets char field value.
*
* @param obj Object.
* @param off Field offset.
* @return Char value.
*/
static char getChar(Object obj, long off) {
return GridUnsafe.getCharField(obj, off);
}
/**
* Sets char field value.
*
* @param obj Object.
* @param off Field offset.
* @param val Value.
*/
static void setChar(Object obj, long off, char val) {
GridUnsafe.putCharField(obj, off, val);
}
/**
* Gets boolean field value.
*
* @param obj Object.
* @param off Field offset.
* @return Boolean value.
*/
static boolean getBoolean(Object obj, long off) {
return GridUnsafe.getBooleanField(obj, off);
}
/**
* Sets boolean field value.
*
* @param obj Object.
* @param off Field offset.
* @param val Value.
*/
static void setBoolean(Object obj, long off, boolean val) {
GridUnsafe.putBooleanField(obj, off, val);
}
/**
* Gets field value.
*
* @param obj Object.
* @param off Field offset.
* @return Value.
*/
static Object getObject(Object obj, long off) {
return GridUnsafe.getObjectField(obj, off);
}
/**
* Sets field value.
*
* @param obj Object.
* @param off Field offset.
* @param val Value.
*/
static void setObject(Object obj, long off, Object val) {
GridUnsafe.putObjectField(obj, off, val);
}
}