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

org.mule.runtime.config.internal.dsl.spring.ObjectFactoryClassRepository Maven / Gradle / Ivy

/*
 * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
 * The software in this package is published under the terms of the CPAL v1.0
 * license, a copy of which has been included with this distribution in the
 * LICENSE.txt file.
 */
package org.mule.runtime.config.internal.dsl.spring;

import static org.springframework.cglib.proxy.Enhancer.registerStaticCallbacks;

import org.mule.runtime.core.internal.util.CompositeClassLoader;
import org.mule.runtime.dsl.api.component.ComponentBuildingDefinition;
import org.mule.runtime.dsl.api.component.ObjectFactory;
import org.mule.runtime.dsl.api.component.ObjectTypeProvider;

import org.springframework.beans.factory.SmartFactoryBean;
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;

import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Supplier;

/**
 * Repository for storing the dynamic class generated to mimic {@link org.springframework.beans.factory.FactoryBean} from an
 * {@link ObjectFactory}. This is done because we need dependency injection and instrospection done over the {@link ObjectFactory}
 * without the user knowing about it.
 * 

* The created {@link org.springframework.beans.factory.FactoryBean} is the one that receives the injection of fields declared by * the {@link ObjectFactory}. It also provides information about the instance type that is creating since it's used to know the * order in which beans must be initialised based on the dependencies between them. * * @since 4.0 */ public class ObjectFactoryClassRepository { /** * Retrieves a {@link Class} for the {@link ObjectFactory} defined by the {@code objectFactoryType} parameter. Once acquired the * {@code Class} instance should not be reused for another {@link ComponentBuildingDefinition}. * * @param componentBuildingDefinition the definition on how to build the component * @param objectFactoryType the {@link ObjectFactory} of the component * @param createdObjectType the type of object created by the {@code ObjectFactory} * @param isLazyInitFunction function that defines if the object created by the component can be created lazily * @param instancePostCreationFunctionOptional function to do custom processing of the created instance by the {@code ObjectFactory}. * When there's no need for post processing this value must be {@link Optional#empty()} * @return the {@code FactoryBean} class to be used by spring for the provided configuration. */ public Class getObjectFactoryClass(ComponentBuildingDefinition componentBuildingDefinition, Class objectFactoryType, Class createdObjectType, Supplier isLazyInitFunction, Optional> instancePostCreationFunctionOptional) { return getObjectFactoryDynamicClass(componentBuildingDefinition, objectFactoryType, createdObjectType, isLazyInitFunction, instancePostCreationFunctionOptional); } private Class getObjectFactoryDynamicClass(final ComponentBuildingDefinition componentBuildingDefinition, final Class objectFactoryType, final Class createdObjectType, final Supplier isLazyInitFunction, final Optional> instancePostCreationFunction) { /* * We need this to allow spring create the object using a FactoryBean but using the object factory setters and getters so we * create as FactoryBean a dynamic class that will have the same attributes and methods as the ObjectFactory that the user * defined. This way our API does not expose spring specific classes. */ Enhancer enhancer = new Enhancer(); // Use SmartFactoryBean since it's the only way to force spring to pre-instantiate FactoryBean for singletons enhancer.setInterfaces(new Class[] {SmartFactoryBean.class}); enhancer.setSuperclass(objectFactoryType); enhancer.setCallbackType(MethodInterceptor.class); if (SmartFactoryBean.class.getClassLoader() != objectFactoryType.getClassLoader()) { // CGLIB needs access to both the spring interface and the extended factory class. // If the factory class is defined in a plugin, its classloader has to be passed. enhancer.setClassLoader(new CompositeClassLoader(ObjectFactoryClassRepository.class.getClassLoader(), objectFactoryType.getClassLoader())); } // The use of the CGLIB cache is turned off when a post creation function is passed as argument in order to // enrich the created proxy with properties. This is only to enable injecting properties in components // from the compatibility module. // Setting this to false will generate an excessive amount of different proxy classes loaded by the container CL // that will end up in Metaspace OOM. enhancer.setUseCache(!instancePostCreationFunction.isPresent()); Class factoryBeanClass = enhancer.createClass(); registerStaticCallbacks(factoryBeanClass, new Callback[] { (MethodInterceptor) (obj, method, args, proxy) -> { if (method.getName().equals("isSingleton")) { return !componentBuildingDefinition.isPrototype(); } if (method.getName().equals("getObjectType") && !ObjectTypeProvider.class.isAssignableFrom(obj.getClass())) { return createdObjectType; } if (method.getName().equals("getObject")) { Object createdInstance = proxy.invokeSuper(obj, args); instancePostCreationFunction.ifPresent(consumer -> consumer.accept(createdInstance)); return createdInstance; } if (method.getName().equals("isPrototype")) { return componentBuildingDefinition.isPrototype(); } if (method.getName().equals("isEagerInit")) { return !isLazyInitFunction.get(); } return proxy.invokeSuper(obj, args); } }); return factoryBeanClass; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy