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  /**
43   * @author Manfred Geiler (latest modification by $Author: bommel $)
44   * @version $Revision: 1187700 $ $Date: 2011-10-22 07:19:37 -0500 (Sat, 22 Oct 2011) $
45   */
46  public class WebXmlParser
47  {
48      //private static final Log log = LogFactory.getLog(WebXmlParser.class);
49      private static final Logger log = Logger.getLogger(WebXmlParser.class.getName());
50  
51      /*
52      private static final String JAXP_SCHEMA_LANGUAGE =
53          "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
54      private static final String W3C_XML_SCHEMA =
55          "http://www.w3.org/2001/XMLSchema";
56          */
57  
58      private static final String WEB_XML_PATH = "/WEB-INF/web.xml";
59  
60      private static final String WEB_APP_2_2_J2EE_SYSTEM_ID = "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd";
61      private static final String WEB_APP_2_2_SYSTEM_ID = "http://java.sun.com/dtd/web-app_2_2.dtd";
62      private static final String WEB_APP_2_2_RESOURCE  = "javax/servlet/resources/web-app_2_2.dtd";
63  
64      private static final String WEB_APP_2_3_SYSTEM_ID = "http://java.sun.com/dtd/web-app_2_3.dtd";
65      private static final String WEB_APP_2_3_RESOURCE  = "javax/servlet/resources/web-app_2_3.dtd";
66      
67      private ExternalContext _context;
68      private org.apache.myfaces.shared.webapp.webxml.WebXml _webXml;
69  
70      public WebXmlParser(ExternalContext context)
71      {
72          _context = context;
73      }
74  
75      public WebXml parse()
76      {
77          _webXml = new WebXml();
78  
79          try
80          {
81              DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
82              dbf.setIgnoringElementContentWhitespace(true);
83              dbf.setIgnoringComments(true);
84              dbf.setNamespaceAware(true);
85              dbf.setValidating(false);
86  //            dbf.setAttribute(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA);
87  
88              DocumentBuilder db = dbf.newDocumentBuilder();
89              db.setEntityResolver(new _EntityResolver());
90              db.setErrorHandler(new MyFacesErrorHandler(log));
91  
92              InputSource is = createContextInputSource(null, WEB_XML_PATH);
93  
94              if(is==null)
95              {
96                  URL url = _context.getResource(WEB_XML_PATH);
97                  log.fine("No web-xml found at : "+(url==null?" null ":url.toString()));
98                  return _webXml;
99              }
100 
101             Document document = db.parse(is);
102 
103             Element webAppElem = document.getDocumentElement();
104             if (webAppElem == null ||
105                 !webAppElem.getNodeName().equals("web-app"))
106             {
107                 throw new FacesException("No valid web-app root element found!");
108             }
109 
110             readWebApp(webAppElem);
111             
112             return _webXml;
113         }
114         catch (Exception e)
115         {
116             log.log(Level.SEVERE, "Unable to parse web.xml", e);
117             throw new FacesException(e);
118         }
119     }
120 
121     public static long getWebXmlLastModified(ExternalContext context)
122     {
123         try {
124             URL url = context.getResource(WEB_XML_PATH);
125             if (url != null)
126                 return url.openConnection().getLastModified();
127         } catch (IOException e) {
128             log.log(Level.SEVERE, "Could not find web.xml in path " + WEB_XML_PATH);
129         }
130         return 0L;
131     }
132 
133 
134     private InputSource createContextInputSource(String publicId, String systemId)
135     {
136         InputStream inStream = _context.getResourceAsStream(systemId);
137         if (inStream == null)
138         {
139             // there is no such entity
140             return null;
141         }
142         InputSource is = new InputSource(inStream);
143         is.setPublicId(publicId);
144         is.setSystemId(systemId);
145         //the next line was removed - encoding should be determined automatically out of the inputStream
146         //DEFAULT_ENCODING was ISO-8859-1
147         //is.setEncoding(DEFAULT_ENCODING);
148         return is;
149     }
150 
151     private InputSource createClassloaderInputSource(String publicId, String systemId)
152     {
153         InputStream inStream = ClassUtils.getResourceAsStream(systemId);
154         if (inStream == null)
155         {
156             // there is no such entity
157             return null;
158         }
159         InputSource is = new InputSource(inStream);
160         is.setPublicId(publicId);
161         is.setSystemId(systemId);
162         //the next line was removed - encoding should be determined automatically out of the inputStream
163         //encoding should be determined automatically out of the inputStream
164         //DEFAULT_ENCODING was ISO-8859-1
165         //is.setEncoding(DEFAULT_ENCODING);
166         return is;
167     }
168 
169     private class _EntityResolver implements EntityResolver
170     {
171         public InputSource resolveEntity(String publicId, String systemId) throws IOException
172         {
173             if (systemId == null)
174             {
175                 throw new UnsupportedOperationException("systemId must not be null");
176             }
177 
178             if (systemId.equals(WebXmlParser.WEB_APP_2_2_SYSTEM_ID) ||
179                 systemId.equals(WebXmlParser.WEB_APP_2_2_J2EE_SYSTEM_ID))
180             {
181                 //Load DTD from servlet.jar
182                 return createClassloaderInputSource(publicId, WebXmlParser.WEB_APP_2_2_RESOURCE);
183             }
184             else if (systemId.equals(WebXmlParser.WEB_APP_2_3_SYSTEM_ID))
185             {
186                 //Load DTD from servlet.jar
187                 return createClassloaderInputSource(publicId, WebXmlParser.WEB_APP_2_3_RESOURCE);
188             }
189             else
190             {
191                 //Load additional entities from web context
192                 return createContextInputSource(publicId, systemId);
193             }
194         }
195 
196     }
197 
198 
199     private void readWebApp(Element webAppElem)
200     {
201         NodeList nodeList = webAppElem.getChildNodes();
202         for (int i = 0, len = nodeList.getLength(); i < len; i++)
203         {
204             Node n = nodeList.item(i);
205             if (n.getNodeType() == Node.ELEMENT_NODE)
206             {
207                 if (n.getNodeName().equals("servlet"))
208                 {
209                     readServlet((Element)n);
210                 }
211                 if (n.getNodeName().equals("servlet-mapping"))
212                 {
213                     readServletMapping((Element)n);
214                 }
215                 if (n.getNodeName().equals("filter"))
216                 {
217                     readFilter((Element)n);
218                 }
219                 if (n.getNodeName().equals("filter-mapping"))
220                 {
221                     readFilterMapping((Element)n);
222                 }
223                 if (n.getNodeName().equals("error-page"))
224                 {
225                     _webXml.setErrorPagePresent(true);
226                 }
227             }
228             else
229             {
230                 if (log.isLoggable(Level.FINE)) log.fine("Ignored node '" + n.getNodeName() + "' of type " + n.getNodeType());
231             }
232         }
233     }
234 
235     private void readServlet(Element servletElem)
236     {
237         String servletName = null;
238         String servletClass = null;
239         NodeList nodeList = servletElem.getChildNodes();
240         for (int i = 0, len = nodeList.getLength(); i < len; i++)
241         {
242             Node n = nodeList.item(i);
243             if (n.getNodeType() == Node.ELEMENT_NODE)
244             {
245                 if (n.getNodeName().equals("servlet-name"))
246                 {
247                     servletName = XmlUtils.getElementText((Element)n);
248                 }
249                 else if (n.getNodeName().equals("servlet-class"))
250                 {
251                     servletClass = org.apache.myfaces.shared.util.xml.XmlUtils.getElementText((Element)n).trim();
252                 }
253                 else if (n.getNodeName().equals("description") || n.getNodeName().equals("load-on-startup") || n.getNodeName().equals("init-param"))
254                 {
255                     //ignore
256                 }
257                 else
258                 {
259                     if (log.isLoggable(Level.FINE)) log.fine("Ignored element '" + n.getNodeName() + "' as child of '" + servletElem.getNodeName() + "'.");
260                 }
261             }
262             else
263             {
264                 if (log.isLoggable(Level.FINE)) log.fine("Ignored node '" + n.getNodeName() + "' of type " + n.getNodeType());
265             }
266         }
267         _webXml.addServlet(servletName, servletClass);
268     }
269 
270 
271     private void readServletMapping(Element servletMappingElem)
272     {
273         String servletName = null;
274         String urlPattern = null;
275         NodeList nodeList = servletMappingElem.getChildNodes();
276         for (int i = 0, len = nodeList.getLength(); i < len; i++)
277         {
278             Node n = nodeList.item(i);
279             if (n.getNodeType() == Node.ELEMENT_NODE)
280             {
281                 if (n.getNodeName().equals("servlet-name"))
282                 {
283                     servletName = org.apache.myfaces.shared.util.xml.XmlUtils.getElementText((Element)n);
284                 }
285                 else if (n.getNodeName().equals("url-pattern"))
286                 {
287                     urlPattern = org.apache.myfaces.shared.util.xml.XmlUtils.getElementText((Element)n).trim();
288                 }
289                 else
290                 {
291                     if (log.isLoggable(Level.FINE)) log.fine("Ignored element '" + n.getNodeName() + "' as child of '" + servletMappingElem.getNodeName() + "'.");
292                 }
293             }
294             else
295             {
296                 if (log.isLoggable(Level.FINE)) log.fine("Ignored node '" + n.getNodeName() + "' of type " + n.getNodeType());
297             }
298         }
299         urlPattern = urlPattern.trim();
300         _webXml.addServletMapping(servletName, urlPattern);
301     }
302 
303     private void readFilter(Element filterElem)
304     {
305         String filterName = null;
306         String filterClass = null;
307         NodeList nodeList = filterElem.getChildNodes();
308         for (int i = 0, len = nodeList.getLength(); i < len; i++)
309         {
310             Node n = nodeList.item(i);
311             if (n.getNodeType() == Node.ELEMENT_NODE)
312             {
313                 if (n.getNodeName().equals("filter-name"))
314                 {
315                     filterName = XmlUtils.getElementText((Element)n).trim();
316                 }
317                 else if (n.getNodeName().equals("filter-class"))
318                 {
319                     filterClass = org.apache.myfaces.shared.util.xml.XmlUtils.getElementText((Element)n).trim();
320                 }
321                 else if (n.getNodeName().equals("description") || n.getNodeName().equals("init-param"))
322                 {
323                     //ignore
324                 }
325                 else
326                 {
327                     if (log.isLoggable(Level.FINE)) log.fine("Ignored element '" + n.getNodeName() + "' as child of '" + filterElem.getNodeName() + "'.");
328                 }
329             }
330             else
331             {
332                 if (log.isLoggable(Level.FINE)) log.fine("Ignored node '" + n.getNodeName() + "' of type " + n.getNodeType());
333             }
334         }
335         _webXml.addFilter(filterName, filterClass);
336     }
337 
338 
339     private void readFilterMapping(Element filterMappingElem)
340     {
341         String filterName = null;
342         String urlPattern = null;
343         NodeList nodeList = filterMappingElem.getChildNodes();
344         for (int i = 0, len = nodeList.getLength(); i < len; i++)
345         {
346             Node n = nodeList.item(i);
347             if (n.getNodeType() == Node.ELEMENT_NODE)
348             {
349                 if (n.getNodeName().equals("filter-name"))
350                 {
351                     filterName = org.apache.myfaces.shared.util.xml.XmlUtils.getElementText((Element)n).trim();
352                 }
353                 else if (n.getNodeName().equals("url-pattern"))
354                 {
355                     urlPattern = org.apache.myfaces.shared.util.xml.XmlUtils.getElementText((Element)n).trim();
356                 }
357                 else if (n.getNodeName().equals("servlet-name"))
358                 {
359                     // we are not interested in servlet-name based mapping - for now
360                 }
361                 else
362                 {
363                     if (log.isLoggable(Level.FINE)) log.fine("Ignored element '" + n.getNodeName() + "' as child of '" + filterMappingElem.getNodeName() + "'.");
364                 }
365             }
366             else
367             {
368                 if (log.isLoggable(Level.FINE)) log.fine("Ignored node '" + n.getNodeName() + "' of type " + n.getNodeType());
369             }
370         }
371         _webXml.addFilterMapping(filterName, urlPattern);
372     }
373 }