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.config;
20  
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.io.Reader;
24  import java.net.URL;
25  import java.net.URLConnection;
26  import java.util.logging.Level;
27  import java.util.logging.Logger;
28  
29  import javax.faces.context.ExternalContext;
30  import javax.xml.XMLConstants;
31  import javax.xml.parsers.ParserConfigurationException;
32  import javax.xml.parsers.SAXParser;
33  import javax.xml.parsers.SAXParserFactory;
34  import javax.xml.transform.Source;
35  import javax.xml.transform.stream.StreamSource;
36  import javax.xml.validation.Schema;
37  import javax.xml.validation.SchemaFactory;
38  import javax.xml.validation.Validator;
39  
40  import org.apache.myfaces.shared.util.ClassUtils;
41  import org.w3c.dom.ls.LSInput;
42  import org.w3c.dom.ls.LSResourceResolver;
43  import org.xml.sax.Attributes;
44  import org.xml.sax.ErrorHandler;
45  import org.xml.sax.SAXException;
46  import org.xml.sax.SAXParseException;
47  import org.xml.sax.helpers.DefaultHandler;
48  
49  public class ConfigFilesXmlValidationUtils
50  {
51      public final static LSResourceResolver JAVAEE_5_LS_RESOURCE_RESOLVER = new ValidatorLSResourceResolver();
52      public final static ErrorHandler VALIDATION_ERROR_HANDLER = new ValidationErrorHandler(); 
53  
54      private final static String FACES_CONFIG_SCHEMA_PATH_12 = "org/apache/myfaces/resource/web-facesconfig_1_2.xsd";
55      private final static String FACES_CONFIG_SCHEMA_PATH_20 = "org/apache/myfaces/resource/web-facesconfig_2_0.xsd";
56      private final static String FACES_TAGLIB_SCHEMA_PATH = "org/apache/myfaces/resource/web-facelettaglibrary_2_0.xsd";
57  
58      public static class LSInputImpl implements LSInput
59      {
60          private final String _publicId;
61          private final String _systemId;
62          private final String _baseURI;
63          private final InputStream _input;
64          
65          public LSInputImpl(String publicId,
66                  String systemId, String baseURI, InputStream input)
67          {
68              super();
69              _publicId = publicId;
70              _systemId = systemId;
71              _baseURI = baseURI;
72              _input = input;
73          }
74  
75          public String getBaseURI()
76          {
77              return _baseURI;
78          }
79  
80          public InputStream getByteStream()
81          {
82              return _input;
83          }
84  
85          public boolean getCertifiedText()
86          {
87              return false;
88          }
89  
90          public Reader getCharacterStream()
91          {
92              return null;
93          }
94  
95          public String getEncoding()
96          {
97              return null;
98          }
99  
100         public String getPublicId()
101         {
102             return _publicId;
103         }
104 
105         public String getStringData()
106         {
107             return null;
108         }
109 
110         public String getSystemId()
111         {
112             return _systemId;
113         }
114 
115         public void setBaseURI(String baseURI)
116         {
117         }
118 
119         public void setByteStream(InputStream byteStream)
120         {
121         }
122 
123         public void setCertifiedText(boolean certifiedText)
124         {
125         }
126 
127         public void setCharacterStream(Reader characterStream)
128         {
129         }
130 
131         public void setEncoding(String encoding)
132         {
133         }
134 
135         public void setPublicId(String publicId)
136         {
137         }
138 
139         public void setStringData(String stringData)
140         {
141         }
142 
143         public void setSystemId(String systemId)
144         {
145         }
146     }
147     
148     public static class ValidatorLSResourceResolver implements LSResourceResolver
149     {
150 
151         public LSInput resolveResource(String type, String namespaceURI,
152                 String publicId, String systemId, String baseURI)
153         {
154             if ("http://www.w3.org/TR/REC-xml".equals(type) && "datatypes.dtd".equals(systemId))
155             {
156                 return new LSInputImpl(publicId, systemId, baseURI, ClassUtils.getResourceAsStream("org/apache/myfaces/resource/datatypes.dtd"));
157             }
158             if ("-//W3C//DTD XMLSCHEMA 200102//EN".equals(publicId) && "XMLSchema.dtd".equals(systemId))
159             {
160                 return new LSInputImpl(publicId, systemId, baseURI, ClassUtils.getResourceAsStream("org/apache/myfaces/resource/XMLSchema.dtd"));
161             }
162             if ("http://java.sun.com/xml/ns/javaee".equals(namespaceURI))
163             {
164                 if ("javaee_5.xsd".equals(systemId))
165                 {
166                     return new LSInputImpl(publicId, systemId, baseURI, ClassUtils.getResourceAsStream("org/apache/myfaces/resource/javaee_5.xsd"));
167                 }
168             }
169             if ("http://www.w3.org/XML/1998/namespace".equals(namespaceURI))
170             {
171                 return new LSInputImpl(publicId, systemId, baseURI, ClassUtils.getResourceAsStream("org/apache/myfaces/resource/xml.xsd")); 
172             }
173             return null;
174         }
175         
176     } 
177     
178     public static class ValidationErrorHandler implements ErrorHandler
179     {
180         public void fatalError(SAXParseException exception) throws SAXException
181         {
182             throw exception;
183         }
184         
185         public void error(SAXParseException exception) throws SAXException
186         {
187             Logger log = Logger.getLogger(ConfigFilesXmlValidationUtils.class.getName());
188             log.log(Level.SEVERE, exception.getMessage(), exception);
189         }
190 
191         public void warning(SAXParseException exception) throws SAXException
192         {
193             Logger log = Logger.getLogger(ConfigFilesXmlValidationUtils.class.getName());
194             log.log(Level.WARNING, exception.getMessage(), exception);
195         }
196     }
197     
198     public static void validateFacesConfigFile(URL xmlFile,
199             ExternalContext externalContext, String version) throws SAXException, IOException
200     {
201         SchemaFactory schemaFactory = SchemaFactory
202                 .newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
203 
204         Source schemaFile = getFacesConfigSchemaFileAsSource(externalContext, version);
205         if (schemaFile == null)
206         {
207             throw new IOException("Could not find schema file for validation.");
208         }
209         
210         schemaFactory.setResourceResolver(JAVAEE_5_LS_RESOURCE_RESOLVER);
211         Schema schema = schemaFactory.newSchema(schemaFile);
212 
213         Validator validator = schema.newValidator();
214         URLConnection conn = xmlFile.openConnection();
215         conn.setUseCaches(false);
216         InputStream is = conn.getInputStream();
217         Source source = new StreamSource(is);
218         validator.setErrorHandler(VALIDATION_ERROR_HANDLER);
219         validator.validate(source);
220     }
221 
222     private static Source getFacesConfigSchemaFileAsSource(ExternalContext externalContext, String version)
223     {
224         String xmlSchema = "1.2".equals(version) ? FACES_CONFIG_SCHEMA_PATH_12 : FACES_CONFIG_SCHEMA_PATH_20; 
225         
226         InputStream stream = ClassUtils.getResourceAsStream(xmlSchema);
227         
228         if (stream == null)
229         {
230            stream = externalContext.getResourceAsStream(xmlSchema);
231         }
232 
233         if (stream == null) {
234             return null;
235         }
236         
237         return new StreamSource(stream);
238     }
239 
240     public static final String getFacesConfigVersion(URL url)
241     {
242         URLConnection conn = null;
243         InputStream input = null;
244         String result = "2.0";
245 
246         try
247         {
248             SAXParserFactory factory = SAXParserFactory.newInstance();
249             SAXParser parser;
250             FacesConfigVersionCheckHandler handler = new FacesConfigVersionCheckHandler();
251 
252             // We need to create a non-validating, non-namespace aware parser used to simply check
253             // which version of the facelets taglib document we are dealing with.
254 
255             factory.setNamespaceAware(false);
256             factory.setFeature("http://xml.org/sax/features/validation", false);
257             factory.setValidating(false);
258 
259             parser = factory.newSAXParser();
260 
261             conn = url.openConnection();
262             conn.setUseCaches(false);
263             input = conn.getInputStream();
264 
265             try
266             {
267                 parser.parse(input, handler);
268             }
269 
270             catch (SAXException e)
271             {
272                 // This is as a result of our aborted parse, so ignore.
273             }
274 
275             result = handler.isVersion20OrLater() ? "2.0" : (handler
276                     .isVersion12() ? "1.2" : "1.1");
277         }
278 
279         catch (Throwable e)
280         {
281             // Most likely a result of our aborted parse, so ignore.
282         }
283 
284         finally
285         {
286             if (input != null)
287             {
288                 try
289                 {
290                     input.close();
291                 }
292 
293                 catch (Throwable e)
294                 {
295                 }
296             }
297         }
298 
299         return result;
300     }
301 
302     private static class FacesConfigVersionCheckHandler extends DefaultHandler
303     {
304         private boolean version12;
305         private boolean version20OrLater;
306 
307         public boolean isVersion12()
308         {
309             return this.version12;
310         }
311 
312         public boolean isVersion20OrLater()
313         {
314             return this.version20OrLater;
315         }
316 
317         @Override
318         public void startElement(String uri, String localName, String name,
319                 Attributes attributes) throws SAXException
320         {
321             if (name.equals("faces-config"))
322             {
323                 int length = attributes.getLength();
324 
325                 for (int i = 0; i < length; i++)
326                 {
327                     String attrName = attributes.getLocalName(i);
328                     attrName = (attrName != null) ? ((attrName.length() > 0) ? attrName
329                             : attributes.getQName(i))
330                             : attributes.getQName(i);
331                     if (attrName.equals("version"))
332                     {
333                         if (attributes.getValue(i).equals("1.2"))
334                         {
335                             this.version12 = true;
336                             this.version20OrLater = false;
337                         }
338                         else
339                         {
340                             this.version20OrLater = true;
341                             this.version12 = false;
342                         }
343                     }
344                 }
345             }
346         }
347     }
348     
349     public static void validateFaceletTagLibFile(URL xmlFile, ExternalContext externalContext, String version)
350         throws SAXException, IOException, ParserConfigurationException
351     {
352         SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
353         
354         Source schemaFile = getFaceletSchemaFileAsSource(externalContext);
355         if (schemaFile == null)
356         {
357             throw new IOException("Could not find schema file for validation.");
358         }
359         schemaFactory.setResourceResolver(ConfigFilesXmlValidationUtils.JAVAEE_5_LS_RESOURCE_RESOLVER);
360         Schema schema = schemaFactory.newSchema(schemaFile);
361     
362         Validator validator = schema.newValidator();
363         URLConnection conn = xmlFile.openConnection();
364         conn.setUseCaches(false);
365         InputStream is = conn.getInputStream();
366         Source source = new StreamSource(is);
367         validator.setErrorHandler(VALIDATION_ERROR_HANDLER);
368         validator.validate(source);
369     }
370     
371     private static Source getFaceletSchemaFileAsSource(ExternalContext externalContext)
372     {
373         InputStream stream = ClassUtils.getResourceAsStream(FACES_TAGLIB_SCHEMA_PATH);
374         
375         if (stream == null)
376         {
377            stream = externalContext.getResourceAsStream(FACES_TAGLIB_SCHEMA_PATH);
378         }
379     
380         if (stream == null) {
381             return null;
382         }
383         
384         return new StreamSource(stream);
385     }
386     
387     public static final String getFaceletTagLibVersion(URL url)
388     {
389         if (isTaglibDocument20OrLater(url))
390         {
391             return "2.0";
392         }
393         else
394         {
395             return "1.0";
396         }
397     }
398 
399     private static final boolean isTaglibDocument20OrLater (URL url)
400     {
401         URLConnection conn = null;
402         InputStream input = null;
403         boolean result = false;
404         
405         try
406         {
407             SAXParserFactory factory = SAXParserFactory.newInstance();
408             SAXParser parser;
409             VersionCheckHandler handler = new VersionCheckHandler();
410             
411             // We need to create a non-validating, non-namespace aware parser used to simply check
412             // which version of the facelets taglib document we are dealing with.
413 
414             factory.setNamespaceAware(false);
415             factory.setFeature("http://xml.org/sax/features/validation", false);
416             factory.setValidating(false);
417             
418             parser = factory.newSAXParser();
419             
420             conn = url.openConnection();
421             conn.setUseCaches(false);
422             input = conn.getInputStream();
423             
424             try
425             {
426                 parser.parse (input, handler);
427             }
428             
429             catch (SAXException e)
430             {
431                 // This is as a result of our aborted parse, so ignore.
432             }
433             
434             result = handler.isVersion20OrLater();
435         }
436         
437         catch (Throwable e)
438         {
439             // Most likely a result of our aborted parse, so ignore.
440         }
441         
442         finally
443         {
444             if (input != null)
445             {
446                 try
447                 {
448                     input.close();
449                 }
450                 
451                 catch (Throwable e)
452                 {
453                 }
454             }
455         }
456         
457         return result;
458     }
459 
460     
461     /*
462      * We need this class to do a quick check on a facelets taglib document to see if it's
463      * a pre-2.0 document.  If it is, we really need to construct a DTD validating, non-namespace
464      * aware parser. Otherwise, we have to construct a schema validating, namespace-aware parser.
465      */
466     private static class VersionCheckHandler extends DefaultHandler
467     {
468         private boolean version20OrLater;
469         
470         public boolean isVersion20OrLater ()
471         {
472             return this.version20OrLater;
473         }
474         
475         @Override
476         public void startElement (String uri, String localName, String name, Attributes attributes) throws SAXException
477         {
478             if (name.equals ("facelet-taglib"))
479             {
480                 int length = attributes.getLength();
481                 
482                 for (int i = 0; i < length; i++)
483                 {
484                     String attrName = attributes.getLocalName(i);
485                     attrName = (attrName != null) ? ( (attrName.length() > 0) ? attrName : attributes.getQName(i)) : attributes.getQName(i);
486                     if (attrName.equals ("version"))
487                     {
488                         // This document has a "version" attribute in the <facelet-taglib> element, so
489                         // it must be a 2.0 or later document as this attribute was never required before.
490 
491                         this.version20OrLater = true;
492                     }
493                 }
494                 
495                 // Throw a dummy parsing exception to terminate parsing as there really isn't any need to go any
496                 // further.
497                 // -= Leonardo Uribe =- THIS IS NOT GOOD PRACTICE! It is better to let the checker continue that                
498                 // throw an exception, and run this one only when project stage != production.
499                 //throw new SAXException();
500             }
501         }
502     }
503 }