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

com.hazelcast.nio.ClassLoaderUtil Maven / Gradle / Ivy

There is a newer version: 4.5.4
Show newest version
/*
 * Copyright (c) 2008-2018, 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.nio;

import com.hazelcast.internal.usercodedeployment.impl.ClassSource;
import com.hazelcast.spi.annotation.PrivateApi;
import com.hazelcast.util.ConcurrentReferenceHashMap;
import com.hazelcast.util.ExceptionUtil;

import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import static com.hazelcast.util.Preconditions.isNotNull;
import static java.util.Collections.unmodifiableMap;

/**
 * Utility class to deal with classloaders.
 */
@PrivateApi
public final class ClassLoaderUtil {

    public static final String HAZELCAST_BASE_PACKAGE = "com.hazelcast.";
    public static final String HAZELCAST_ARRAY = "[L" + HAZELCAST_BASE_PACKAGE;

    private static final boolean CLASS_CACHE_DISABLED = Boolean.getBoolean("hazelcast.compat.classloading.cache.disabled");

    private static final Map PRIMITIVE_CLASSES;
    private static final int MAX_PRIM_CLASSNAME_LENGTH = 7;

    private static final ClassLoaderWeakCache CONSTRUCTOR_CACHE = new ClassLoaderWeakCache();
    private static final ClassLoaderWeakCache CLASS_CACHE = new ClassLoaderWeakCache();

    static {
        final Map primitives = new HashMap(10, 1.0f);
        primitives.put("boolean", boolean.class);
        primitives.put("byte", byte.class);
        primitives.put("int", int.class);
        primitives.put("long", long.class);
        primitives.put("short", short.class);
        primitives.put("float", float.class);
        primitives.put("double", double.class);
        primitives.put("char", char.class);
        primitives.put("void", void.class);
        PRIMITIVE_CLASSES = unmodifiableMap(primitives);
    }

    private ClassLoaderUtil() {
    }

    /**
     * Returns the {@code instance} if not null, otherwise constructs a new instance of the class using
     * {@link #newInstance(Class, ClassLoader, String)}.
     *
     * @param instance    the instance of the class, can be null
     * @param classLoader the classloader used for class instantiation
     * @param className   the name of the class being constructed
     * @return either the provided {@code instance} or a newly constructed instance of {@code className}
     */
    public static  T getOrCreate(T instance, ClassLoader classLoader, String className) {
        if (instance != null) {
            return instance;
        } else if (className != null) {
            try {
                return ClassLoaderUtil.newInstance(classLoader, className);
            } catch (Exception e) {
                throw ExceptionUtil.rethrow(e);
            }
        } else {
            return null;
        }
    }

    @SuppressWarnings("unchecked")
    public static  T newInstance(ClassLoader classLoader, final String className) throws Exception {
        classLoader = classLoader == null ? ClassLoaderUtil.class.getClassLoader() : classLoader;
        Constructor constructor = CONSTRUCTOR_CACHE.get(classLoader, className);
        if (constructor != null) {
            return constructor.newInstance();
        }
        Class klass = (Class) loadClass(classLoader, className);
        return newInstance(klass, classLoader, className);
    }

    public static  T newInstance(Class klass, ClassLoader classLoader, String className) throws Exception {
        final Constructor constructor = klass.getDeclaredConstructor();
        if (!constructor.isAccessible()) {
            constructor.setAccessible(true);
        }
        if (!shouldBypassCache(klass)) {
            CONSTRUCTOR_CACHE.put(classLoader, className, constructor);
        }
        return constructor.newInstance();
    }

    public static Class loadClass(final ClassLoader classLoader, final String className) throws ClassNotFoundException {
        isNotNull(className, "className");
        if (className.length() <= MAX_PRIM_CLASSNAME_LENGTH && Character.isLowerCase(className.charAt(0))) {
            final Class primitiveClass = PRIMITIVE_CLASSES.get(className);
            if (primitiveClass != null) {
                return primitiveClass;
            }
        }
        ClassLoader theClassLoader = classLoader;
        if (theClassLoader == null) {
            theClassLoader = Thread.currentThread().getContextClassLoader();
        }

        // first try to load it through the given classloader
        if (theClassLoader != null) {
            try {
                return tryLoadClass(className, theClassLoader);
            } catch (ClassNotFoundException ignore) {
                // reset selected classloader and try with others
                theClassLoader = null;
            }
        }

        // if failed and this is a Hazelcast class try again with our classloader
        if (className.startsWith(HAZELCAST_BASE_PACKAGE) || className.startsWith(HAZELCAST_ARRAY)) {
            theClassLoader = ClassLoaderUtil.class.getClassLoader();
        }
        if (theClassLoader == null) {
            theClassLoader = Thread.currentThread().getContextClassLoader();
        }
        if (theClassLoader != null) {
            return tryLoadClass(className, theClassLoader);
        }
        return Class.forName(className);
    }

