001/*
002 * Copyright 2012-2018 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 *      http://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.boot.web.servlet;
018
019import java.util.Collections;
020import java.util.EventListener;
021import java.util.HashSet;
022import java.util.Set;
023
024import javax.servlet.ServletContext;
025import javax.servlet.ServletContextAttributeListener;
026import javax.servlet.ServletContextListener;
027import javax.servlet.ServletRequestAttributeListener;
028import javax.servlet.ServletRequestListener;
029import javax.servlet.http.HttpSessionAttributeListener;
030import javax.servlet.http.HttpSessionListener;
031
032import org.springframework.util.Assert;
033import org.springframework.util.ClassUtils;
034
035/**
036 * A {@link ServletContextInitializer} to register {@link EventListener}s in a Servlet
037 * 3.0+ container. Similar to the {@link ServletContext#addListener(EventListener)
038 * registration} features provided by {@link ServletContext} but with a Spring Bean
039 * friendly design.
040 *
041 * This bean can be used to register the following types of listener:
042 * <ul>
043 * <li>{@link ServletContextAttributeListener}</li>
044 * <li>{@link ServletRequestListener}</li>
045 * <li>{@link ServletRequestAttributeListener}</li>
046 * <li>{@link HttpSessionAttributeListener}</li>
047 * <li>{@link HttpSessionListener}</li>
048 * <li>{@link ServletContextListener}</li>
049 * </ul>
050 *
051 * @param <T> the type of listener
052 * @author Dave Syer
053 * @author Phillip Webb
054 * @since 1.4.0
055 */
056public class ServletListenerRegistrationBean<T extends EventListener>
057                extends RegistrationBean {
058
059        private static final Set<Class<?>> SUPPORTED_TYPES;
060
061        static {
062                Set<Class<?>> types = new HashSet<>();
063                types.add(ServletContextAttributeListener.class);
064                types.add(ServletRequestListener.class);
065                types.add(ServletRequestAttributeListener.class);
066                types.add(HttpSessionAttributeListener.class);
067                types.add(HttpSessionListener.class);
068                types.add(ServletContextListener.class);
069                SUPPORTED_TYPES = Collections.unmodifiableSet(types);
070        }
071
072        private T listener;
073
074        /**
075         * Create a new {@link ServletListenerRegistrationBean} instance.
076         */
077        public ServletListenerRegistrationBean() {
078        }
079
080        /**
081         * Create a new {@link ServletListenerRegistrationBean} instance.
082         * @param listener the listener to register
083         */
084        public ServletListenerRegistrationBean(T listener) {
085                Assert.notNull(listener, "Listener must not be null");
086                Assert.isTrue(isSupportedType(listener), "Listener is not of a supported type");
087                this.listener = listener;
088        }
089
090        /**
091         * Set the listener that will be registered.
092         * @param listener the listener to register
093         */
094        public void setListener(T listener) {
095                Assert.notNull(listener, "Listener must not be null");
096                Assert.isTrue(isSupportedType(listener), "Listener is not of a supported type");
097                this.listener = listener;
098        }
099
100        /**
101         * Return the listener to be registered.
102         * @return the listener to be registered
103         */
104        public T getListener() {
105                return this.listener;
106        }
107
108        @Override
109        protected String getDescription() {
110                Assert.notNull(this.listener, "Listener must not be null");
111                return "listener " + this.listener;
112        }
113
114        @Override
115        protected void register(String description, ServletContext servletContext) {
116                try {
117                        servletContext.addListener(this.listener);
118                }
119                catch (RuntimeException ex) {
120                        throw new IllegalStateException(
121                                        "Failed to add listener '" + this.listener + "' to servlet context",
122                                        ex);
123                }
124        }
125
126        /**
127         * Returns {@code true} if the specified listener is one of the supported types.
128         * @param listener the listener to test
129         * @return if the listener is of a supported type
130         */
131        public static boolean isSupportedType(EventListener listener) {
132                for (Class<?> type : SUPPORTED_TYPES) {
133                        if (ClassUtils.isAssignableValue(type, listener)) {
134                                return true;
135                        }
136                }
137                return false;
138        }
139
140        /**
141         * Return the supported types for this registration.
142         * @return the supported types
143         */
144        public static Set<Class<?>> getSupportedTypes() {
145                return SUPPORTED_TYPES;
146        }
147
148}