2009/05/20 - Apache Shale has been retired.
For more information, please explore the Attic.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.shale.remoting.faces;
19
20 import java.io.InputStream;
21 import java.lang.reflect.Method;
22 import java.net.URL;
23 import java.util.ArrayList;
24 import java.util.List;
25 import java.util.ResourceBundle;
26 import javax.faces.FacesException;
27 import javax.faces.application.ViewHandler;
28 import javax.faces.context.FacesContext;
29 import javax.xml.parsers.DocumentBuilder;
30 import javax.xml.parsers.DocumentBuilderFactory;
31 import org.apache.commons.logging.Log;
32 import org.apache.commons.logging.LogFactory;
33 import org.apache.shale.remoting.Constants;
34 import org.apache.shale.remoting.Mapping;
35 import org.apache.shale.remoting.Mappings;
36 import org.apache.shale.remoting.Mechanism;
37 import org.apache.shale.remoting.Processor;
38 import org.apache.shale.remoting.impl.FilteringProcessor;
39 import org.apache.shale.remoting.impl.MappingImpl;
40 import org.apache.shale.remoting.impl.MappingsImpl;
41 import org.w3c.dom.Document;
42 import org.w3c.dom.Node;
43 import org.w3c.dom.NodeList;
44
45 /***
46 * <p>Helper bean for accessing the {@link Mappings} instance for this
47 * application, creating it the first time if necessary.</p>
48 *
49 * @since 1.0.1
50 */
51 public class MappingsHelper {
52
53
54
55
56
57 /***
58 * <p><code>ResourceBundle</code> containing our localized messages.</p>
59 */
60 private ResourceBundle bundle =
61 ResourceBundle.getBundle("org.apache.shale.remoting.Bundle");
62
63
64 /***
65 * <p>Log instance for this class.</p>
66 */
67 private transient Log log = null;
68
69
70
71
72
73 /***
74 * <p>Return the {@link Mappings} instance for this web application,
75 * creating it if necessary.</p>
76 *
77 * @param context <code>FacesContext</code> for the current request
78 */
79 public Mappings getMappings(FacesContext context) {
80
81 Mappings mappings = (Mappings)
82 context.getExternalContext().getApplicationMap().
83 get(Constants.MAPPINGS_ATTR);
84 if (mappings == null) {
85 mappings = createMappings(context);
86 context.getExternalContext().getApplicationMap().
87 put(Constants.MAPPINGS_ATTR, mappings);
88 }
89 return mappings;
90
91 }
92
93
94
95
96
97 /***
98 * <p>Configure {@link Mapping} instances on the specified {@link Mappings}
99 * instance, for the specified context initialization parameter.</p>
100 *
101 * @param context <code>FacesContext</code> for the current request
102 * @param mappings {@link Mappings} instance being configured
103 * @param paramName Context initialization parameter name to process
104 * @param excludesName Context initialization parameter containing our
105 * exclude patterns
106 * @param excludesDefault Default exclude patterns if none are configured
107 * @param includesName Context initialization parameter containing our
108 * include patterns
109 * @param includesDefault Default include patterns if none are configured
110 * @param mechanism {@link Mechanism} to configure on created instances
111 * @param defaultValue Default value (if any) if not specified
112 *
113 * @exception FacesException if a new Mapping instance cannot be created
114 * or configured
115 */
116 private void configureMappings(FacesContext context, Mappings mappings,
117 String paramName,
118 String excludesName, String excludesDefault,
119 String includesName, String includesDefault,
120 Mechanism mechanism, String defaultValue) {
121
122
123 Class clazz = MappingImpl.class;
124 String mappingClass =
125 context.getExternalContext().getInitParameter(Constants.MAPPING_CLASS);
126 if (mappingClass != null) {
127 try {
128 clazz = loadClass(mappingClass);
129 } catch (Exception e) {
130 throw new FacesException(e);
131 }
132 }
133
134
135 String paramValue = context.getExternalContext().getInitParameter(paramName);
136 if (paramValue == null) {
137 paramValue = defaultValue;
138 }
139 if (paramValue == null) {
140 return;
141 }
142
143
144 while (true) {
145 paramValue = paramValue.trim();
146 if (paramValue.length() == 0) {
147 break;
148 }
149 String pair = null;
150 int comma = paramValue.indexOf(',');
151 if (comma >= 0) {
152 pair = paramValue.substring(0, comma).trim();
153 paramValue = paramValue.substring(comma + 1);
154 } else {
155 pair = paramValue.trim();
156 paramValue = "";
157 }
158 int colon = pair.indexOf(':');
159 if (colon < 0) {
160 throw new IllegalArgumentException(pair);
161 }
162 String pattern = pair.substring(0, colon).trim();
163 String processorClass = pair.substring(colon + 1).trim();
164 if (log().isInfoEnabled()) {
165 log().info(bundle.getString("mapping.configure"));
166 log().info(pattern + ":" + processorClass);
167 }
168 Class processorClazz = null;
169 try {
170 processorClazz = loadClass(processorClass);
171 } catch (Exception e) {
172 throw new FacesException(e);
173 }
174 try {
175 Mapping mapping = (Mapping) clazz.newInstance();
176 mapping.setMappings(mappings);
177 mapping.setMechanism(mechanism);
178 mapping.setPattern(pattern);
179 Processor processor = (Processor) processorClazz.newInstance();
180 if (processor instanceof FilteringProcessor) {
181 String excludesPatterns =
182 context.getExternalContext().getInitParameter(excludesName);
183 if (excludesPatterns == null) {
184 excludesPatterns = excludesDefault;
185 }
186 ((FilteringProcessor) processor).setExcludes(excludesPatterns);
187 String includesPatterns =
188 context.getExternalContext().getInitParameter(includesName);
189 if (includesPatterns == null) {
190 includesPatterns = includesDefault;
191 }
192 ((FilteringProcessor) processor).setIncludes(includesPatterns);
193 }
194 mapping.setProcessor(processor);
195 mappings.addMapping(mapping);
196 } catch (RuntimeException e) {
197 throw e;
198 } catch (Exception e) {
199 throw new FacesException(e);
200 }
201 }
202
203 }
204
205
206 /***
207 * <p>Create and configure a {@link Mappings} instance based on the relevant
208 * context initialization parameters for this web application.</p>
209 *
210 * @param context <code>FacesContext</code> for the current request
211 *
212 * @exception FacesException if a new Mappings instance cannot be created
213 * or configured
214 */
215 private Mappings createMappings(FacesContext context) {
216
217
218 Mappings mappings = null;
219 String mappingsClass = MappingsImpl.class.getName();
220 String mappingsClassParam =
221 context.getExternalContext().getInitParameter(Constants.MAPPINGS_CLASS);
222 if (mappingsClassParam != null) {
223 mappingsClass = mappingsClassParam;
224 }
225 Class clazz = null;
226 try {
227 if (log().isInfoEnabled()) {
228 log().info(bundle.getString("mappings.configure"));
229 log().info(mappingsClass);
230 }
231 clazz = loadClass(mappingsClass);
232 } catch (Exception e) {
233 throw new FacesException(e);
234 }
235 try {
236 mappings = (Mappings) clazz.newInstance();
237 } catch (Exception e) {
238 throw new FacesException(e);
239 }
240
241
242 configureMappings(context, mappings, Constants.CLASS_RESOURCES_PARAM,
243 Constants.CLASS_RESOURCES_EXCLUDES,
244 Constants.CLASS_RESOURCES_EXCLUDES_DEFAULT,
245 Constants.CLASS_RESOURCES_INCLUDES,
246 Constants.CLASS_RESOURCES_INCLUDES_DEFAULT,
247 Mechanism.CLASS_RESOURCE,
248 "/static/*:org.apache.shale.remoting.impl.ClassResourceProcessor");
249 configureMappings(context, mappings, Constants.DYNAMIC_RESOURCES_PARAM,
250 Constants.DYNAMIC_RESOURCES_EXCLUDES,
251 Constants.DYNAMIC_RESOURCES_EXCLUDES_DEFAULT,
252 Constants.DYNAMIC_RESOURCES_INCLUDES,
253 Constants.DYNAMIC_RESOURCES_INCLUDES_DEFAULT,
254 Mechanism.DYNAMIC_RESOURCE,
255 "/dynamic/*:org.apache.shale.remoting.impl.MethodBindingProcessor");
256 configureMappings(context, mappings, Constants.OTHER_RESOURCES_PARAM,
257 Constants.OTHER_RESOURCES_EXCLUDES,
258 Constants.OTHER_RESOURCES_EXCLUDES_DEFAULT,
259 Constants.OTHER_RESOURCES_INCLUDES,
260 Constants.OTHER_RESOURCES_INCLUDES_DEFAULT,
261 Mechanism.OTHER_RESOURCE,
262 null);
263 configureMappings(context, mappings, Constants.WEBAPP_RESOURCES_PARAM,
264 Constants.WEBAPP_RESOURCES_EXCLUDES,
265 Constants.WEBAPP_RESOURCES_EXCLUDES_DEFAULT,
266 Constants.WEBAPP_RESOURCES_INCLUDES,
267 Constants.WEBAPP_RESOURCES_INCLUDES_DEFAULT,
268 Mechanism.WEBAPP_RESOURCE,
269 "/webapp/*:org.apache.shale.remoting.impl.WebResourceProcessor");
270
271
272
273 String extension = context.getExternalContext().
274 getInitParameter(ViewHandler.DEFAULT_SUFFIX_PARAM_NAME);
275 if (extension == null) {
276 extension = ViewHandler.DEFAULT_SUFFIX;
277 }
278 mappings.setExtension(extension);
279
280
281
282 String[] patterns = patterns(context);
283 if (log().isTraceEnabled()) {
284 for (int i = 0; i < patterns.length; i++) {
285 log().trace("FacesServlet is mapped with URL pattern '" + patterns[i] + "'");
286 }
287 }
288 mappings.setPatterns(patterns);
289
290
291 int patternIndex = 0;
292 String patternIndexString =
293 context.getExternalContext().getInitParameter(Constants.FACES_SERVLET_URL_PARAM);
294 if (patternIndexString != null) {
295 patternIndex = Integer.parseInt(patternIndexString.trim());
296 }
297 if (patternIndex >= patterns.length) {
298 log.warn("FacesServlet pattern index of " + patternIndex
299 + " does not match any specified pattern");
300 }
301 mappings.setPatternIndex(patternIndex);
302
303
304 return mappings;
305
306 }
307
308
309 /***
310 * <p>Load the specified class from the web application class loader
311 * (if possible).</p>
312 *
313 * @param name Fully qualified class name
314 *
315 * @exception ClassNotFoundException if the specified class cannot
316 * be loaded
317 */
318 private Class loadClass(String name) throws ClassNotFoundException {
319
320 ClassLoader cl = Thread.currentThread().getContextClassLoader();
321 if (cl == null) {
322 cl = this.getClass().getClassLoader();
323 }
324 return cl.loadClass(name);
325
326 }
327
328
329 /***
330 * <p>Return the <code>Log</code> instance to use, creating one if needed.</p>
331 */
332 private Log log() {
333
334 if (this.log == null) {
335 log = LogFactory.getLog(MappingsHelper.class);
336 }
337 return log;
338
339 }
340
341
342 /***
343 * <p>Return an array of URL patterns that <code>FacesServlet</code> is
344 * mapped to for this application.</p>
345 *
346 * @param context <code>FacesContext</code> for the current request
347 */
348 private String[] patterns(FacesContext context) {
349
350 Document document = null;
351 InputStream stream = null;
352
353 try {
354
355
356 Object ctxt = context.getExternalContext().getContext();
357 Method method =
358 ctxt.getClass().getMethod("getResource",
359 new Class[] { String.class });
360 URL url = (URL) method.invoke(ctxt, new Object[] { "/WEB-INF/web.xml" });
361 if (url == null) {
362 if (log().isTraceEnabled()) {
363 log().trace("No /WEB-INF/web.xml resource available, returning empty list");
364 }
365 return new String[0];
366 }
367
368
369 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
370 DocumentBuilder db = dbf.newDocumentBuilder();
371 stream = url.openStream();
372 document = db.parse(stream);
373
374 } catch (Exception e) {
375
376 if (log().isErrorEnabled()) {
377 log().error(bundle.getString("mappings.parseWebXml"), e);
378 }
379 return new String[0];
380
381 } finally {
382
383 if (stream != null) {
384 try { stream.close(); } catch (Exception e) { ; }
385 }
386
387 }
388
389
390 String name =
391 context.getExternalContext().getInitParameter(Constants.FACES_SERVLET_NAME_PARAM);
392 if (null == name) {
393 NodeList servletNodes = document.getElementsByTagName("servlet");
394 for (int i = 0; i < servletNodes.getLength(); i++) {
395 Node servletNode = servletNodes.item(i);
396 String servletName = null;
397 String servletClass = null;
398 NodeList kids = servletNode.getChildNodes();
399 for (int j = 0; j < kids.getLength(); j++) {
400 Node kid = kids.item(j);
401 if ("servlet-name".equals(kid.getNodeName())) {
402 servletName = text(kid);
403 } else if ("servlet-class".equals(kid.getNodeName())) {
404 servletClass = text(kid);
405 }
406 }
407 if ("javax.faces.webapp.FacesServlet".equals(servletClass)) {
408 name = servletName;
409 }
410 }
411 }
412
413
414 List list = new ArrayList();
415 NodeList mappingNodes = document.getElementsByTagName("servlet-mapping");
416 for (int i = 0; i < mappingNodes.getLength(); i++) {
417 Node mappingNode = mappingNodes.item(i);
418 String servletName = null;
419 String urlPattern = null;
420 NodeList kids = mappingNode.getChildNodes();
421 for (int j = 0; j < kids.getLength(); j++) {
422 Node kid = kids.item(j);
423 if ("servlet-name".equals(kid.getNodeName())) {
424 servletName = text(kid);
425 } else if ("url-pattern".equals(kid.getNodeName())) {
426 urlPattern = text(kid);
427 }
428 }
429 if (name.equals(servletName)) {
430 list.add(urlPattern);
431 }
432 }
433
434
435 return (String[]) list.toArray(new String[list.size()]);
436
437 }
438
439
440 /***
441 * <p>Return the text content inside the specified node.</p>
442 *
443 * @param node Node from which text is to be extracted
444 */
445 private String text(Node node) {
446
447 NodeList kids = node.getChildNodes();
448 for (int k = 0; k < kids.getLength(); k++) {
449 Node kid = kids.item(k);
450 if ("#text".equals(kid.getNodeName())) {
451 return kid.getNodeValue().trim();
452 }
453 }
454 return "";
455
456 }
457
458
459 }