/* * Copyright (c) 2008-2017, 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.config.SerializationConfig; import com.hazelcast.config.SerializerConfig; import com.hazelcast.logging.Logger; import com.hazelcast.nio.ClassLoaderUtil; import com.hazelcast.nio.serialization.HazelcastSerializationException; import com.hazelcast.nio.serialization.Serializer; import com.hazelcast.nio.serialization.SerializerHook; import com.hazelcast.util.ExceptionUtil; import com.hazelcast.util.ServiceLoader; import java.lang.reflect.Constructor; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; /** * Loads auto registered serializers using {@link com.hazelcast.nio.serialization.SerializerHook} * by reading in the file "META-INF/services/com.hazelcast.SerializerHook" and instantiating * the defined SerializerHooks.<br/> * This system is meant to be internal code and is subject to change at any time. */ final class SerializerHookLoader { private static final String FACTORY_ID = "com.hazelcast.SerializerHook"; private final boolean useDefaultConstructorOnly = Boolean.getBoolean("hazelcast.compat.serializers.use.default.constructor.only"); private final Map<Class, Object> serializers = new HashMap<Class, Object>(); private final Collection<SerializerConfig> serializerConfigs; private final ClassLoader classLoader; SerializerHookLoader(SerializationConfig serializationConfig, ClassLoader classLoader) { this.serializerConfigs = serializationConfig != null ? serializationConfig.getSerializerConfigs() : null; this.classLoader = classLoader; load(); } private void load() { try { final Iterator<SerializerHook> hooks = ServiceLoader.iterator(SerializerHook.class, FACTORY_ID, classLoader); while (hooks.hasNext()) { final SerializerHook hook = hooks.next(); final Class serializationType = hook.getSerializationType(); if (serializationType != null) { serializers.put(serializationType, hook); } } } catch (Exception e) { throw ExceptionUtil.rethrow(e); } if (serializerConfigs != null) { for (SerializerConfig serializerConfig : serializerConfigs) { Serializer serializer = serializerConfig.getImplementation(); Class serializationType = serializerConfig.getTypeClass(); if (serializationType == null) { try { serializationType = ClassLoaderUtil.loadClass(classLoader, serializerConfig.getTypeClassName()); } catch (ClassNotFoundException e) { throw new HazelcastSerializationException(e); } } if (serializer == null) { serializer = createSerializerInstance(serializerConfig, serializationType); } register(serializationType, serializer); } } } private Serializer createSerializerInstance(SerializerConfig serializerConfig, Class serializationType) { try { String className = serializerConfig.getClassName(); if (useDefaultConstructorOnly) { return ClassLoaderUtil.newInstance(classLoader, className); } else { return createSerializerInstanceWithFallback(serializationType, className); } } catch (Exception e) { throw new HazelcastSerializationException(e); } } private Serializer createSerializerInstanceWithFallback(Class serializationType, String className) throws Exception { Class<?> clazz = ClassLoaderUtil.loadClass(classLoader, className); try { Constructor<?> constructor = clazz.getDeclaredConstructor(Class.class); constructor.setAccessible(true); return (Serializer) constructor.newInstance(serializationType); } catch (NoSuchMethodException e) { //fallback to no-arg constructor Constructor<?> constructor = clazz.getDeclaredConstructor(); constructor.setAccessible(true); return (Serializer) constructor.newInstance(); } } Map<Class, Object> getSerializers() { return serializers; } private void register(Class serializationType, Serializer serializer) { final Object current = serializers.get(serializationType); if (current != null) { if (current.equals(serializer)) { Logger.getLogger(getClass()).warning("Serializer[" + serializationType.toString() + "] is already registered! Skipping " + serializer); } else if (current instanceof SerializerHook && ((SerializerHook) current).isOverwritable()) { serializers.put(serializationType, serializer); } else { throw new IllegalArgumentException("Serializer[" + serializationType.toString() + "] is already registered! " + current + " -> " + serializer); } } else { serializers.put(serializationType, serializer); } } }