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: 1.7 $ $Date: 2004/11/30 05:52:23 $
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>The <code>Log</code> instance for this class.</p>
143 */
144 private static final Log log = LogFactory.getLog(ChainListener.class);
145
146
147
148
149
150 /***
151 * <p>Remove the configured {@link Catalog} from the servlet context
152 * attributes for this web application.</p>
153 *
154 * @param event <code>ServletContextEvent</code> to be processed
155 */
156 public void contextDestroyed(ServletContextEvent event) {
157
158 ServletContext context = event.getServletContext();
159 String attr = context.getInitParameter(CONFIG_ATTR);
160 if (attr != null) {
161 context.removeAttribute(attr);
162 }
163 CatalogFactory.clear();
164
165 }
166
167
168 /***
169 * <p>Scan the required chain configuration resources, assemble the
170 * configured chains into a {@link Catalog}, and expose it as a
171 * servlet context attribute under the specified key.</p>
172 *
173 * @param event <code>ServletContextEvent</code> to be processed
174 */
175 public void contextInitialized(ServletContextEvent event) {
176
177 if (log.isInfoEnabled()) {
178 log.info("Initializing chain listener");
179 }
180 ServletContext context = event.getServletContext();
181
182
183 String attr = context.getInitParameter(CONFIG_ATTR);
184 String classResources =
185 context.getInitParameter(CONFIG_CLASS_RESOURCE);
186 String ruleSet = context.getInitParameter(RULE_SET);
187 String webResources = context.getInitParameter(CONFIG_WEB_RESOURCE);
188
189
190 Catalog catalog = null;
191 if (attr != null) {
192 catalog = (Catalog) context.getAttribute(attr);
193 if (catalog == null) {
194 catalog = new CatalogBase();
195 }
196 }
197
198
199 ConfigParser parser = new ConfigParser();
200 if (ruleSet != null) {
201 try {
202 ClassLoader loader =
203 Thread.currentThread().getContextClassLoader();
204 if (loader == null) {
205 loader = this.getClass().getClassLoader();
206 }
207 Class clazz = loader.loadClass(ruleSet);
208 parser.setRuleSet((RuleSet) clazz.newInstance());
209 } catch (Exception e) {
210 throw new RuntimeException("Exception initalizing RuleSet '"
211 + ruleSet + "' instance: "
212 + e.getMessage());
213 }
214 }
215
216
217 if (attr == null) {
218 parseJarResources(context, parser);
219 ChainResources.parseClassResources
220 (classResources, parser);
221 ChainResources.parseWebResources
222 (context, webResources, parser);
223 } else {
224 parseJarResources(catalog, context, parser);
225 ChainResources.parseClassResources
226 (catalog, classResources, parser);
227 ChainResources.parseWebResources
228 (catalog, context, webResources, parser);
229 }
230
231
232 if (attr != null) {
233 context.setAttribute(attr, catalog);
234 }
235
236 }
237
238
239
240
241
242 /***
243 * <p>Parse resources found in JAR files in the <code>/WEB-INF/lib</code>
244 * subdirectory (if any).</p>
245 *
246 * @param context <code>ServletContext</code> for this web application
247 * @param parser {@link ConfigParser} to use for parsing
248 */
249 private void parseJarResources(ServletContext context,
250 ConfigParser parser) {
251
252 Set jars = context.getResourcePaths("/WEB-INF/lib");
253 if (jars == null) {
254 jars = new HashSet();
255 }
256 String path = null;
257 Iterator paths = jars.iterator();
258 while (paths.hasNext()) {
259
260 path = (String) paths.next();
261 if (!path.endsWith(".jar")) {
262 continue;
263 }
264 URL resourceURL = null;
265 try {
266 URL jarURL = context.getResource(path);
267 resourceURL = new URL("jar:"
268 + translate(jarURL.toExternalForm())
269 + "!/META-INF/chain-config.xml");
270 if (resourceURL == null) {
271 continue;
272 }
273 InputStream is = null;
274 try {
275 is = resourceURL.openStream();
276 } catch (Exception e) {
277 ;
278 }
279 if (is == null) {
280 continue;
281 } else {
282 is.close();
283 }
284 parser.parse(resourceURL);
285 } catch (Exception e) {
286 throw new RuntimeException
287 ("Exception parsing chain config resource '"
288 + resourceURL.toExternalForm() + "': "
289 + e.getMessage());
290 }
291 }
292
293 }
294
295
296 /***
297 * <p>Parse resources found in JAR files in the <code>/WEB-INF/lib</code>
298 * subdirectory (if any).</p>
299 *
300 * @param catalog {@link Catalog} we are populating
301 * @param context <code>ServletContext</code> for this web application
302 * @param parser {@link ConfigParser} to use for parsing
303 *
304 * @deprecated Use the variant that does not take a catalog, on a
305 * configuration resource containing "catalog" element(s)
306 */
307 private void parseJarResources(Catalog catalog, ServletContext context,
308 ConfigParser parser) {
309
310 Set jars = context.getResourcePaths("/WEB-INF/lib");
311 if (jars == null) {
312 jars = new HashSet();
313 }
314 String path = null;
315 Iterator paths = jars.iterator();
316 while (paths.hasNext()) {
317
318 path = (String) paths.next();
319 if (!path.endsWith(".jar")) {
320 continue;
321 }
322 URL resourceURL = null;
323 try {
324 URL jarURL = context.getResource(path);
325 resourceURL = new URL("jar:"
326 + translate(jarURL.toExternalForm())
327 + "!/META-INF/chain-config.xml");
328 if (resourceURL == null) {
329 continue;
330 }
331 InputStream is = null;
332 try {
333 is = resourceURL.openStream();
334 } catch (Exception e) {
335 ;
336 }
337 if (is == null) {
338 continue;
339 } else {
340 is.close();
341 }
342 parser.parse(catalog, resourceURL);
343 } catch (Exception e) {
344 throw new RuntimeException
345 ("Exception parsing chain config resource '"
346 + resourceURL.toExternalForm() + "': "
347 + e.getMessage());
348 }
349 }
350
351 }
352
353
354 /***
355 * <p>Translate space character into <code>&pct;20</code> to avoid problems
356 * with paths that contain spaces on some JVMs.</p>
357 *
358 * @param value Value to translate
359 */
360 private String translate(String value) {
361
362 while (true) {
363 int index = value.indexOf(' ');
364 if (index < 0) {
365 break;
366 }
367 value = value.substring(0, index) + value.substring(index + 1);
368 }
369 return (value);
370
371 }
372
373
374 }