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

com.ethlo.lamebda.spring.ProjectSetupService Maven / Gradle / Ivy

The newest version!
package com.ethlo.lamebda.spring;

/*-
 * #%L
 * lamebda-spring-web-starter
 * %%
 * Copyright (C) 2018 - 2019 Morten Haraldsen (ethlo)
 * %%
 * 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.
 * #L%
 */

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.stream.Collectors;

import org.aopalliance.intercept.MethodInterceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.annotation.BeanFactoryAspectJAdvisorsBuilder;
import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.aop.target.SingletonTargetSource;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.env.PropertyResolver;
import org.springframework.stereotype.Controller;
import org.springframework.util.ClassUtils;
import org.springframework.util.MimeType;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import com.ethlo.lamebda.HttpMethod;
import com.ethlo.lamebda.ProjectConfiguration;
import com.ethlo.lamebda.lifecycle.ProjectLoadedEvent;
import com.ethlo.lamebda.mapping.RequestMapping;

public class ProjectSetupService implements ApplicationListener
{
    private static final Logger logger = LoggerFactory.getLogger(ProjectSetupService.class);
    private final ListableBeanFactory beanFactory;
    private final List methodInterceptors;

    public ProjectSetupService(final ListableBeanFactory beanFactory, final List methodInterceptors)
    {
        this.beanFactory = beanFactory;
        this.methodInterceptors = methodInterceptors;
    }

    private List register(PropertyResolver propertyResolver, Collection handlerMappings, Object controller, ProjectConfiguration projectConfiguration)
    {
        final Object wrappedController = wrapController(controller);

        final List result = new LinkedList<>();

        final Class userType = ClassUtils.getUserClass(controller.getClass());

        final Map methods = MethodIntrospector.selectMethods(userType, (MethodIntrospector.MetadataLookup) method ->
                {
                    try
                    {
                        return RequestMappingInfoUtil.getMappingForMethod(projectConfiguration, propertyResolver, userType, method);
                    }
                    catch (Throwable ex)
                    {
                        throw new IllegalStateException("Invalid mapping on handler class [" +
                                userType.getName() + "]: " + method, ex);
                    }
                }
        );

        handlerMappings.forEach(handlerMapping -> methods.forEach((method, mapping) -> result.add(doRegister(handlerMapping, wrappedController, method, mapping))));
        return result;
    }

    private Object wrapController(final Object controller)
    {
        final BeanFactoryAspectJAdvisorsBuilder advisorsBuilder = new BeanFactoryAspectJAdvisorsBuilder(beanFactory);
        final List advisors = advisorsBuilder.buildAspectJAdvisors();
        return createAOPProxyWithInterceptorsAndAdvisors(methodInterceptors, advisors, controller);
    }

    private RequestMapping doRegister(final RequestMappingHandlerMapping handlerMapping, final Object object, final Method m, final RequestMappingInfo mappingToUse)
    {

        final Set methods = mappingToUse.getMethodsCondition().getMethods().stream().map(method -> HttpMethod.parse(method.name())).collect(Collectors.toSet());
        final Set patterns = mappingToUse.getPatternValues();
        final Set consumes = mappingToUse.getConsumesCondition().getConsumableMediaTypes().stream().map(MimeType::toString).collect(Collectors.toSet());
        final Set produces = mappingToUse.getProducesCondition().getProducibleMediaTypes().stream().map(MimeType::toString).collect(Collectors.toSet());

        logger.info("Registering {}", mappingToUse);
        handlerMapping.unregisterMapping(mappingToUse);
        handlerMapping.registerMapping(mappingToUse, object, m);

        return new RequestMapping(patterns, methods, consumes, produces);
    }

    private Object createAOPProxyWithInterceptorsAndAdvisors(final List methodInterceptors, final List advisors, Object controller)
    {
        final ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
        proxyFactoryBean.setProxyTargetClass(true);
        proxyFactoryBean.setProxyClassLoader(controller.getClass().getClassLoader());
        proxyFactoryBean.setTargetSource(new SingletonTargetSource(controller));
        proxyFactoryBean.addAdvisors(advisors);
        methodInterceptors.forEach(proxyFactoryBean::addAdvice);
        return proxyFactoryBean.getObject();
    }

    @Override
    public void onApplicationEvent(final ProjectLoadedEvent event)
    {
        final AnnotationConfigApplicationContext projectCtx = event.getProjectContext();
        final ProjectConfiguration projectCfg = event.getProjectConfiguration();

        final List handlerMappings = ProjectCleanupService.getMappingHandler(event);

        // Register controller beans
        final SortedSet allMappings = new TreeSet<>();
        projectCtx.getBeansWithAnnotation(Controller.class).forEach((beanName, controller) ->
        {
            final List mappings = register(projectCtx.getEnvironment(), handlerMappings, controller, projectCfg);
            allMappings.addAll(mappings);
        });

        event.getProjectContext().registerBean("_all_mappings", Set.class, () -> allMappings);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy