Moving the MethodPublishingInterceptor and @Publisher annotation support to the sandbox for now.

This commit is contained in:
Mark Fisher
2008-10-20 17:55:36 +00:00
parent 7ffab20b45
commit 6e4b501ec6
12 changed files with 0 additions and 850 deletions

View File

@@ -1,91 +0,0 @@
/*
* Copyright 2002-2008 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.aop;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.support.AopUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.integration.channel.ChannelResolver;
import org.springframework.integration.core.MessageChannel;
import org.springframework.util.Assert;
/**
* {@link MessagePublishingInterceptor} that resolves the channel from the
* publisher annotation of the invoked method.
*
* @author Mark Fisher
*/
public class AnnotationAwareMessagePublishingInterceptor extends MessagePublishingInterceptor {
private Class<? extends Annotation> publisherAnnotationType;
private String channelAttributeName;
private ChannelResolver channelResolver;
public AnnotationAwareMessagePublishingInterceptor(Class<? extends Annotation> publisherAnnotationType,
String channelAttributeName, ChannelResolver channelResolver) {
Assert.notNull(publisherAnnotationType, "'publisherAnnotationType' must not be null");
Assert.notNull(channelAttributeName, "'channelAttributeName' must not be null");
Assert.notNull(channelResolver, "'channelResolver' must not be null");
this.publisherAnnotationType = publisherAnnotationType;
this.channelAttributeName = channelAttributeName;
this.channelResolver = channelResolver;
}
@Override
protected MessageChannel resolveChannel(MethodInvocation invocation) {
String channelName = this.extractAnnotationValue(invocation, this.channelAttributeName, String.class);
if (channelName != null) {
MessageChannel channel = this.channelResolver.resolveChannelName(channelName);
if (channel != null) {
return channel;
}
}
return super.resolveChannel(invocation);
}
@Override
protected PayloadType determinePayloadType(MethodInvocation invocation) {
PayloadType payloadType = this.extractAnnotationValue(invocation, "payloadType", PayloadType.class);
if (payloadType != null) {
return payloadType;
}
return super.determinePayloadType(invocation);
}
@SuppressWarnings("unchecked")
private <T> T extractAnnotationValue(MethodInvocation invocation, String attributeName, Class<T> type) {
Class<?> targetClass = AopUtils.getTargetClass(invocation.getThis());
Method method = AopUtils.getMostSpecificMethod(invocation.getMethod(), targetClass);
Annotation annotation = AnnotationUtils.getAnnotation(method, this.publisherAnnotationType);
if (annotation != null) {
Object value = AnnotationUtils.getValue(annotation, attributeName);
if (value != null && type.isAssignableFrom(value.getClass())) {
return (T) value;
}
}
return null;
}
}

View File

@@ -1,106 +0,0 @@
/*
* Copyright 2002-2008 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.aop;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.integration.channel.MessageChannelTemplate;
import org.springframework.integration.core.Message;
import org.springframework.integration.core.MessageChannel;
import org.springframework.integration.message.MessageBuilder;
import org.springframework.util.Assert;
/**
* Interceptor that publishes a target method's return value to a channel.
*
* @author Mark Fisher
*/
public class MessagePublishingInterceptor implements MethodInterceptor {
public static enum PayloadType { RETURN_VALUE, ARGUMENTS, EXCEPTION };
protected final Log logger = LogFactory.getLog(getClass());
private volatile MessageChannel outputChannel;
private final MessageChannelTemplate channelTemplate = new MessageChannelTemplate();
private volatile PayloadType payloadType = PayloadType.RETURN_VALUE;
public void setOutputChannel(MessageChannel outputChannel) {
this.outputChannel = outputChannel;
}
public void setPayloadType(PayloadType payloadType) {
Assert.notNull(payloadType, "'payloadType' must not be null");
this.payloadType = payloadType;
}
/**
* Invoke the target method and publish its return value.
*/
public Object invoke(MethodInvocation invocation) throws Throwable {
PayloadType payloadType = this.determinePayloadType(invocation);
if (payloadType.equals(PayloadType.ARGUMENTS)) {
this.sendMessage(invocation.getArguments(), invocation);
}
Object retval = null;
Throwable throwable = null;
try {
retval = invocation.proceed();
return retval;
}
catch (Throwable t) {
throwable = t;
throw t;
}
finally {
if (payloadType.equals(PayloadType.RETURN_VALUE)) {
this.sendMessage(retval, invocation);
}
else if (payloadType.equals(PayloadType.EXCEPTION)) {
this.sendMessage(throwable, invocation);
}
}
}
private void sendMessage(Object payload, MethodInvocation invocation) {
if (payload != null) {
Message<?> message = (payload instanceof Message)
? (Message<?>) payload
: MessageBuilder.withPayload(payload).build();
this.channelTemplate.send(message, this.resolveChannel(invocation));
}
}
/**
* Subclasses may override this method to provide custom behavior.
*/
protected MessageChannel resolveChannel(MethodInvocation invocation) {
return this.outputChannel;
}
protected PayloadType determinePayloadType(MethodInvocation invocation) {
return this.payloadType;
}
}

View File

@@ -1,52 +0,0 @@
/*
* Copyright 2002-2008 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.aop;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Indicates that the method's return value, thrown exception, or
* invocation arguments should be published to the specified
* channel. The value will only be published if non-null.
* The default payload type is <em>return value</em>.
*
* @author Mark Fisher
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Publisher {
/**
* name of the channel where Messages should be sent
*/
String channel();
/**
* type of payload to intercept and send
* @see MessagePublishingInterceptor.PayloadType
*/
MessagePublishingInterceptor.PayloadType payloadType()
default MessagePublishingInterceptor.PayloadType.RETURN_VALUE;
}

View File

@@ -1,68 +0,0 @@
/*
* Copyright 2002-2008 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.aop;
import java.lang.annotation.Annotation;
import org.aopalliance.aop.Advice;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.AbstractPointcutAdvisor;
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
import org.springframework.integration.channel.ChannelResolver;
import org.springframework.util.Assert;
/**
* Advisor whose pointcut matches a method annotation and whose advice will
* publish a message to the channel provided by that annotation.
*
* @author Mark Fisher
* @see Publisher
* @see AnnotationAwareMessagePublishingInterceptor
*/
@SuppressWarnings("serial")
public class PublisherAnnotationAdvisor extends AbstractPointcutAdvisor {
private AnnotationAwareMessagePublishingInterceptor advice;
private AnnotationMatchingPointcut pointcut;
public PublisherAnnotationAdvisor(ChannelResolver channelResolver) {
this(Publisher.class, "channel", channelResolver);
}
public PublisherAnnotationAdvisor(Class<? extends Annotation> publisherAnnotationType,
String channelNameAttribute, ChannelResolver channelResolver) {
Assert.notNull(publisherAnnotationType, "'publisherAnnotationType' must not be null");
Assert.notNull(channelNameAttribute, "'channelNameAttribute' must not be null");
Assert.notNull(channelResolver, "'channelResolver' must not be null");
this.pointcut = AnnotationMatchingPointcut.forMethodAnnotation(publisherAnnotationType);
this.advice = new AnnotationAwareMessagePublishingInterceptor(
publisherAnnotationType, channelNameAttribute, channelResolver);
}
public Pointcut getPointcut() {
return this.pointcut;
}
public Advice getAdvice() {
return this.advice;
}
}

View File

