001/*
002 * Copyright 2002-2017 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.context.event;
018
019import java.lang.reflect.Constructor;
020
021import org.aopalliance.intercept.MethodInterceptor;
022import org.aopalliance.intercept.MethodInvocation;
023
024import org.springframework.beans.factory.InitializingBean;
025import org.springframework.context.ApplicationEvent;
026import org.springframework.context.ApplicationEventPublisher;
027import org.springframework.context.ApplicationEventPublisherAware;
028import org.springframework.lang.Nullable;
029import org.springframework.util.Assert;
030
031/**
032 * {@link MethodInterceptor Interceptor} that publishes an
033 * {@code ApplicationEvent} to all {@code ApplicationListeners}
034 * registered with an {@code ApplicationEventPublisher} after each
035 * <i>successful</i> method invocation.
036 *
037 * <p>Note that this interceptor is only capable of publishing <i>stateless</i>
038 * events configured via the
039 * {@link #setApplicationEventClass "applicationEventClass"} property.
040 *
041 * @author Dmitriy Kopylenko
042 * @author Juergen Hoeller
043 * @author Rick Evans
044 * @see #setApplicationEventClass
045 * @see org.springframework.context.ApplicationEvent
046 * @see org.springframework.context.ApplicationListener
047 * @see org.springframework.context.ApplicationEventPublisher
048 * @see org.springframework.context.ApplicationContext
049 */
050public class EventPublicationInterceptor
051                implements MethodInterceptor, ApplicationEventPublisherAware, InitializingBean {
052
053        @Nullable
054        private Constructor<?> applicationEventClassConstructor;
055
056        @Nullable
057        private ApplicationEventPublisher applicationEventPublisher;
058
059
060        /**
061         * Set the application event class to publish.
062         * <p>The event class <b>must</b> have a constructor with a single
063         * {@code Object} argument for the event source. The interceptor
064         * will pass in the invoked object.
065         * @throws IllegalArgumentException if the supplied {@code Class} is
066         * {@code null} or if it is not an {@code ApplicationEvent} subclass or
067         * if it does not expose a constructor that takes a single {@code Object} argument
068         */
069        public void setApplicationEventClass(Class<?> applicationEventClass) {
070                if (ApplicationEvent.class == applicationEventClass ||
071                                !ApplicationEvent.class.isAssignableFrom(applicationEventClass)) {
072                        throw new IllegalArgumentException("'applicationEventClass' needs to extend ApplicationEvent");
073                }
074                try {
075                        this.applicationEventClassConstructor = applicationEventClass.getConstructor(Object.class);
076                }
077                catch (NoSuchMethodException ex) {
078                        throw new IllegalArgumentException("ApplicationEvent class [" +
079                                        applicationEventClass.getName() + "] does not have the required Object constructor: " + ex);
080                }
081        }
082
083        @Override
084        public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
085                this.applicationEventPublisher = applicationEventPublisher;
086        }
087
088        @Override
089        public void afterPropertiesSet() throws Exception {
090                if (this.applicationEventClassConstructor == null) {
091                        throw new IllegalArgumentException("Property 'applicationEventClass' is required");
092                }
093        }
094
095
096        @Override
097        public Object invoke(MethodInvocation invocation) throws Throwable {
098                Object retVal = invocation.proceed();
099
100                Assert.state(this.applicationEventClassConstructor != null, "No ApplicationEvent class set");
101                ApplicationEvent event = (ApplicationEvent)
102                                this.applicationEventClassConstructor.newInstance(invocation.getThis());
103
104                Assert.state(this.applicationEventPublisher != null, "No ApplicationEventPublisher available");
105                this.applicationEventPublisher.publishEvent(event);
106
107                return retVal;
108        }
109
110}