View Javadoc

1   package org.cateproject.controller.context;
2   
3   import java.util.Enumeration;
4   import java.util.Map;
5   import java.util.concurrent.ConcurrentHashMap;
6   
7   import javax.servlet.ServletContext;
8   import javax.servlet.ServletContextEvent;
9   import javax.servlet.ServletContextListener;
10  
11  import org.apache.commons.logging.Log;
12  import org.apache.commons.logging.LogFactory;
13  import org.springframework.beans.BeanUtils;
14  import org.springframework.beans.factory.DisposableBean;
15  import org.springframework.beans.factory.access.BeanFactoryReference;
16  import org.springframework.context.ApplicationContext;
17  import org.springframework.context.ApplicationContextException;
18  import org.springframework.util.ObjectUtils;
19  import org.springframework.web.context.ConfigurableWebApplicationContext;
20  import org.springframework.web.context.ContextLoader;
21  import org.springframework.web.context.WebApplicationContext;
22  
23  public class CateContextLoaderListener  extends ContextLoader implements ServletContextListener {
24  	private static final Log logger = LogFactory.getLog(CateContextLoaderListener.class);
25  	private WebApplicationContext context;
26  	private static volatile WebApplicationContext currentContext;
27  	private static final Map<ClassLoader, WebApplicationContext> currentContextPerThread =
28  		new ConcurrentHashMap<ClassLoader, WebApplicationContext>(1);
29  	private static final String FAILSAFE_APPLICATION_CONTEXT = "/WEB-INF/classes/org/cateproject/web/applicationContext-failsafe.xml";
30  	
31  	private BeanFactoryReference parentContextRef;
32  		
33  	public void contextInitialized(final ServletContextEvent servletContextEvent) {
34  		try {
35  		  logger.info("Attempting to initialize default context");
36  		  initWebApplicationContext(servletContextEvent.getServletContext());
37  	      logger.info("Default context successfully initialized");
38  		} catch(Throwable e) {
39  			logger.warn("Default Context was not initialized successfully: " + e.getLocalizedMessage());
40  			
41  			if(servletContextEvent.getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
42  				closeWebApplicationContext(servletContextEvent.getServletContext());
43  				logger.info("Partially initialized WebApplicationContext closed");
44  			}
45  			// Cleanup the context 
46  			cleanupAttributes(servletContextEvent.getServletContext());
47  			logger.info("Loading failsafe context");
48  			initFailSafeWebApplicationContext(servletContextEvent.getServletContext());
49  		}
50  	  }
51  	
52  	/**
53  	 * Initialize Spring's web application context for the given servlet context,
54  	 * according to the "{@link #CONTEXT_CLASS_PARAM contextClass}" and
55  	 * "{@link #CONFIG_LOCATION_PARAM contextConfigLocation}" context-params.
56  	 * @param servletContext current servlet context
57  	 * @return the new WebApplicationContext
58  	 * @see #CONTEXT_CLASS_PARAM
59  	 * @see #CONFIG_LOCATION_PARAM
60  	 */
61  	@Override
62  	public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
63  		if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
64  			throw new IllegalStateException(
65  					"Cannot initialize context because there is already a root application context present - " +
66  					"check whether you have multiple ContextLoader* definitions in your web.xml!");
67  		}
68  
69  		Log logger = LogFactory.getLog(ContextLoader.class);
70  		servletContext.log("Initializing Spring root WebApplicationContext");
71  		if (logger.isInfoEnabled()) {
72  			logger.info("Root WebApplicationContext: initialization started");
73  		}
74  		long startTime = System.currentTimeMillis();
75  
76  		try {
77  			// Determine parent for root web application context, if any.
78  			ApplicationContext parent = loadParentContext(servletContext);
79  
80  			// Store context in local instance variable, to guarantee that
81  			// it is available on ServletContext shutdown.
82  			this.context = createWebApplicationContext(servletContext, parent);
83  			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
84  
85  			ClassLoader ccl = Thread.currentThread().getContextClassLoader();
86  			if (ccl == ContextLoader.class.getClassLoader()) {
87  				currentContext = this.context;
88  			}
89  			else if (ccl != null) {
90  				currentContextPerThread.put(ccl, this.context);
91  			}
92  
93  			if (logger.isDebugEnabled()) {
94  				logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
95  						WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
96  			}
97  			if (logger.isInfoEnabled()) {
98  				long elapsedTime = System.currentTimeMillis() - startTime;
99  				logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
100 			}
101 
102 			return this.context;
103 		}
104 		catch (RuntimeException ex) {
105 			logger.error("Context initialization failed", ex);
106 			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
107 			throw ex;
108 		}
109 		catch (Error err) {
110 			logger.error("Context initialization failed", err);
111 			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
112 			throw err;
113 		}
114 	}
115 
116 
117 	private WebApplicationContext initFailSafeWebApplicationContext(ServletContext servletContext) {
118 		if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
119 			throw new IllegalStateException(
120 					"Cannot initialize context because there is already a root application context present - " +
121 					"check whether you have multiple ContextLoader* definitions in your web.xml!");
122 		}
123 
124 		Log logger = LogFactory.getLog(ContextLoader.class);
125 		servletContext.log("Initializing Spring root WebApplicationContext");
126 		if (logger.isInfoEnabled()) {
127 			logger.info("Root WebApplicationContext: initialization started");
128 		}
129 		long startTime = System.currentTimeMillis();
130 
131 		try {
132 			// Determine parent for root web application context, if any.
133 			ApplicationContext parent = loadParentContext(servletContext);
134 
135 			// Store context in local instance variable, to guarantee that
136 			// it is available on ServletContext shutdown.
137 			
138 			this.context = createFailSafeWebApplicationContext(servletContext, parent);
139 			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
140 
141 			ClassLoader ccl = Thread.currentThread().getContextClassLoader();
142 			if (ccl == ContextLoader.class.getClassLoader()) {
143 				currentContext = this.context;
144 			}
145 			else if (ccl != null) {
146 				currentContextPerThread.put(ccl, this.context);
147 			}
148 
149 			if (logger.isDebugEnabled()) {
150 				logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
151 						WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
152 			}
153 			if (logger.isInfoEnabled()) {
154 				long elapsedTime = System.currentTimeMillis() - startTime;
155 				logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
156 			}
157 
158 			return this.context;
159 		}
160 		catch (RuntimeException ex) {
161 			logger.error("Context initialization failed", ex);
162 			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
163 			throw ex;
164 		}
165 		catch (Error err) {
166 			logger.error("Context initialization failed", err);
167 			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
168 			throw err;
169 		}
170 
171 	}
172 	
173 	protected WebApplicationContext createFailSafeWebApplicationContext(ServletContext sc, ApplicationContext parent) {
174 		Class<?> contextClass = determineContextClass(sc);
175 		if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
176 			throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
177 					"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
178 		}
179 		ConfigurableWebApplicationContext wac =
180 				(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
181 
182 		// Assign the best possible id value.
183 		if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {
184 			// Servlet <= 2.4: resort to name specified in web.xml, if any.
185 			String servletContextName = sc.getServletContextName();
186 			wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
187 					ObjectUtils.getDisplayString(servletContextName));
188 		}
189 		else {
190 			// Servlet 2.5's getContextPath available!
191 			try {
192 				String contextPath = (String) ServletContext.class.getMethod("getContextPath").invoke(sc);
193 				wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
194 						ObjectUtils.getDisplayString(contextPath));
195 			}
196 			catch (Exception ex) {
197 				throw new IllegalStateException("Failed to invoke Servlet 2.5 getContextPath method", ex);
198 			}
199 		}
200 
201 		wac.setParent(parent);
202 		wac.setServletContext(sc);
203 		
204 		wac.setConfigLocation(FAILSAFE_APPLICATION_CONTEXT);
205 		customizeContext(sc, wac);
206 		wac.refresh();
207 		return wac;
208 	}
209 	
210 	@Override
211 	protected WebApplicationContext createWebApplicationContext(ServletContext sc, ApplicationContext parent) {
212 		Class<?> contextClass = determineContextClass(sc);
213 		if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
214 			throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
215 					"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
216 		}
217 		ConfigurableWebApplicationContext wac =
218 				(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
219 
220 		// Assign the best possible id value.
221 		if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {
222 			// Servlet <= 2.4: resort to name specified in web.xml, if any.
223 			String servletContextName = sc.getServletContextName();
224 			wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
225 					ObjectUtils.getDisplayString(servletContextName));
226 		}
227 		else {
228 			// Servlet 2.5's getContextPath available!
229 			try {
230 				String contextPath = (String) ServletContext.class.getMethod("getContextPath").invoke(sc);
231 				wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
232 						ObjectUtils.getDisplayString(contextPath));
233 			}
234 			catch (Exception ex) {
235 				throw new IllegalStateException("Failed to invoke Servlet 2.5 getContextPath method", ex);
236 			}
237 		}
238 
239 		wac.setParent(parent);
240 		wac.setServletContext(sc);
241 		wac.setConfigLocation(sc.getInitParameter(CONFIG_LOCATION_PARAM));
242 		customizeContext(sc, wac);
243 		wac.refresh();
244 		return wac;
245 	}
246 	
247 	/**
248 	 * Close Spring's web application context for the given servlet context. If
249 	 * the default {@link #loadParentContext(ServletContext)} implementation,
250 	 * which uses ContextSingletonBeanFactoryLocator, has loaded any shared
251 	 * parent context, release one reference to that shared parent context.
252 	 * <p>If overriding {@link #loadParentContext(ServletContext)}, you may have
253 	 * to override this method as well.
254 	 * @param servletContext the ServletContext that the WebApplicationContext runs in
255 	 */
256 	@Override
257 	public void closeWebApplicationContext(ServletContext servletContext) {
258 		servletContext.log("Closing Spring root WebApplicationContext");
259 		try {
260 			if (this.context instanceof ConfigurableWebApplicationContext) {
261 				((ConfigurableWebApplicationContext) this.context).close();
262 			}
263 		}
264 		finally {
265 			ClassLoader ccl = Thread.currentThread().getContextClassLoader();
266 			if (ccl == ContextLoader.class.getClassLoader()) {
267 				currentContext = null;
268 			}
269 			else if (ccl != null) {
270 				currentContextPerThread.remove(ccl);
271 			}
272 			servletContext.removeAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
273 			if (this.parentContextRef != null) {
274 				this.parentContextRef.release();
275 			}
276 		}
277 	}
278 
279 
280 
281 	private void cleanupAttributes(ServletContext servletContext) {
282 		Enumeration attrNames = servletContext.getAttributeNames();
283         while (attrNames.hasMoreElements()) {
284         	String attrName = (String) attrNames.nextElement();
285         	if (attrName.startsWith("org.springframework.")) {
286         		Object attrValue = servletContext.getAttribute(attrName);
287         		if (attrValue instanceof DisposableBean) {
288         			try {
289         				((DisposableBean) attrValue).destroy();
290         			} catch (Throwable ex) {
291         				 logger.error("Couldn't invoke destroy method of attribute with name '" + attrName + "'", ex);
292         			}
293         		}
294         	}
295         }
296 	}
297 
298 	public void contextDestroyed(ServletContextEvent servletContextEvent) {
299 		closeWebApplicationContext(servletContextEvent.getServletContext());
300 		cleanupAttributes(servletContextEvent.getServletContext());
301 	}
302 
303 }