1 package com.atlassian.johnson.spring.web.servlet.support;
2
3 import com.atlassian.johnson.context.JohnsonContextListener;
4 import com.atlassian.johnson.spring.web.context.JohnsonContextLoaderListener;
5 import com.atlassian.johnson.spring.web.servlet.JohnsonDispatcherServlet;
6 import org.springframework.util.Assert;
7 import org.springframework.util.ObjectUtils;
8 import org.springframework.web.context.ContextLoaderListener;
9 import org.springframework.web.context.WebApplicationContext;
10 import org.springframework.web.servlet.DispatcherServlet;
11 import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;
12
13 import javax.servlet.Filter;
14 import javax.servlet.ServletContext;
15 import javax.servlet.ServletException;
16 import javax.servlet.ServletRegistration;
17
18 /**
19 * Extends Spring's {@code AbstractDispatcherServletInitializer} to use Johnson-aware components.
20 * <ul>
21 * <li>A {@link JohnsonContextListener} will be registered <i>before</i> any other listeners that are registered
22 * by this initializer</li>
23 * <li>A {@link JohnsonContextLoaderListener} will be used to initialize the {@link #createRootApplicationContext()
24 * root ApplicationContext}, if one is created</li>
25 * <li>A {@link JohnsonDispatcherServlet} will be used to initialize the {@link #createServletApplicationContext()
26 * servlet ApplicationContext}</li>
27 * </ul>
28 * <p>
29 * In addition to using Johnson-aware components by default, this base class allows derived initializers to override
30 * {@link #createContextLoaderListener(WebApplicationContext) the ContextLoaderListener} and
31 * {@link #createDispatcherServlet(WebApplicationContext) DispatcherServlet} types used. This is intended to allow
32 * for application-specific handling on top of the Johnson-aware handling.
33 *
34 * @since 3.0
35 */
36 public abstract class AbstractJohnsonDispatcherServletInitializer extends AbstractDispatcherServletInitializer {
37
38 /**
39 * Creates a {@link JohnsonContextLoaderListener} which will initialize and terminate the provided
40 * {@code WebApplicationContext}. This method is provided as a convenience for derived classes to
41 * simplify replacing the listener used.
42 *
43 * @param context the {@code WebApplicationContext} to be initialized by the created listener
44 * @return the listener to register with the {@code ServletContext}
45 */
46 protected ContextLoaderListener createContextLoaderListener(WebApplicationContext context) {
47 return new JohnsonContextLoaderListener(context);
48 }
49
50 /**
51 * Creates a {@link JohnsonDispatcherServlet}, which will initialize the SpringMVC context in a Johnson-aware
52 * away. If SpringMVC initialization fails, the application will be locked.
53 *
54 * @param context the {@code WebApplicationContext} to be initialized by the created dispatcher
55 * @return the servlet to register with the {@code ServletContext}
56 */
57 protected DispatcherServlet createDispatcherServlet(WebApplicationContext context) {
58 return new JohnsonDispatcherServlet(context);
59 }
60
61 /**
62 * {@link #registerJohnsonContextListener(ServletContext) Registers a} {@link JohnsonContextListener} and then
63 * delegates to the superclass's {@code onStartup(ServletContext)} implementation.
64 *
65 * @param servletContext the {@code ServletContext} to initialize
66 * @throws ServletException potentially thrown by the superclass {@code onStartup(ServletContext)} implementation
67 */
68 @Override
69 public void onStartup(ServletContext servletContext) throws ServletException {
70 registerJohnsonContextListener(servletContext);
71
72 super.onStartup(servletContext);
73 }
74
75 /**
76 * Overrides {@code AbstractContextLoaderListener}'s {@code registerContextLoaderListener} to register a
77 * {@link #createContextLoaderListener(WebApplicationContext) JohnsonContextLoaderListener} instead of the
78 * standard Spring {@code ContextLoaderListener}.
79 *
80 * @param servletContext the {@code ServletContext} to register the {@link JohnsonContextLoaderListener} in
81 */
82 @Override
83 protected void registerContextLoaderListener(ServletContext servletContext) {
84 WebApplicationContext context = createRootApplicationContext();
85 if (context == null) {
86 logger.debug("No ContextLoaderListener registered, as createRootApplicationContext() did not return an application context");
87 } else {
88 servletContext.addListener(createContextLoaderListener(context));
89 }
90 }
91
92 /**
93 * Overrides {@code AbstractDispatcherServletInitializer}'s {@code registerDispatcherServlet(ServletContext)} to
94 * register a {@link #createDispatcherServlet(WebApplicationContext) JohnsonDispatcherServlet} instead of the
95 * standard Spring {@code DispatcherServlet}.
96 *
97 * @param servletContext the {@code ServletContext} to register the {@link JohnsonDispatcherServlet} in
98 */
99 @Override
100 protected void registerDispatcherServlet(ServletContext servletContext) {
101 String servletName = getServletName();
102 Assert.hasLength(servletName, "getServletName() may not return empty or null");
103
104 WebApplicationContext servletAppContext = createServletApplicationContext();
105 Assert.notNull(servletAppContext,
106 "createServletApplicationContext() did not return an application context for servlet ["
107 + servletName + "]"
108 );
109
110 ServletRegistration.Dynamic registration =
111 servletContext.addServlet(servletName, createDispatcherServlet(servletAppContext));
112 Assert.notNull(registration,
113 "Failed to register servlet with name '" + servletName +
114 "'. Check if there is another servlet registered under the same name."
115 );
116
117 registration.setAsyncSupported(isAsyncSupported());
118 registration.setLoadOnStartup(1);
119 registration.addMapping(getServletMappings());
120
121 Filter[] filters = getServletFilters();
122 if (!ObjectUtils.isEmpty(filters)) {
123 for (Filter filter : filters) {
124 registerServletFilter(servletContext, filter);
125 }
126 }
127
128 customizeRegistration(registration);
129 }
130
131 /**
132 * Registers an {@link JohnsonContextListener} in in the provided {@code ServletContext}. This listener ensures
133 * Johnson is initialized and terminated with the application.
134 * <p>
135 * Note: Even if this method is called multiple times, with its default implementation the listener will only be
136 * added <i>once</i>.
137 *
138 * @param servletContext the {@code ServletContext} to register the {@link JohnsonContextListener} in
139 * @see JohnsonContextListener#register(ServletContext)
140 */
141 protected void registerJohnsonContextListener(ServletContext servletContext) {
142 JohnsonContextListener.register(servletContext);
143 }
144 }