/* * Copyright 2014-2017 the original author or authors. * * 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. */ package org.springframework.integration.config; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.springframework.aop.Advisor; import org.springframework.aop.TargetSource; import org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator; import org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor; import org.springframework.aop.support.NameMatchMethodPointcut; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.messaging.MessageHandler; import org.springframework.util.Assert; import org.springframework.util.PatternMatchUtils; /** * The {@link AbstractAutoProxyCreator} implementation that applies {@code IdempotentReceiverInterceptor}s * to {@link MessageHandler}s mapped by their {@code endpoint beanName}. * * @author Artem Bilan * @author Gary Russell * @since 4.1 */ @SuppressWarnings("serial") class IdempotentReceiverAutoProxyCreator extends AbstractAutoProxyCreator { private volatile List<Map<String, String>> idempotentEndpointsMapping; private volatile Map<String, List<String>> idempotentEndpoints; // double check locking requires volatile public void setIdempotentEndpointsMapping(List<Map<String, String>> idempotentEndpointsMapping) { Assert.notEmpty(idempotentEndpointsMapping, "'idempotentEndpointsMapping' must not be empty"); this.idempotentEndpointsMapping = idempotentEndpointsMapping; //NOSONAR (inconsistent sync) } @Override protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource customTargetSource) throws BeansException { initIdempotentEndpointsIfNecessary(); if (MessageHandler.class.isAssignableFrom(beanClass)) { List<Advisor> interceptors = new ArrayList<Advisor>(); for (Map.Entry<String, List<String>> entry : this.idempotentEndpoints.entrySet()) { List<String> mappedNames = entry.getValue(); for (String mappedName : mappedNames) { if (isMatch(mappedName, beanName)) { DefaultBeanFactoryPointcutAdvisor idempotentReceiverInterceptor = new DefaultBeanFactoryPointcutAdvisor(); idempotentReceiverInterceptor.setAdviceBeanName(entry.getKey()); NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut(); pointcut.setMappedName("handleMessage"); idempotentReceiverInterceptor.setPointcut(pointcut); idempotentReceiverInterceptor.setBeanFactory(getBeanFactory()); interceptors.add(idempotentReceiverInterceptor); } } } if (!interceptors.isEmpty()) { return interceptors.toArray(); } } return DO_NOT_PROXY; } private void initIdempotentEndpointsIfNecessary() { if (this.idempotentEndpoints == null) { //NOSONAR (inconsistent sync) synchronized (this) { if (this.idempotentEndpoints == null) { this.idempotentEndpoints = new LinkedHashMap<String, List<String>>(); for (Map<String, String> mapping : this.idempotentEndpointsMapping) { Assert.isTrue(mapping.size() == 1, "The 'idempotentEndpointMapping' must be a SingletonMap"); String interceptor = mapping.keySet().iterator().next(); String endpoint = mapping.values().iterator().next(); Assert.hasText(interceptor, "The 'idempotentReceiverInterceptor' can't be empty String"); Assert.hasText(endpoint, "The 'idempotentReceiverEndpoint' can't be empty String"); List<String> endpoints = this.idempotentEndpoints.get(interceptor); if (endpoints == null) { endpoints = new ArrayList<String>(); this.idempotentEndpoints.put(interceptor, endpoints); } endpoints.add(endpoint); } } } } } private boolean isMatch(String mappedName, String beanName) { boolean matched = PatternMatchUtils.simpleMatch(mappedName, beanName); if (!matched) { BeanFactory beanFactory = getBeanFactory(); if (beanFactory != null) { String[] aliases = beanFactory.getAliases(beanName); for (String alias : aliases) { matched = PatternMatchUtils.simpleMatch(mappedName, alias); if (matched) { break; } } } } return matched; } }