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

com.catchpoint.trace.lambda.integrations.spring.SpringLambdaHandlerPlugin Maven / Gradle / Ivy

The newest version!
package com.catchpoint.trace.lambda.integrations.spring;

import com.catchpoint.trace.common.instance.InstanceProvider;
import com.catchpoint.trace.common.instance.InstanceScope;
import com.catchpoint.trace.common.util.ExceptionUtils;

import com.catchpoint.trace.lambda.core.handler.LambdaContext;
import com.catchpoint.trace.lambda.core.handler.LambdaHandler;
import com.catchpoint.trace.lambda.core.handler.LambdaHandlerConfig;
import com.catchpoint.trace.lambda.core.handler.plugin.LambdaHandlerPlugin;
import com.catchpoint.trace.lambda.core.handler.ResponseHolder;
import org.slf4j.Logger;
import com.catchpoint.trace.common.logger.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidatorFactory;
import javax.validation.executable.ExecutableValidator;
import java.lang.reflect.*;
import java.util.*;

/**
 * @author serkan
 */
public class SpringLambdaHandlerPlugin implements LambdaHandlerPlugin {

    private static final Logger LOGGER = LoggerFactory.getLogger(SpringLambdaHandlerPlugin.class);

    private final Class configClass;
    private final String configFile;
    private final SpringLambdaHandlerPluginConfig pluginConfig;

    private SpringContextSupplier contextSupplier;
    private LambdaHandler handler;

    private boolean validate = true;
    private ValidatorFactory validatorFactory;
    private ExecutableValidator executableValidator;
    private Method doHandleRequestMethod;

    private List extensions = new ArrayList<>();

    public SpringLambdaHandlerPlugin(Class configClass) {
        this.configClass = configClass;
        this.configFile = null;
        this.pluginConfig = null;
    }

    public SpringLambdaHandlerPlugin(Class configClass, boolean validate, ValidatorFactory validatorFactory) {
        this.configClass = configClass;
        this.configFile = null;
        this.validate = validate;
        this.validatorFactory = validatorFactory;
        this.pluginConfig = null;
    }

    public SpringLambdaHandlerPlugin(String configFile) {
        this.configClass = null;
        this.configFile = configFile;
        this.pluginConfig = null;
    }

    public SpringLambdaHandlerPlugin(String configFile, boolean validate, ValidatorFactory validatorFactory) {
        this.configClass = null;
        this.configFile = configFile;
        this.validate = validate;
        this.validatorFactory = validatorFactory;
        this.pluginConfig = null;
    }

    public SpringLambdaHandlerPlugin(SpringLambdaHandlerPluginConfig pluginConfig) {
        this.configClass = pluginConfig.configClass();
        this.configFile = pluginConfig.configFile();
        this.pluginConfig = pluginConfig;
        this.validate = pluginConfig.validate();
        if (!pluginConfig.validatorFactory().equals(ValidatorFactory.class)) {
            this.validatorFactory = InstanceProvider.getInstance(pluginConfig.validatorFactory(), InstanceScope.GLOBAL);
        }
    }

    public SpringContextSupplier getSpringContextSupplier() {
        return contextSupplier;
    }

    private ApplicationContext retrieveApplicationContext(boolean createIfNotExist) {
        ApplicationContext applicationContext = null;
        try {
            if (!configClass.equals(Object.class)) {
                applicationContext =
                        createIfNotExist
                                ? SpringContextManager.getOrCreateContext(configClass)
                                : SpringContextManager.getContext(configClass);
            } else if (!configFile.equals("")) {
                applicationContext =
                        createIfNotExist
                            ? SpringContextManager.getOrCreateContext(configFile)
                            : SpringContextManager.getContext(configFile);
            } else {
                throw new IllegalArgumentException("One of the 'configClass' or `configFile` properties must be configured!");
            }
            if (pluginConfig != null) {
                for (Class extensionClass : pluginConfig.extensions()) {
                    SpringLambdaHandlerPluginExtension extension = extensionClass.newInstance();
                    extensions.add(extension);
                }
            }
        } catch (Throwable t) {
            LOGGER.error("Unable to instantiate SpringLambdaHandlerPlugin!", t);
            ExceptionUtils.sneakyThrow(t);
        }
        return applicationContext;
    }

    @Override
    public String name() {
        return "spring";
    }

