All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hazelcast.internal.serialization.impl.DataSerializableSerializer Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2008-2024, Hazelcast, Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.hazelcast.internal.serialization.impl;

import com.hazelcast.instance.BuildInfoProvider;
import com.hazelcast.internal.nio.ClassLoaderUtil;
import com.hazelcast.internal.serialization.DataSerializerHook;
import com.hazelcast.internal.util.ExceptionUtil;
import com.hazelcast.internal.util.ServiceLoader;
import com.hazelcast.internal.util.collection.Int2ObjectHashMap;
import com.hazelcast.logging.Logger;
import com.hazelcast.nio.ObjectDataInput;
import com.hazelcast.nio.ObjectDataOutput;
import com.hazelcast.nio.serialization.DataSerializable;
import com.hazelcast.nio.serialization.DataSerializableFactory;
import com.hazelcast.nio.serialization.HazelcastSerializationException;
import com.hazelcast.nio.serialization.IdentifiedDataSerializable;
import com.hazelcast.nio.serialization.StreamSerializer;
import com.hazelcast.nio.serialization.TypedDataSerializable;
import com.hazelcast.nio.serialization.TypedStreamDeserializer;
import com.hazelcast.nio.serialization.impl.VersionedIdentifiedDataSerializable;
import com.hazelcast.version.Version;

import java.io.IOException;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static com.hazelcast.internal.serialization.impl.SerializationConstants.CONSTANT_TYPE_DATA_SERIALIZABLE;

/**
 * The { StreamSerializer} that handles:
 * 
    *
  1. { DataSerializable}
  2. *
  3. { IdentifiedDataSerializable}
  4. *
*/ @SuppressWarnings("checkstyle:npathcomplexity") final class DataSerializableSerializer implements StreamSerializer, TypedStreamDeserializer { public static final byte IDS_FLAG = 1 << 0; public static final byte EE_FLAG = 1 << 1; private static final String FACTORY_ID = "com.hazelcast.DataSerializerHook"; private final Version version = Version.of(BuildInfoProvider.getBuildInfo().getVersion()); private final Int2ObjectHashMap factories = new Int2ObjectHashMap<>(); DataSerializableSerializer(Map dataSerializableFactories, ClassLoader classLoader) { try { List hooks = new ArrayList<>(); ServiceLoader.iterator(DataSerializerHook.class, FACTORY_ID, classLoader) .forEachRemaining(hooks::add); for (DataSerializerHook hook : hooks) { if (!hook.shouldRegister()) { continue; } final DataSerializableFactory factory = hook.createFactory(); if (factory != null) { register(hook.getFactoryId(), factory); } } for (DataSerializerHook hook : hooks) { hook.afterFactoriesCreated(factories); } } catch (Exception e) { throw ExceptionUtil.rethrow(e); } if (dataSerializableFactories != null) { for (Map.Entry entry : dataSerializableFactories.entrySet()) { register(entry.getKey(), entry.getValue()); } } } private void register(int factoryId, DataSerializableFactory factory) { final DataSerializableFactory current = factories.get(factoryId); if (current != null) { if (current.equals(factory)) { Logger.getLogger(getClass()).warning("DataSerializableFactory[" + factoryId + "] is already registered! Skipping " + factory); } else { throw new IllegalArgumentException("DataSerializableFactory[" + factoryId + "] is already registered! " + current + " -> " + factory); } } else { factories.put(factoryId, factory); } } @Override public int getTypeId() { return CONSTANT_TYPE_DATA_SERIALIZABLE; } @Override public DataSerializable read(ObjectDataInput in) throws IOException { return readInternal(in, null); } @Override public DataSerializable read(ObjectDataInput in, Class aClass) throws IOException { return readInternal(in, aClass); } private DataSerializable readInternal(ObjectDataInput in, Class aClass) throws IOException { setInputVersion(in, version); DataSerializable ds = null; if (null != aClass) { try { ds = (DataSerializable) aClass.getDeclaredConstructor().newInstance(); } catch (Exception e) { Exception ex = tryClarifyReflectiveOperationException(aClass, e); throw new HazelcastSerializationException("Requested class " + aClass + " could not be instantiated.", ex); } } final byte header = in.readByte(); int id = 0; int factoryId = 0; String className = null; try { // If you ever change the way this is serialized think about to change // BasicOperationService::extractOperationCallId if (isFlagSet(header, IDS_FLAG)) { factoryId = in.readInt(); final DataSerializableFactory dsf = factories.get(factoryId); if (dsf == null) { throw new HazelcastSerializationException("No DataSerializerFactory registered for namespace: " + factoryId); } id = in.readInt(); if (null == aClass) { ds = dsf.create(id); if (ds == null) { throw new HazelcastSerializationException(dsf + " is not be able to create an instance for ID: " + id + " on factory ID: " + factoryId); } } } else { className = in.readString(); if (null == aClass) { ds = ClassLoaderUtil.newInstance(in.getClassLoader(), className); } } if (isFlagSet(header, EE_FLAG)) { in.readByte(); in.readByte(); } ds.readData(in); return ds; } catch (Exception e) { Exception ex = tryClarifyNoSuchMethodException(in.getClassLoader(), className, e); throw rethrowReadException(id, factoryId, className, ex); } } public static boolean isFlagSet(byte value, byte flag) { return (value & flag) != 0; } private IOException rethrowReadException(int id, int factoryId, String className, Exception e) throws IOException { if (e instanceof IOException exception) { throw exception; } if (e instanceof HazelcastSerializationException exception) { throw exception; } throw new HazelcastSerializationException("Problem while reading DataSerializable, namespace: " + factoryId + ", ID: " + id + ", class: '" + className + "'" + ", exception: " + e.getMessage(), e); } /** * @return *
    *
  • If {@code exception} is an { NoSuchMethodError} and matches criteria of * { #tryGenerateClarifiedExceptionMessage(Class)}, a new { ReflectiveOperationException} with the new * message *
  • Otherwise, {code exception} *
*/ private Exception tryClarifyReflectiveOperationException(Class aClass, Exception exception) { if (!(exception instanceof ReflectiveOperationException)) { return exception; } String message = tryGenerateClarifiedExceptionMessage(aClass); if (message == null) { return exception; } Exception clarifiedException = new ReflectiveOperationException(message); clarifiedException.initCause(exception); return clarifiedException; } private Exception tryClarifyNoSuchMethodException(ClassLoader classLoader, String className, Exception exception) { if (!(exception instanceof NoSuchMethodException noSuchMethodException)) { return exception; } Class aClass; try { ClassLoader effectiveClassLoader = classLoader == null ? ClassLoaderUtil.class.getClassLoader() : classLoader; aClass = ClassLoaderUtil.loadClass(effectiveClassLoader, className); } catch (Exception e) { return noSuchMethodException; } String message = tryGenerateClarifiedExceptionMessage(aClass); if (message == null) { message = "Classes conforming to DataSerializable should provide a no-arguments constructor."; } NoSuchMethodException clarifiedException = new NoSuchMethodException(message); clarifiedException.initCause(noSuchMethodException); return clarifiedException; } @Override public void write(ObjectDataOutput out, DataSerializable obj) throws IOException { // If you ever change the way this is serialized think about to change // BasicOperationService::extractOperationCallId setOutputVersion(out, version); final boolean identified = obj instanceof IdentifiedDataSerializable; out.writeBoolean(identified); if (identified) { final IdentifiedDataSerializable ds = (IdentifiedDataSerializable) obj; out.writeInt(ds.getFactoryId()); out.writeInt(ds instanceof VersionedIdentifiedDataSerializable vids ? vids.getClassId(version) : ds.getClassId()); } else { if (obj instanceof TypedDataSerializable serializable) { out.writeString(serializable.getClassType().getName()); } else { out.writeString(obj.getClass().getName()); } } obj.writeData(out); } @Override public void destroy() { factories.clear(); } private static void setOutputVersion(ObjectDataOutput out, Version version) { out.setVersion(version); } private static void setInputVersion(ObjectDataInput in, Version version) { in.setVersion(version); } /** * @return an error message if {@code aClass} is: *
    *
  • { Class#isAnonymousClass()} *
  • { Class#isLocalClass()} *
  • non-{@code static} { Class#isMemberClass()} *
*/ private static String tryGenerateClarifiedExceptionMessage(Class aClass) { final String classType; if (aClass.isAnonymousClass()) { classType = "Anonymous"; } else if (aClass.isLocalClass()) { classType = "Local"; } else if (aClass.isMemberClass() && !Modifier.isStatic(aClass.getModifiers())) { classType = "Non-static member"; } else { return null; } return String.format("%s classes can't conform to DataSerializable since they can't " + "provide an explicit no-arguments constructor.", classType); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy