package eu.codearte.resteeth.core; import eu.codearte.resteeth.annotation.LogScope; import eu.codearte.resteeth.annotation.RestClient; import eu.codearte.resteeth.endpoint.EndpointProvider; import eu.codearte.resteeth.handlers.RestInvocationHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.aop.framework.ProxyFactory; import org.springframework.core.OrderComparator; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.client.RestTemplate; import java.lang.annotation.Annotation; import java.lang.invoke.MethodHandles; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @author Jakub Kubrynski * @author Tomasz Nurkiewicz */ public class BeanProxyCreator { private final static Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); private final RestTemplate restTemplate; private final MetadataExtractor metadataExtractor = new MetadataExtractor(); private final List<RestInvocationHandler> handlers; public BeanProxyCreator(RestTemplate restTemplate, Collection<RestInvocationHandler> handlers) { this.restTemplate = restTemplate; this.handlers = new ArrayList<>(handlers); OrderComparator.sort(this.handlers); LOG.debug("Custom handlers: {}", this.handlers); } public Object createProxyBean(Class<?> beanClass, EndpointProvider endpointProvider, AnnotationAttributes enableResteethAttributes, List<Annotation> restClientAnnotations) { LOG.info("Creating Resteeth bean for interface {}", beanClass.getCanonicalName()); final RestInvocationInterceptor interceptor = buildInvocationHandler(beanClass, endpointProvider, enableResteethAttributes, restClientAnnotations); return buildProxy(beanClass, interceptor); } private RestInvocationInterceptor buildInvocationHandler(Class<?> beanClass, EndpointProvider endpointProvider, AnnotationAttributes enableResteethAttributes, List<Annotation> restClientAnnotations) { final Map<Method, MethodMetadata> methodMetadataMap = extractInterfaceInformation(beanClass, enableResteethAttributes, restClientAnnotations); final List<RestInvocationHandler> handlersList = prepareHandlersList(endpointProvider); return new RestInvocationInterceptor(methodMetadataMap, handlersList); } private List<RestInvocationHandler> prepareHandlersList(EndpointProvider endpointProvider) { final List<RestInvocationHandler> handlersList = new ArrayList<>(this.handlers); handlersList.add(new RestTemplateInvoker(restTemplate, endpointProvider)); return handlersList; } private Object buildProxy(Class<?> beanClass, RestInvocationInterceptor invocation) { ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.addInterface(beanClass); proxyFactory.addAdvice(invocation); return proxyFactory.getProxy(); } private Map<Method, MethodMetadata> extractInterfaceInformation(Class<?> beanClass, AnnotationAttributes enableResteethAttributes, List<Annotation> restClientAnnotations) { Map<Method, MethodMetadata> methodMetadataMap = new HashMap<>(); RequestMapping controllerRequestMapping = beanClass.getAnnotation(RequestMapping.class); ResteethAnnotationMetadata annotationMetadata = mergeAnnotations(enableResteethAttributes, restClientAnnotations, Arrays.asList(beanClass.getAnnotations())); for (Method method : beanClass.getMethods()) { methodMetadataMap.put(method, metadataExtractor.extractMethodMetadata(method, controllerRequestMapping, annotationMetadata)); } return methodMetadataMap; } private ResteethAnnotationMetadata mergeAnnotations(AnnotationAttributes enableResteethAttributes, List<Annotation> restClientAnnotations, List<Annotation> interfaceAnnotations) { return new ResteethAnnotationMetadata(enableResteethAttributes, restClientAnnotations, interfaceAnnotations); } }