    @Override
    public void onInit(LambdaHandler handler, LambdaHandlerConfig config) {
        if (this.handler != null) {
            throw new IllegalStateException("Plugin already initiated with handler " + this.handler.toString());
        }

        this.handler = handler;

        ApplicationContext applicationContext = retrieveApplicationContext(true);
        applicationContext.getAutowireCapableBeanFactory().autowireBean(handler);
        contextSupplier = new SyncSpringContextSupplier(applicationContext);

        if (validate) {
            initValidation();
        }

        if (!extensions.isEmpty()) {
            for (SpringLambdaHandlerPluginExtension extension : extensions) {
                extension.onInit(contextSupplier, handler);
            }
        }
    }

    private void initValidation() {
        if (validatorFactory == null) {
            try {
                validatorFactory = (ValidatorFactory) contextSupplier.get().getBean("validator");
            } catch (NoSuchBeanDefinitionException e) {
            }
        } else {
            processValidatorFactory(validatorFactory);
        }
        if (validatorFactory == null) {
            validatorFactory = createDefaultValidatorFactory();
            processValidatorFactory(validatorFactory);
        }
        executableValidator = validatorFactory.getValidator().forExecutables();

        Method[] methods = handler.getClass().getMethods();
        for (Method method : methods) {
            if (method.getName().equals("doHandleRequest") && !method.isBridge()) {
                doHandleRequestMethod = method;
                break;
            }
        }
        if (doHandleRequestMethod == null) {
            throw new IllegalStateException("There is no available 'doHandleRequest' to be used for validation");
        }

        LOGGER.info("Activated validation support");
    }

    private void processValidatorFactory(ValidatorFactory validatorFactory) {
        if (validatorFactory instanceof InitializingBean) {
            try {
                ((InitializingBean) validatorFactory).afterPropertiesSet();
            } catch (Exception e) {
                ExceptionUtils.sneakyThrow(e);
            }
        }
    }

    private ValidatorFactory createDefaultValidatorFactory() {
        return new LocalValidatorFactoryBean() {
            @Override
            public boolean supports(Class clazz) {
                if (!super.supports(clazz)) {
                    return false;
                }
                if (AnnotatedElementUtils.isAnnotated(clazz, Validated.class)) {
                    return true;
                }
                return false;
            }
        };
    }

    private String generateValidationExceptionMessage(String message, Set constraintViolations) {
        StringBuilder sb = new StringBuilder();
        if (constraintViolations != null) {
            for (ConstraintViolation constraintViolation : constraintViolations) {
                if (!sb.toString().isEmpty()) {
                    sb.append("\n");
                }
                sb.append(constraintViolation.getMessage());
            }
        } else if (message != null) {
            sb.append(message);
        }
        return sb.toString();
    }

    private void handleValidation(Req request, LambdaContext lambdaContext) {
        if (validate && executableValidator != null) {
            Set constraintViolations =
                    (Set) ((Object)
                            executableValidator.validateParameters(
                                    handler, doHandleRequestMethod, new Object[]{request, lambdaContext}));
            if (constraintViolations.size() > 0) {
                throw new ConstraintViolationException(
                        generateValidationExceptionMessage(null, constraintViolations),
                        (Set>) ((Object) constraintViolations));
            }
        }
    }

    @Override
    public boolean onRequest(LambdaHandler lambdaHandler, Req request,
                             LambdaContext lambdaContext, ResponseHolder responseHolder) {
        if (!extensions.isEmpty()) {
            for (SpringLambdaHandlerPluginExtension extension : extensions) {
                if (!extension.onRequest(request, lambdaContext)) {
                    return false;
                }
            }
        }

        handleValidation(request, lambdaContext);

        return true;
    }

    @Override
    public void onResponse(LambdaHandler lambdaHandler, Res response, LambdaContext lambdaContext) {
        if (!extensions.isEmpty()) {
            for (SpringLambdaHandlerPluginExtension extension : extensions) {
                extension.onResponse(response, lambdaContext);
            }
        }
    }

    @Override
    public boolean onError(LambdaHandler lambdaHandler, Req request, LambdaContext lambdaContext, Throwable error, ResponseHolder responseHolder) {
        if (!extensions.isEmpty()) {
            for (SpringLambdaHandlerPluginExtension extension : extensions) {
                extension.onError(request, lambdaContext, error);
            }
        }
        return true;
    }

    private static class SyncSpringContextSupplier implements SpringContextSupplier {

        private final ApplicationContext applicationContext;

        public SyncSpringContextSupplier(ApplicationContext applicationContext) {
            this.applicationContext = applicationContext;
        }

        @Override
        public ApplicationContext get() {
            return applicationContext;
        }

        @Override
        public ApplicationContext getNow() {
            return applicationContext;
        }

    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy