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