    public static boolean isClassAvailable(final ClassLoader classLoader, final String className) {
        try {
            Class clazz = loadClass(classLoader, className);
            return clazz != null;
        } catch (ClassNotFoundException e) {
            return false;
        }
    }

    private static Class tryLoadClass(String className, ClassLoader classLoader) throws ClassNotFoundException {
        Class clazz;
        if (!CLASS_CACHE_DISABLED) {
            clazz = CLASS_CACHE.get(classLoader, className);
            if (clazz != null) {
                return clazz;
            }
        }

        if (className.startsWith("[")) {
            clazz = Class.forName(className, false, classLoader);
        } else {
            clazz = classLoader.loadClass(className);
        }

        if (!CLASS_CACHE_DISABLED) {
            if (!shouldBypassCache(clazz)) {
                CLASS_CACHE.put(classLoader, className, clazz);
            }
        }

        return clazz;
    }

    public static boolean isInternalType(Class type) {
        String name = type.getName();
        ClassLoader classLoader = ClassLoaderUtil.class.getClassLoader();
        return type.getClassLoader() == classLoader && name.startsWith(HAZELCAST_BASE_PACKAGE);
    }

    /**
     * Tries to load the given class.
     *
     * @param className Name of the class to load
     * @return Loaded class
     * @throws ClassNotFoundException when the class is not found
     */
    public static Class tryLoadClass(String className) throws ClassNotFoundException {
        try {
            return Class.forName(className);
        } catch (ClassNotFoundException e) {
            ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
            return contextClassLoader.loadClass(className);
        }
    }

    /**
     * Indicates whether or not the given class exists
     *
     * @param className Name of the class
     * @return {@code true} if the class exists, {@code false} otherwise
     */
    public static boolean isClassDefined(String className) {
        try {
            tryLoadClass(className);
            return true;
        } catch (ClassNotFoundException e) {
            return false;
        }
    }

    /**
     * Check whether given class implements an interface with the same name.
     * It returns true even when the implemented interface is loaded by a different
     * classloader and hence the class is not assignable into it.
     *
     * An interface is considered as implemented even when either:
     * 
    *
  • The class directly implements the interface
  • *
  • The class implements an interface which extends the original interface
  • *
  • One of superclasses directly implements the interface
  • *
  • One of superclasses implements an interface which extends the original interface
  • *
* * This is useful for logging purposes. * * @param clazz class to check whether implements the interface * @param iface interface to be implemented * @return true when the class implements the inteface with the same name */ public static boolean implementsInterfaceWithSameName(Class clazz, Class iface) { Class[] interfaces = getAllInterfaces(clazz); for (Class implementedInterface : interfaces) { if (implementedInterface.getName().equals(iface.getName())) { return true; } } return false; } public static Class[] getAllInterfaces(Class clazz) { Collection> interfaces = new HashSet>(); addOwnInterfaces(clazz, interfaces); addInterfacesOfSuperclasses(clazz, interfaces); return interfaces.toArray(new Class[0]); } private static void addOwnInterfaces(Class clazz, Collection> allInterfaces) { Class[] interfaces = clazz.getInterfaces(); Collections.addAll(allInterfaces, interfaces); for (Class cl : interfaces) { addOwnInterfaces(cl, allInterfaces); } } private static void addInterfacesOfSuperclasses(Class clazz, Collection> interfaces) { Class superClass = clazz.getSuperclass(); while (superClass != null) { addOwnInterfaces(superClass, interfaces); superClass = superClass.getSuperclass(); } } private static final class ClassLoaderWeakCache { private final ConcurrentMap>> cache; private ClassLoaderWeakCache() { // let's guess 16 classloaders to not waste too much memory (16 is default concurrency level) cache = new ConcurrentReferenceHashMap>>(16); } private void put(ClassLoader classLoader, String className, V value) { ClassLoader cl = classLoader == null ? ClassLoaderUtil.class.getClassLoader() : classLoader; ConcurrentMap> innerCache = cache.get(cl); if (innerCache == null) { // let's guess a start of 100 classes per classloader innerCache = new ConcurrentHashMap>(100); ConcurrentMap> old = cache.putIfAbsent(cl, innerCache); if (old != null) { innerCache = old; } } innerCache.put(className, new WeakReference(value)); } public V get(ClassLoader classloader, String className) { isNotNull(className, "className"); ConcurrentMap> innerCache = cache.get(classloader); if (innerCache == null) { return null; } WeakReference reference = innerCache.get(className); V value = reference == null ? null : reference.get(); if (reference != null && value == null) { innerCache.remove(className); } return value; } } private static boolean shouldBypassCache(Class clazz) { // dynamically loaded class should not be cached here, as they are already // cached in the DistributedLoadingService (when cache is enabled) return (clazz.getClassLoader() instanceof ClassSource); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy