1 /*
2  * JBoss, Home of Professional Open Source.
3  * Copyright 2014 Red Hat, Inc., and individual contributors
4  * as indicated by the @author tags.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *  Unless required by applicable law or agreed to in writing, software
13  *  distributed under the License is distributed on an "AS IS" BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License.
17  */

18
19 package io.undertow.servlet.core;
20
21 import io.undertow.servlet.UndertowServletLogger;
22
23 import javax.servlet.ServletContext;
24 import javax.servlet.ServletContextAttributeEvent;
25 import javax.servlet.ServletContextAttributeListener;
26 import javax.servlet.ServletContextEvent;
27 import javax.servlet.ServletContextListener;
28 import javax.servlet.ServletException;
29 import javax.servlet.ServletRequest;
30 import javax.servlet.ServletRequestAttributeEvent;
31 import javax.servlet.ServletRequestAttributeListener;
32 import javax.servlet.ServletRequestEvent;
33 import javax.servlet.ServletRequestListener;
34 import javax.servlet.http.HttpServletRequest;
35 import javax.servlet.http.HttpSession;
36 import javax.servlet.http.HttpSessionAttributeListener;
37 import javax.servlet.http.HttpSessionBindingEvent;
38 import javax.servlet.http.HttpSessionEvent;
39 import javax.servlet.http.HttpSessionIdListener;
40 import javax.servlet.http.HttpSessionListener;
41 import java.util.ArrayList;
42 import java.util.List;
43
44 import static io.undertow.servlet.core.ApplicationListeners.ListenerState.DECLARED_LISTENER;
45 import static io.undertow.servlet.core.ApplicationListeners.ListenerState.PROGRAMATIC_LISTENER;
46
47 /**
48  * Class that is responsible for invoking application listeners.
49  * <p>
50  * This class does not perform any context setup, the context must be setup
51  * before invoking this class.
52  * <p>
53  * Note that arrays are used instead of lists for performance reasons.
54  *
55  * @author Stuart Douglas
56  */

57 public class ApplicationListeners implements Lifecycle {
58
59
60     private static final ManagedListener[] EMPTY = {};
61
62     private static final Class[] LISTENER_CLASSES = {ServletContextListener.class,
63             ServletContextAttributeListener.class,
64             ServletRequestListener.class,
65             ServletRequestAttributeListener.class,
66             javax.servlet.http.HttpSessionListener.class,
67             javax.servlet.http.HttpSessionAttributeListener.class,
68             HttpSessionIdListener.class};
69
70     private static final ThreadLocal<ListenerState> IN_PROGRAMATIC_SC_LISTENER_INVOCATION = new ThreadLocal<ListenerState>() {
71         @Override
72         protected ListenerState initialValue() {
73             return ListenerState.NO_LISTENER;
74         }
75     };
76
77     private ServletContext servletContext;
78     private final List<ManagedListener> allListeners = new ArrayList<>();
79     private ManagedListener[] servletContextListeners;
80     private ManagedListener[] servletContextAttributeListeners;
81     private ManagedListener[] servletRequestListeners;
82     private ManagedListener[] servletRequestAttributeListeners;
83     private ManagedListener[] httpSessionListeners;
84     private ManagedListener[] httpSessionAttributeListeners;
85     private ManagedListener[] httpSessionIdListeners;
86     private volatile boolean started = false;
87
88     public ApplicationListeners(final List<ManagedListener> allListeners, final ServletContext servletContext) {
89         this.servletContext = servletContext;
90         servletContextListeners = EMPTY;
91         servletContextAttributeListeners = EMPTY;
92         servletRequestListeners = EMPTY;
93         servletRequestAttributeListeners = EMPTY;
94         httpSessionListeners = EMPTY;
95         httpSessionAttributeListeners = EMPTY;
96         httpSessionIdListeners = EMPTY;
97         for (final ManagedListener listener : allListeners) {
98             addListener(listener);
99         }
100     }
101
102     public void addListener(final ManagedListener listener) {
103         if (ServletContextListener.class.isAssignableFrom(listener.getListenerInfo().getListenerClass())) {
104             ManagedListener[] old = servletContextListeners;
105             servletContextListeners = new ManagedListener[old.length + 1];
106             System.arraycopy(old, 0, servletContextListeners, 0, old.length);
107             servletContextListeners[old.length] = listener;
108         }
109         if (ServletContextAttributeListener.class.isAssignableFrom(listener.getListenerInfo().getListenerClass())) {
110
111             ManagedListener[] old = servletContextAttributeListeners;
112             servletContextAttributeListeners = new ManagedListener[old.length + 1];
113             System.arraycopy(old, 0, servletContextAttributeListeners, 0, old.length);
114             servletContextAttributeListeners[old.length] = listener;
115         }
116         if (ServletRequestListener.class.isAssignableFrom(listener.getListenerInfo().getListenerClass())) {
117             ManagedListener[] old = servletRequestListeners;
118             servletRequestListeners = new ManagedListener[old.length + 1];
119             System.arraycopy(old, 0, servletRequestListeners, 0, old.length);
120             servletRequestListeners[old.length] = listener;
121         }
122         if (ServletRequestAttributeListener.class.isAssignableFrom(listener.getListenerInfo().getListenerClass())) {
123             ManagedListener[] old = servletRequestAttributeListeners;
124             servletRequestAttributeListeners = new ManagedListener[old.length + 1];
125             System.arraycopy(old, 0, servletRequestAttributeListeners, 0, old.length);
126             servletRequestAttributeListeners[old.length] = listener;
127         }
128         if (HttpSessionListener.class.isAssignableFrom(listener.getListenerInfo().getListenerClass())) {
129             ManagedListener[] old = httpSessionListeners;
130             httpSessionListeners = new ManagedListener[old.length + 1];
131             System.arraycopy(old, 0, httpSessionListeners, 0, old.length);
132             httpSessionListeners[old.length] = listener;
133         }
134         if (HttpSessionAttributeListener.class.isAssignableFrom(listener.getListenerInfo().getListenerClass())) {
135             ManagedListener[] old = httpSessionAttributeListeners;
136             httpSessionAttributeListeners = new ManagedListener[old.length + 1];
137             System.arraycopy(old, 0, httpSessionAttributeListeners, 0, old.length);
138             httpSessionAttributeListeners[old.length] = listener;
139         }
140         if (HttpSessionIdListener.class.isAssignableFrom(listener.getListenerInfo().getListenerClass())) {
141             ManagedListener[] old = httpSessionIdListeners;
142             httpSessionIdListeners = new ManagedListener[old.length + 1];
143             System.arraycopy(old, 0, httpSessionIdListeners, 0, old.length);
144             httpSessionIdListeners[old.length] = listener;
145         }
146         this.allListeners.add(listener);
147         if(started) {
148             try {
149                 listener.start();
150             } catch (ServletException e) {
151                 throw new RuntimeException(e);
152             }
153         }
154     }
155
156     public void start() throws ServletException {
157         started = true;
158         for (ManagedListener listener : allListeners) {
159             listener.start();
160         }
161     }
162
163     public void stop() {
164         if (started) {
165             started = false;
166             for (final ManagedListener listener : allListeners) {
167                 listener.stop();
168             }
169         }
170     }
171
172     @Override
173     public boolean isStarted() {
174         return started;
175     }
176
177     public void contextInitialized() {
178         if(!started) {
179             return;
180         }
181         //new listeners can be added here, so we don't use an iterator
182         final ServletContextEvent event = new ServletContextEvent(servletContext);
183         for (int i = 0; i < servletContextListeners.length; ++i) {
184             ManagedListener listener = servletContextListeners[i];
185             IN_PROGRAMATIC_SC_LISTENER_INVOCATION.set(listener.isProgramatic() ? PROGRAMATIC_LISTENER : DECLARED_LISTENER);
186             try {
187                 this.<ServletContextListener>get(listener).contextInitialized(event);
188             } finally {
189                 IN_PROGRAMATIC_SC_LISTENER_INVOCATION.remove();
190             }
191         }
192     }
193
194     public void contextDestroyed() {
195         if(!started) {
196             return;
197         }
198         final ServletContextEvent event = new ServletContextEvent(servletContext);
199         for (int i = servletContextListeners.length - 1; i >= 0; --i) {
200             ManagedListener listener = servletContextListeners[i];
201             try {
202                 this.<ServletContextListener>get(listener).contextDestroyed(event);
203             } catch (Throwable t) {
204                 UndertowServletLogger.REQUEST_LOGGER.errorInvokingListener("contextDestroyed", listener.getListenerInfo().getListenerClass(), t);
205             }
206         }
207     }
208
209     public void servletContextAttributeAdded(final String name, final Object value) {
210         if(!started) {
211             return;
212         }
213         final ServletContextAttributeEvent sre = new ServletContextAttributeEvent(servletContext, name, value);
214         for (int i = 0; i < servletContextAttributeListeners.length; ++i) {
215             this.<ServletContextAttributeListener>get(servletContextAttributeListeners[i]).attributeAdded(sre);
216         }
217     }
218
219     public void servletContextAttributeRemoved(final String name, final Object value) {
220         if(!started) {
221             return;
222         }
223         final ServletContextAttributeEvent sre = new ServletContextAttributeEvent(servletContext, name, value);
224         for (int i = 0; i < servletContextAttributeListeners.length; ++i) {
225             this.<ServletContextAttributeListener>get(servletContextAttributeListeners[i]).attributeRemoved(sre);
226         }
227     }
228
229     public void servletContextAttributeReplaced(final String name, final Object value) {
230         if(!started) {
231             return;
232         }
233         final ServletContextAttributeEvent sre = new ServletContextAttributeEvent(servletContext, name, value);
234         for (int i = 0; i < servletContextAttributeListeners.length; ++i) {
235             this.<ServletContextAttributeListener>get(servletContextAttributeListeners[i]).attributeReplaced(sre);
236         }
237     }
238
239     public void requestInitialized(final ServletRequest request) {
240         if(!started) {
241             return;
242         }
243         if(servletRequestListeners.length > 0) {
244             int i = 0;
245             final ServletRequestEvent sre = new ServletRequestEvent(servletContext, request);
246             try {
247                 for (; i < servletRequestListeners.length; ++i) {
248                     this.<ServletRequestListener>get(servletRequestListeners[i]).requestInitialized(sre);
249                 }
250             } catch (RuntimeException e) {
251                 UndertowServletLogger.REQUEST_LOGGER.errorInvokingListener("requestInitialized", servletRequestListeners[i].getListenerInfo().getListenerClass(), e);
252                 for (; i >= 0; i--) {
253                     try {
254                         this.<ServletRequestListener>get(servletRequestListeners[i]).requestDestroyed(sre);
255                     } catch (Throwable t) {
256                         UndertowServletLogger.REQUEST_LOGGER.errorInvokingListener("requestDestroyed", servletRequestListeners[i].getListenerInfo().getListenerClass(), e);
257                     }
258                 }
259                 throw e;
260             }
261         }
262     }
263
264     public void requestDestroyed(final ServletRequest request) {
265         if(!started) {
266             return;
267         }
268         if(servletRequestListeners.length > 0) {
269             final ServletRequestEvent sre = new ServletRequestEvent(servletContext, request);
270             for (int i = servletRequestListeners.length - 1; i >= 0; --i) {
271                 ManagedListener listener = servletRequestListeners[i];
272                 try {
273                     this.<ServletRequestListener>get(listener).requestDestroyed(sre);
274                 } catch (Exception e) {
275                     UndertowServletLogger.REQUEST_LOGGER.errorInvokingListener("requestDestroyed", listener.getListenerInfo().getListenerClass(), e);
276                 }
277             }
278         }
279     }
280
281     public void servletRequestAttributeAdded(final HttpServletRequest request, final String name, final Object value) {
282         if(!started) {
283             return;
284         }
285         final ServletRequestAttributeEvent sre = new ServletRequestAttributeEvent(servletContext, request, name, value);
286         for (int i = 0; i < servletRequestAttributeListeners.length; ++i) {
287             this.<ServletRequestAttributeListener>get(servletRequestAttributeListeners[i]).attributeAdded(sre);
288         }
289     }
290
291     public void servletRequestAttributeRemoved(final HttpServletRequest request, final String name, final Object value) {
292         if(!started) {
293             return;
294         }
295         final ServletRequestAttributeEvent sre = new ServletRequestAttributeEvent(servletContext, request, name, value);
296         for (int i = 0; i < servletRequestAttributeListeners.length; ++i) {
297             this.<ServletRequestAttributeListener>get(servletRequestAttributeListeners[i]).attributeRemoved(sre);
298         }
299     }
300
301     public void servletRequestAttributeReplaced(final HttpServletRequest request, final String name, final Object value) {
302         if(!started) {
303             return;
304         }
305         final ServletRequestAttributeEvent sre = new ServletRequestAttributeEvent(servletContext, request, name, value);
306         for (int i = 0; i < servletRequestAttributeListeners.length; ++i) {
307             this.<ServletRequestAttributeListener>get(servletRequestAttributeListeners[i]).attributeReplaced(sre);
308         }
309     }
310
311     public void sessionCreated(final HttpSession session) {
312         if(!started) {
313             return;
314         }
315         final HttpSessionEvent sre = new HttpSessionEvent(session);
316         for (int i = 0; i < httpSessionListeners.length; ++i) {
317             this.<HttpSessionListener>get(httpSessionListeners[i]).sessionCreated(sre);
318         }
319     }
320
321     public void sessionDestroyed(final HttpSession session) {
322         if(!started) {
323             return;
324         }
325         final HttpSessionEvent sre = new HttpSessionEvent(session);
326         for (int i = httpSessionListeners.length - 1; i >= 0; --i) {
327             ManagedListener listener = httpSessionListeners[i];
328             this.<HttpSessionListener>get(listener).sessionDestroyed(sre);
329         }
330     }
331
332     public void httpSessionAttributeAdded(final HttpSession session, final String name, final Object value) {
333         if(!started) {
334             return;
335         }
336         final HttpSessionBindingEvent sre = new HttpSessionBindingEvent(session, name, value);
337         for (int i = 0; i < httpSessionAttributeListeners.length; ++i) {
338             this.<HttpSessionAttributeListener>get(httpSessionAttributeListeners[i]).attributeAdded(sre);
339         }
340     }
341
342     public void httpSessionAttributeRemoved(final HttpSession session, final String name, final Object value) {
343         if(!started) {
344             return;
345         }
346         final HttpSessionBindingEvent sre = new HttpSessionBindingEvent(session, name, value);
347         for (int i = 0; i < httpSessionAttributeListeners.length; ++i) {
348             this.<HttpSessionAttributeListener>get(httpSessionAttributeListeners[i]).attributeRemoved(sre);
349         }
350     }
351
352     public void httpSessionAttributeReplaced(final HttpSession session, final String name, final Object value) {
353         if(!started) {
354             return;
355         }
356         final HttpSessionBindingEvent sre = new HttpSessionBindingEvent(session, name, value);
357         for (int i = 0; i < httpSessionAttributeListeners.length; ++i) {
358             this.<HttpSessionAttributeListener>get(httpSessionAttributeListeners[i]).attributeReplaced(sre);
359         }
360     }
361
362     public void httpSessionIdChanged(final HttpSession session, final String oldSessionId) {
363         if(!started) {
364             return;
365         }
366         final HttpSessionEvent sre = new HttpSessionEvent(session);
367         for (int i = 0; i < httpSessionIdListeners.length; ++i) {
368             this.<HttpSessionIdListener>get(httpSessionIdListeners[i]).sessionIdChanged(sre, oldSessionId);
369         }
370     }
371
372     private <T> T get(final ManagedListener listener) {
373         return (T) listener.instance();
374     }
375
376     /**
377      * returns true if this is in in a
378      */

379     public static ListenerState listenerState() {
380         return IN_PROGRAMATIC_SC_LISTENER_INVOCATION.get();
381     }
382
383     /**
384      * @param clazz The potential listener class
385      * @return true if the provided class is a valid listener class
386      */

387     public static boolean isListenerClass(final Class<?> clazz) {
388         for (Class c : LISTENER_CLASSES) {
389             if (c.isAssignableFrom(clazz)) {
390                 return true;
391             }
392         }
393         return false;
394     }
395
396     public enum ListenerState {
397         NO_LISTENER,
398         DECLARED_LISTENER,
399         PROGRAMATIC_LISTENER,
400     }
401
402 }
403