@@ -1,137 +0,0 @@
/*
* Copyright 2002-2008 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.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.concurrent.atomic.AtomicBoolean;
import org.springframework.aop.Advisor;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.integration.aop.Publisher;
import org.springframework.integration.aop.PublisherAnnotationAdvisor;
import org.springframework.integration.channel.BeanFactoryChannelResolver;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
/**
* A {@link BeanPostProcessor} that adds a message publishing interceptor when
* it discovers annotated methods.
*
* @author Mark Fisher
*/
public class PublisherAnnotationPostProcessor implements BeanPostProcessor, BeanFactoryAware, BeanClassLoaderAware {
private volatile Class<? extends Annotation> publisherAnnotationType = Publisher.class;
private volatile String channelNameAttribute = "channel";
private volatile Advisor advisor;
private volatile BeanFactory beanFactory;
private volatile ClassLoader beanClassLoader;
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
public void setBeanClassLoader(ClassLoader beanClassLoader) {
Assert.notNull(beanClassLoader, "'beanClassLoader' must not be null");
this.beanClassLoader = beanClassLoader;
}
public void setPublisherAnnotationType(Class<? extends Annotation> publisherAnnotationType) {
Assert.notNull(publisherAnnotationType, "'publisherAnnotationType' must not be null");
this.publisherAnnotationType = publisherAnnotationType;
}
public void setChannelNameAttribute(String channelNameAttribute) {
Assert.notNull(channelNameAttribute, "'channelNameAttribute' must not be null");
this.channelNameAttribute = channelNameAttribute;
}
private void createAdvisor() {
Assert.state(this.beanFactory != null, "BeanFactory is required");
this.advisor = new PublisherAnnotationAdvisor(this.publisherAnnotationType, this.channelNameAttribute,
new BeanFactoryChannelResolver(this.beanFactory));
}
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
Class<?> targetClass = AopUtils.getTargetClass(bean);
if (targetClass == null) {
return bean;
}
if (this.advisor == null) {
this.createAdvisor();
}
if (AopUtils.canApply(this.advisor, targetClass)) {
if (bean instanceof Advised) {
((Advised) bean).addAdvisor(this.advisor);
return bean;
}
else {
ProxyFactory pf = new ProxyFactory(bean);
pf.setProxyTargetClass(this.requiresClassProxying(targetClass));
pf.addAdvisor(this.advisor);
return pf.getProxy(this.beanClassLoader);
}
}
else {
return bean;
}
}
private boolean requiresClassProxying(Class<?> targetClass) {
final AtomicBoolean result = new AtomicBoolean(false);
final Class<?>[] interfaces = targetClass.getInterfaces();
ReflectionUtils.doWithMethods(targetClass, new ReflectionUtils.MethodCallback() {
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
Annotation annotation = AnnotationUtils.findAnnotation(method, publisherAnnotationType);
if (annotation != null) {
boolean foundMethodOnInterface = false;
for (Class<?> iface : interfaces) {
Method ifaceMethod = ReflectionUtils.findMethod(
iface, method.getName(), method.getParameterTypes());
if (ifaceMethod != null) {
foundMethodOnInterface = true;
break;
}
}
if (!foundMethodOnInterface) {
result.set(true);
}
}
}
});
return result.get();
}
}

View File

@@ -40,7 +40,6 @@ import org.springframework.integration.bus.MessageBusAwareBeanPostProcessor;
import org.springframework.integration.channel.MessagePublishingErrorHandler;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.config.annotation.MessagingAnnotationPostProcessor;
import org.springframework.integration.config.annotation.PublisherAnnotationPostProcessor;
import org.springframework.integration.scheduling.SimpleTaskScheduler;
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@@ -60,9 +59,6 @@ public class MessageBusParser extends AbstractSimpleBeanDefinitionParser {
public static final String MESSAGE_BUS_AWARE_POST_PROCESSOR_BEAN_NAME =
"internal.MessageBusAwareBeanPostProcessor";
private static final String PUBLISHER_ANNOTATION_POST_PROCESSOR_BEAN_NAME =
"internal.PublisherAnnotationPostProcessor";
private static final String MESSAGING_ANNOTATION_POST_PROCESSOR_BEAN_NAME =
"internal.MessagingAnnotationPostProcessor";
@@ -154,7 +150,6 @@ public class MessageBusParser extends AbstractSimpleBeanDefinitionParser {
private void addPostProcessors(Element element, ParserContext parserContext) {
this.registerMessageBusAwarePostProcessor(parserContext);
if ("true".equals(element.getAttribute("enable-annotations").toLowerCase())) {
this.registerPublisherPostProcessor(parserContext);
this.registerMessagingAnnotationPostProcessor(parserContext);
}
}
@@ -166,12 +161,6 @@ public class MessageBusParser extends AbstractSimpleBeanDefinitionParser {
parserContext.getRegistry().registerBeanDefinition(MESSAGE_BUS_AWARE_POST_PROCESSOR_BEAN_NAME, builder.getBeanDefinition());
}
private void registerPublisherPostProcessor(ParserContext parserContext) {
BeanDefinition bd = new RootBeanDefinition(PublisherAnnotationPostProcessor.class);
BeanComponentDefinition bcd = new BeanComponentDefinition(bd, PUBLISHER_ANNOTATION_POST_PROCESSOR_BEAN_NAME);
parserContext.registerBeanComponent(bcd);
}
private void registerMessagingAnnotationPostProcessor(ParserContext parserContext) {
BeanDefinition bd = new RootBeanDefinition(MessagingAnnotationPostProcessor.class);
BeanComponentDefinition bcd = new BeanComponentDefinition(