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

io.smallrye.config.ConfigMappingLoader Maven / Gradle / Ivy

package io.smallrye.config;

import java.lang.invoke.MethodHandles;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.ArrayList;
import java.util.List;

import io.smallrye.common.classloader.ClassDefiner;

public final class ConfigMappingLoader {
    private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();

    private static final ClassValue CACHE = new ClassValue() {
        @Override
        protected ConfigMappingObjectHolder computeValue(final Class type) {
            return new ConfigMappingObjectHolder(getImplementationClass(type));
        }
    };

    public static List getConfigMappingsMetadata(Class type) {
        final List mappings = new ArrayList<>();
        final ConfigMappingInterface configurationInterface = ConfigMappingInterface.getConfigurationInterface(type);
        if (configurationInterface != null) {
            mappings.add(configurationInterface);
            mappings.addAll(configurationInterface.getNested());
        }
        final ConfigMappingClass configMappingClass = ConfigMappingClass.getConfigurationClass(type);
        if (configMappingClass != null) {
            mappings.add(configMappingClass);
            mappings.addAll(getConfigMappingsMetadata(getConfigMappingInterface(type).getInterfaceType()));
        }
        return mappings;
    }

    static ConfigMappingInterface getConfigMappingInterface(final Class type) {
        return ConfigMappingInterface.getConfigurationInterface(getConfigMappingClass(type));
    }

    static Class getConfigMappingClass(final Class type) {
        final ConfigMappingClass configMappingClass = ConfigMappingClass.getConfigurationClass(type);
        if (configMappingClass == null) {
            return type;
        } else {
            return loadClass(type, configMappingClass);
        }
    }

    static  T configMappingObject(Class interfaceType, ConfigMappingContext configMappingContext) {
        ConfigMappingObject instance;
        try {
            Constructor constructor = CACHE.get(interfaceType).getImplementationClass()
                    .getDeclaredConstructor(ConfigMappingContext.class);
            instance = constructor.newInstance(configMappingContext);
        } catch (NoSuchMethodException e) {
            throw new NoSuchMethodError(e.getMessage());
        } catch (InstantiationException e) {
            throw new InstantiationError(e.getMessage());
        } catch (IllegalAccessException e) {
            throw new IllegalAccessError(e.getMessage());
        } catch (InvocationTargetException e) {
            try {
                throw e.getCause();
            } catch (RuntimeException | Error e2) {
                throw e2;
            } catch (Throwable t) {
                throw new UndeclaredThrowableException(t);
            }
        }
        return interfaceType.cast(instance);
    }

    @SuppressWarnings("unchecked")
    static  Class getImplementationClass(Class type) {
        final ConfigMappingMetadata mappingMetadata = ConfigMappingInterface.getConfigurationInterface(type);
        return (Class) loadClass(type, mappingMetadata);
    }

    static Class loadClass(final Class parent, final ConfigMappingMetadata configMappingMetadata) {
        // Check if the interface implementation was already loaded. If not we will load it.
        try {
            return parent.getClassLoader().loadClass(configMappingMetadata.getClassName());
        } catch (ClassNotFoundException e) {
            return defineClass(parent, configMappingMetadata.getClassName(), configMappingMetadata.getClassBytes());
        }
    }

    /**
     * Do not remove this method or inline it. It is keep separate on purpose, so it is easier to substitute it with
     * the GraalVM API for native image compilation.
     *
     * We cannot keep dynamic references to LOOKUP, so this method may be replaced. This is not a problem, since for
     * native image we can generate the mapping class bytes in the binary so we don't need to dynamically load them.
     */
    private static Class defineClass(final Class parent, final String className, final byte[] classBytes) {
        return ClassDefiner.defineClass(LOOKUP, parent, className, classBytes);
    }

    private static final class ConfigMappingObjectHolder {
        private final Class implementationClass;

        ConfigMappingObjectHolder(final Class implementationClass) {
            this.implementationClass = implementationClass;
        }

        public Class getImplementationClass() {
            return implementationClass;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy