1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.commons.chain.web;
17
18
19 import java.io.InputStream;
20 import java.net.URL;
21 import java.util.HashSet;
22 import java.util.Iterator;
23 import java.util.Set;
24 import javax.servlet.ServletContext;
25 import javax.servlet.ServletContextEvent;
26 import javax.servlet.ServletContextListener;
27 import org.apache.commons.chain.Catalog;
28 import org.apache.commons.chain.CatalogFactory;
29 import org.apache.commons.chain.config.ConfigParser;
30 import org.apache.commons.chain.impl.CatalogBase;
31 import org.apache.commons.digester.RuleSet;
32 import org.apache.commons.logging.Log;
33 import org.apache.commons.logging.LogFactory;
34
35
36 /***
37 * <p><code>ServletContextListener</code> that automatically
38 * scans chain configuration files in the current web application at
39 * startup time, and exposes the result in a {@link Catalog} under a
40 * specified servlet context attribute. The following <em>context</em> init
41 * parameters are utilized:</p>
42 * <ul>
43 * <li><strong>org.apache.commons.chain.CONFIG_CLASS_RESOURCE</strong> -
44 * comma-delimited list of chain configuration resources to be loaded
45 * via <code>ClassLoader.getResource()</code> calls. If not specified,
46 * no class loader resources will be loaded.</li>
47 * <li><strong>org.apache.commons.chain.CONFIG_WEB_RESOURCE</strong> -
48 * comma-delimited list of chain configuration webapp resources
49 * to be loaded. If not specified, no web application resources
50 * will be loaded.</li>
51 * <li><strong>org.apache.commons.chain.CONFIG_ATTR</strong> -
52 * Name of the servlet context attribute under which the
53 * resulting {@link Catalog} will be created or updated.
54 * If not specified, it is expected that parsed resources will
55 * contain <code><catalog></code> elements (which will
56 * cause registration of the created {@link Catalog}s into
57 * the {@link CatalogFactory} for this application, and no
58 * servet context attribute will be created.
59 * <strong>NOTE</strong> - This parameter is deprecated.</p>
60 * <li><strong>org.apache.commons.chain.RULE_SET</strong> -
61 * Fully qualified class name of a Digester <code>RuleSet</code>
62 * implementation to use for parsing configuration resources (this
63 * class must have a public zero-args constructor). If not defined,
64 * the standard <code>RuleSet</code> implementation will be used.</li>
65 * </ul>
66 *
67 * <p>When a web application that has configured this listener is
68 * started, it will acquire the {@link Catalog} under the specified servlet
69 * context attribute key, creating a new one if there is none already there.
70 * This {@link Catalog} will then be populated by scanning configuration
71 * resources from the following sources (loaded in this order):</p>
72 * <ul>
73 * <li>Resources loaded from any <code>META-INF/chain-config.xml</code>
74 * resource found in a JAR file in <code>/WEB-INF/lib</code>.</li>
75 * <li>Resources loaded from specified resource paths from the
76 * webapp's class loader (via <code>ClassLoader.getResource()</code>).</li>
77 * <li>Resources loaded from specified resource paths in the web application
78 * archive (via <code>ServetContext.getResource()</code>).</li>
79 * </ul>
80 *
81 * <p>If no attribute key is specified, on the other hand, parsed configuration
82 * resources are expected to contain <code><catalog></code> elements,
83 * and the catalogs will be registered with the {@link CatalogFactory}
84 * for this web application.</p>
85 *
86 * <p>This class requires Servlet 2.3 or later. If you are running on
87 * Servlet 2.2 system, consider using {@link ChainServlet} instead.
88 * Note that {@link ChainServlet} uses parameters of the
89 * same names, but they are <em>servlet</em> init parameters instead
90 * of <em>context</em> init parameters. Because of this, you can use
91 * both facilities in the same application, if desired.</p>
92 *
93 * @author Craig R. McClanahan
94 * @author Ted Husted
95 * @version $Revision: 411893 $ $Date: 2006-06-05 19:59:05 +0100 (Mon, 05 Jun 2006) $
96 */
97
98 public class ChainListener implements ServletContextListener {
99
100
101
102
103
104 /***
105 * <p>The name of the context init parameter containing the name of the
106 * servlet context attribute under which our resulting {@link Catalog}
107 * will be stored.</p>
108 */
109 public static final String CONFIG_ATTR =
110 "org.apache.commons.chain.CONFIG_ATTR";
111
112
113 /***
114 * <p>The name of the context init parameter containing a comma-delimited
115 * list of class loader resources to be scanned.</p>
116 */
117 public static final String CONFIG_CLASS_RESOURCE =
118 "org.apache.commons.chain.CONFIG_CLASS_RESOURCE";
119
120
121 /***
122 * <p>The name of the context init parameter containing a comma-delimited
123 * list of web applicaton resources to be scanned.</p>
124 */
125 public static final String CONFIG_WEB_RESOURCE =
126 "org.apache.commons.chain.CONFIG_WEB_RESOURCE";
127
128
129 /***
130 * <p>The name of the context init parameter containing the fully
131 * qualified class name of the <code>RuleSet</code> implementation
132 * for configuring our {@link ConfigParser}.</p>
133 */
134 public static final String RULE_SET =
135 "org.apache.commons.chain.RULE_SET";
136
137
138
139
140
141 /***
142 * <p>Remove the configured {@link Catalog} from the servlet context
143 * attributes for this web application.</p>
144 *
145 * @param event <code>ServletContextEvent</code> to be processed
146 */
147 public void contextDestroyed(ServletContextEvent event) {
148
149 ServletContext context = event.getServletContext();
150 String attr = context.getInitParameter(CONFIG_ATTR);
151 if (attr != null) {
152 context.removeAttribute(attr);
153 }
154 CatalogFactory.clear();
155
156 }
157
158
159 /***
160 * <p>Scan the required chain configuration resources, assemble the
161 * configured chains into a {@link Catalog}, and expose it as a
162 * servlet context attribute under the specified key.</p>
163 *
164 * @param event <code>ServletContextEvent</code> to be processed
165 */
166 public void contextInitialized(ServletContextEvent event) {
167
168 Log log = LogFactory.getLog(ChainListener.class);
169 if (log.isInfoEnabled()) {
170 log.info("Initializing chain listener");
171 }
172 ServletContext context = event.getServletContext();
173
174
175 String attr = context.getInitParameter(CONFIG_ATTR);
176 String classResources =
177 context.getInitParameter(CONFIG_CLASS_RESOURCE);
178 String ruleSet = context.getInitParameter(RULE_SET);
179 String webResources = context.getInitParameter(CONFIG_WEB_RESOURCE);
180
181
182 Catalog catalog = null;
183 if (attr != null) {
184 catalog = (Catalog) context.getAttribute(attr);
185 if (catalog == null) {
186 catalog = new CatalogBase();
187 }
188 }
189
190
191 ConfigParser parser = new ConfigParser();
192 if (ruleSet != null) {
193 try {
194 ClassLoader loader =
195 Thread.currentThread().getContextClassLoader();
196 if (loader == null) {
197 loader = this.getClass().getClassLoader();
198 }
199 Class clazz = loader.loadClass(ruleSet);
200 parser.setRuleSet((RuleSet) clazz.newInstance());
201 } catch (Exception e) {
202 throw new RuntimeException("Exception initalizing RuleSet '"
203 + ruleSet + "' instance: "
204 + e.getMessage());
205 }
206 }
207
208
209 if (attr == null) {
210 parseJarResources(context, parser);
211 ChainResources.parseClassResources
212 (classResources, parser);
213 ChainResources.parseWebResources
214 (context, webResources, parser);
215 } else {
216 parseJarResources(catalog, context, parser);
217 ChainResources.parseClassResources
218 (catalog, classResources, parser);
219 ChainResources.parseWebResources
220 (catalog, context, webResources, parser);
221 }
222
223
224 if (attr != null) {
225 context.setAttribute(attr, catalog);
226 }
227
228 }
229
230
231
232
233
234 /***
235 * <p>Parse resources found in JAR files in the <code>/WEB-INF/lib</code>
236 * subdirectory (if any).</p>
237 *
238 * @param context <code>ServletContext</code> for this web application
239 * @param parser {@link ConfigParser} to use for parsing
240 */
241 private void parseJarResources(ServletContext context,
242 ConfigParser parser) {
243
244 Set jars = context.getResourcePaths("/WEB-INF/lib");
245 if (jars == null) {
246 jars = new HashSet();
247 }
248 String path = null;
249 Iterator paths = jars.iterator();
250 while (paths.hasNext()) {
251
252 path = (String) paths.next();
253 if (!path.endsWith(".jar")) {
254 continue;
255 }
256 URL resourceURL = null;
257 try {
258 URL jarURL = context.getResource(path);
259 resourceURL = new URL("jar:"
260 + translate(jarURL.toExternalForm())
261 + "!/META-INF/chain-config.xml");
262 if (resourceURL == null) {
263 continue;
264 }
265 InputStream is = null;
266 try {
267 is = resourceURL.openStream();
268 } catch (Exception e) {
269
270 }
271 if (is == null) {
272 continue;
273 } else {
274 is.close();
275 }
276 parser.parse(resourceURL);
277 } catch (Exception e) {
278 throw new RuntimeException
279 ("Exception parsing chain config resource '"
280 + resourceURL.toExternalForm() + "': "
281 + e.getMessage());
282 }
283 }
284
285 }
286
287
288 /***
289 * <p>Parse resources found in JAR files in the <code>/WEB-INF/lib</code>
290 * subdirectory (if any).</p>
291 *
292 * @param catalog {@link Catalog} we are populating
293 * @param context <code>ServletContext</code> for this web application
294 * @param parser {@link ConfigParser} to use for parsing
295 *
296 * @deprecated Use the variant that does not take a catalog, on a
297 * configuration resource containing "catalog" element(s)
298 */
299 private void parseJarResources(Catalog catalog, ServletContext context,
300 ConfigParser parser) {
301
302 Set jars = context.getResourcePaths("/WEB-INF/lib");
303 if (jars == null) {
304 jars = new HashSet();
305 }
306 String path = null;
307 Iterator paths = jars.iterator();
308 while (paths.hasNext()) {
309
310 path = (String) paths.next();
311 if (!path.endsWith(".jar")) {
312 continue;
313 }
314 URL resourceURL = null;
315 try {
316 URL jarURL = context.getResource(path);
317 resourceURL = new URL("jar:"
318 + translate(jarURL.toExternalForm())
319 + "!/META-INF/chain-config.xml");
320 if (resourceURL == null) {
321 continue;
322 }
323 InputStream is = null;
324 try {
325 is = resourceURL.openStream();
326 } catch (Exception e) {
327
328 }
329 if (is == null) {
330 continue;
331 } else {
332 is.close();
333 }
334 parser.parse(catalog, resourceURL);
335 } catch (Exception e) {
336 throw new RuntimeException
337 ("Exception parsing chain config resource '"
338 + resourceURL.toExternalForm() + "': "
339 + e.getMessage());
340 }
341 }
342
343 }
344
345
346 /***
347 * <p>Translate space character into <code>&pct;20</code> to avoid problems
348 * with paths that contain spaces on some JVMs.</p>
349 *
350 * @param value Value to translate
351 */
352 private String translate(String value) {
353
354 while (true) {
355 int index = value.indexOf(' ');
356 if (index < 0) {
357 break;
358 }
359 value = value.substring(0, index) + value.substring(index + 1);
360 }
361 return (value);
362
363 }
364
365
366 }