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