View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.shared.webapp.webxml;
20  
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.net.URL;
24  import java.util.logging.Level;
25  import java.util.logging.Logger;
26  
27  import javax.faces.FacesException;
28  import javax.faces.context.ExternalContext;
29  import javax.xml.parsers.DocumentBuilder;
30  import javax.xml.parsers.DocumentBuilderFactory;
31  
32  import org.apache.myfaces.shared.util.ClassUtils;
33  import org.apache.myfaces.shared.util.xml.MyFacesErrorHandler;
34  import org.apache.myfaces.shared.util.xml.XmlUtils;
35  import org.w3c.dom.Document;
36  import org.w3c.dom.Element;
37  import org.w3c.dom.Node;
38  import org.w3c.dom.NodeList;
39  import org.xml.sax.EntityResolver;
40  import org.xml.sax.InputSource;
41  
42  public class WebXmlParser
43  {
44      //private static final Log log = LogFactory.getLog(WebXmlParser.class);
45      private static final Logger log = Logger.getLogger(WebXmlParser.class.getName());
46  
47      /*
48      private static final String JAXP_SCHEMA_LANGUAGE =
49          "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
50      private static final String W3C_XML_SCHEMA =
51          "http://www.w3.org/2001/XMLSchema";
52          */
53  
54      private static final String WEB_XML_PATH = "/WEB-INF/web.xml";
55  
56      private static final String WEB_APP_2_2_J2EE_SYSTEM_ID = "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd";
57      private static final String WEB_APP_2_2_SYSTEM_ID = "http://java.sun.com/dtd/web-app_2_2.dtd";
58      private static final String WEB_APP_2_2_RESOURCE  = "javax/servlet/resources/web-app_2_2.dtd";
59  
60      private static final String WEB_APP_2_3_SYSTEM_ID = "http://java.sun.com/dtd/web-app_2_3.dtd";
61      private static final String WEB_APP_2_3_RESOURCE  = "javax/servlet/resources/web-app_2_3.dtd";
62      
63      private ExternalContext _context;
64      private org.apache.myfaces.shared.webapp.webxml.WebXml _webXml;
65  
66      public WebXmlParser(ExternalContext context)
67      {
68          _context = context;
69      }
70  
71      public WebXml parse()
72      {
73          _webXml = new WebXml();
74  
75          try
76          {
77              DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
78              dbf.setIgnoringElementContentWhitespace(true);
79              dbf.setIgnoringComments(true);
80              dbf.setNamespaceAware(true);
81              dbf.setValidating(false);
82  //            dbf.setAttribute(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA);
83  
84              DocumentBuilder db = dbf.newDocumentBuilder();
85              db.setEntityResolver(new _EntityResolver());
86              db.setErrorHandler(new MyFacesErrorHandler(log));
87  
88              InputSource is = createContextInputSource(null, WEB_XML_PATH);
89  
90              if(is==null)
91              {
92                  URL url = _context.getResource(WEB_XML_PATH);
93                  log.fine("No web-xml found at : "+(url==null?" null ":url.toString()));
94                  return _webXml;
95              }
96  
97              Document document = db.parse(is);
98  
99              Element webAppElem = document.getDocumentElement();
100             if (webAppElem == null ||
101                 !webAppElem.getNodeName().equals("web-app"))
102             {
103                 throw new FacesException("No valid web-app root element found!");
104             }
105 
106             readWebApp(webAppElem);
107             
108             return _webXml;
109         }
110         catch (Exception e)
111         {
112             log.log(Level.SEVERE, "Unable to parse web.xml", e);
113             throw new FacesException(e);
114         }
115     }
116 
117     public static long getWebXmlLastModified(ExternalContext context)
118     {
119         try
120         {
121             URL url = context.getResource(WEB_XML_PATH);
122             if (url != null)
123             {
124                 return url.openConnection().getLastModified();
125             }
126         }
127         catch (IOException e)
128         {
129             log.log(Level.SEVERE, "Could not find web.xml in path " + WEB_XML_PATH);
130         }
131         return 0L;
132     }
133 
134 
135     private InputSource createContextInputSource(String publicId, String systemId)
136     {
137         InputStream inStream = _context.getResourceAsStream(systemId);
138         if (inStream == null)
139         {
140             // there is no such entity
141             return null;
142         }
143         InputSource is = new InputSource(inStream);
144         is.setPublicId(publicId);
145         is.setSystemId(systemId);
146         //the next line was removed - encoding should be determined automatically out of the inputStream
147         //DEFAULT_ENCODING was ISO-8859-1
148         //is.setEncoding(DEFAULT_ENCODING);
149         return is;
150     }
151 
152     private InputSource createClassloaderInputSource(String publicId, String systemId)
153     {
154         InputStream inStream = ClassUtils.getResourceAsStream(systemId);
155         if (inStream == null)
156         {
157             // there is no such entity
158             return null;
159         }
160         InputSource is = new InputSource(inStream);
161         is.setPublicId(publicId);
162         is.setSystemId(systemId);
163         //the next line was removed - encoding should be determined automatically out of the inputStream
164         //encoding should be determined automatically out of the inputStream
165         //DEFAULT_ENCODING was ISO-8859-1
166         //is.setEncoding(DEFAULT_ENCODING);
167         return is;
168     }
169 
170     private class _EntityResolver implements EntityResolver
171     {
172         public InputSource resolveEntity(String publicId, String systemId) throws IOException
173         {
174             if (systemId == null)
175             {
176                 throw new UnsupportedOperationException("systemId must not be null");
177             }
178 
179             if (systemId.equals(WebXmlParser.WEB_APP_2_2_SYSTEM_ID) ||
180                 systemId.equals(WebXmlParser.WEB_APP_2_2_J2EE_SYSTEM_ID))
181             {
182                 //Load DTD from servlet.jar
183                 return createClassloaderInputSource(publicId, WebXmlParser.WEB_APP_2_2_RESOURCE);
184             }
185             else if (systemId.equals(WebXmlParser.WEB_APP_2_3_SYSTEM_ID))
186             {
187                 //Load DTD from servlet.jar
188                 return createClassloaderInputSource(publicId, WebXmlParser.WEB_APP_2_3_RESOURCE);
189             }
190             else
191             {
192                 //Load additional entities from web context
193                 return createContextInputSource(publicId, systemId);
194             }
195         }
196 
197     }
198 
199 
200     private void readWebApp(Element webAppElem)
201     {
202         NodeList nodeList = webAppElem.getChildNodes();
203         for (int i = 0, len = nodeList.getLength(); i < len; i++)
204         {
205             Node n = nodeList.item(i);
206             if (n.getNodeType() == Node.ELEMENT_NODE)
207             {
208                 if (n.getNodeName().equals("servlet"))
209                 {
210                     readServlet((Element)n);
211                 }
212                 if (n.getNodeName().equals("servlet-mapping"))
213                 {
214                     readServletMapping((Element)n);
215                 }
216                 if (n.getNodeName().equals("filter"))
217                 {
218                     readFilter((Element)n);
219                 }
220                 if (n.getNodeName().equals("filter-mapping"))
221                 {
222                     readFilterMapping((Element)n);
223                 }
224                 if (n.getNodeName().equals("error-page"))
225                 {
226                     _webXml.setErrorPagePresent(true);
227                 }
228             }
229             else
230             {
231                 if (log.isLoggable(Level.FINE))
232                 {
233                     log.fine("Ignored node '" + n.getNodeName() + "' of type " + n.getNodeType());
234                 }
235             }
236         }
237     }
238 
239     private void readServlet(Element servletElem)
240     {
241         String servletName = null;
242         String servletClass = null;
243         NodeList nodeList = servletElem.getChildNodes();
244         for (int i = 0, len = nodeList.getLength(); i < len; i++)
245         {
246             Node n = nodeList.item(i);
247             if (n.getNodeType() == Node.ELEMENT_NODE)
248             {
249                 if (n.getNodeName().equals("servlet-name"))
250                 {
251                     servletName = XmlUtils.getElementText((Element)n);
252                 }
253                 else if (n.getNodeName().equals("servlet-class"))
254                 {
255                     servletClass = org.apache.myfaces.shared.util.xml.XmlUtils.getElementText((Element)n).trim();
256                 }
257                 else if (n.getNodeName().equals("description") || n.getNodeName().equals("load-on-startup") 
258                         || n.getNodeName().equals("init-param"))
259                 {
260                     //ignore
261                 }
262                 else
263                 {
264                     if (log.isLoggable(Level.FINE))
265                     {
266                         log.fine("Ignored element '" + n.getNodeName() + "' as child of '" + 
267                                 servletElem.getNodeName() + "'.");
268                     }
269                 }
270             }
271             else
272             {
273                 if (log.isLoggable(Level.FINE))
274                 {
275                     log.fine("Ignored node '" + n.getNodeName() + "' of type " + n.getNodeType());
276                 }
277             }
278         }
279         _webXml.addServlet(servletName, servletClass);
280     }
281 
282 
283     private void readServletMapping(Element servletMappingElem)
284     {
285         String servletName = null;
286         String urlPattern = null;
287         NodeList nodeList = servletMappingElem.getChildNodes();
288         for (int i = 0, len = nodeList.getLength(); i < len; i++)
289         {
290             Node n = nodeList.item(i);
291             if (n.getNodeType() == Node.ELEMENT_NODE)
292             {
293                 if (n.getNodeName().equals("servlet-name"))
294                 {
295                     servletName = org.apache.myfaces.shared.util.xml.XmlUtils.getElementText((Element)n);
296                 }
297                 else if (n.getNodeName().equals("url-pattern"))
298                 {
299                     urlPattern = org.apache.myfaces.shared.util.xml.XmlUtils.getElementText((Element)n).trim();
300                 }
301                 else
302                 {
303                     if (log.isLoggable(Level.FINE))
304                     {
305                         log.fine("Ignored element '" + n.getNodeName() + "' as child of '" + 
306                                 servletMappingElem.getNodeName() + "'.");
307                     }
308                 }
309             }
310             else
311             {
312                 if (log.isLoggable(Level.FINE))
313                 {
314                     log.fine("Ignored node '" + n.getNodeName() + "' of type " + n.getNodeType());
315                 }
316             }
317         }
318         urlPattern = urlPattern.trim();
319         _webXml.addServletMapping(servletName, urlPattern);
320     }
321 
322     private void readFilter(Element filterElem)
323     {
324         String filterName = null;
325         String filterClass = null;
326         NodeList nodeList = filterElem.getChildNodes();
327         for (int i = 0, len = nodeList.getLength(); i < len; i++)
328         {
329             Node n = nodeList.item(i);
330             if (n.getNodeType() == Node.ELEMENT_NODE)
331             {
332                 if (n.getNodeName().equals("filter-name"))
333                 {
334                     filterName = XmlUtils.getElementText((Element)n).trim();
335                 }
336                 else if (n.getNodeName().equals("filter-class"))
337                 {
338                     filterClass = org.apache.myfaces.shared.util.xml.XmlUtils.getElementText((Element)n).trim();
339                 }
340                 else if (n.getNodeName().equals("description") || n.getNodeName().equals("init-param"))
341                 {
342                     //ignore
343                 }
344                 else
345                 {
346                     if (log.isLoggable(Level.FINE))
347                     {
348                         log.fine("Ignored element '" + n.getNodeName() + "' as child of '" + 
349                                 filterElem.getNodeName() + "'.");
350                     }
351                 }
352             }
353             else
354             {
355                 if (log.isLoggable(Level.FINE))
356                 {
357                     log.fine("Ignored node '" + n.getNodeName() + "' of type " + n.getNodeType());
358                 }
359             }
360         }
361         _webXml.addFilter(filterName, filterClass);
362     }
363 
364 
365     private void readFilterMapping(Element filterMappingElem)
366     {
367         String filterName = null;
368         String urlPattern = null;
369         NodeList nodeList = filterMappingElem.getChildNodes();
370         for (int i = 0, len = nodeList.getLength(); i < len; i++)
371         {
372             Node n = nodeList.item(i);
373             if (n.getNodeType() == Node.ELEMENT_NODE)
374             {
375                 if (n.getNodeName().equals("filter-name"))
376                 {
377                     filterName = org.apache.myfaces.shared.util.xml.XmlUtils.getElementText((Element)n).trim();
378                 }
379                 else if (n.getNodeName().equals("url-pattern"))
380                 {
381                     urlPattern = org.apache.myfaces.shared.util.xml.XmlUtils.getElementText((Element)n).trim();
382                 }
383                 else if (n.getNodeName().equals("servlet-name"))
384                 {
385                     // we are not interested in servlet-name based mapping - for now
386                 }
387                 else
388                 {
389                     if (log.isLoggable(Level.FINE))
390                     {
391                         log.fine("Ignored element '" + n.getNodeName() + "' as child of '" + 
392                                 filterMappingElem.getNodeName() + "'.");
393                     }
394                 }
395             }
396             else
397             {
398                 if (log.isLoggable(Level.FINE))
399                 {
400                     log.fine("Ignored node '" + n.getNodeName() + "' of type " + n.getNodeType());
401                 }
402             }
403         }
404         _webXml.addFilterMapping(filterName, urlPattern);
405     }
406 }