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                 else if ("javaee_web_services_client_1_2.xsd".equals(systemId))
169                 {
170                     return new LSInputImpl(publicId, systemId, baseURI, ClassUtils.getResourceAsStream("org/apache/myfaces/resource/javaee_web_services_client_1_2.xsd"));
171                 }
172             }
173             if ("http://www.w3.org/XML/1998/namespace".equals(namespaceURI))
174             {
175                 return new LSInputImpl(publicId, systemId, baseURI, ClassUtils.getResourceAsStream("org/apache/myfaces/resource/xml.xsd")); 
176             }
177             return null;
178         }
179         
180     } 
181     
182     public static class ValidationErrorHandler implements ErrorHandler
183     {
184         public void fatalError(SAXParseException exception) throws SAXException
185         {
186             throw exception;
187         }
188         
189         public void error(SAXParseException exception) throws SAXException
190         {
191             Logger log = Logger.getLogger(ConfigFilesXmlValidationUtils.class.getName());
192             log.log(Level.SEVERE, exception.getMessage(), exception);
193         }
194 
195         public void warning(SAXParseException exception) throws SAXException
196         {
197             Logger log = Logger.getLogger(ConfigFilesXmlValidationUtils.class.getName());
198             log.log(Level.WARNING, exception.getMessage(), exception);
199         }
200     }
201     
202     public static void validateFacesConfigFile(URL xmlFile,
203             ExternalContext externalContext, String version) throws SAXException, IOException
204     {
205         SchemaFactory schemaFactory = SchemaFactory
206                 .newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
207 
208         Source schemaFile = getFacesConfigSchemaFileAsSource(externalContext, version);
209         if (schemaFile == null)
210         {
211             throw new IOException("Could not find schema file for validation.");
212         }
213         
214         schemaFactory.setResourceResolver(JAVAEE_5_LS_RESOURCE_RESOLVER);
215         Schema schema = schemaFactory.newSchema(schemaFile);
216 
217         Validator validator = schema.newValidator();
218         URLConnection conn = xmlFile.openConnection();
219         conn.setUseCaches(false);
220         InputStream is = conn.getInputStream();
221         Source source = new StreamSource(is);
222         validator.setErrorHandler(VALIDATION_ERROR_HANDLER);
223         validator.validate(source);
224     }
225 
226     private static Source getFacesConfigSchemaFileAsSource(ExternalContext externalContext, String version)
227     {
228         String xmlSchema = "1.2".equals(version) ? FACES_CONFIG_SCHEMA_PATH_12 : FACES_CONFIG_SCHEMA_PATH_20; 
229         
230         InputStream stream = ClassUtils.getResourceAsStream(xmlSchema);
231         
232         if (stream == null)
233         {
234            stream = externalContext.getResourceAsStream(xmlSchema);
235         }
236 
237         if (stream == null) {
238             return null;
239         }
240         
241         return new StreamSource(stream);
242     }
243 
244     public static final String getFacesConfigVersion(URL url)
245     {
246         URLConnection conn = null;
247         InputStream input = null;
248         String result = "2.0";
249 
250         try
251         {
252             SAXParserFactory factory = SAXParserFactory.newInstance();
253             SAXParser parser;
254             FacesConfigVersionCheckHandler handler = new FacesConfigVersionCheckHandler();
255 
256             // We need to create a non-validating, non-namespace aware parser used to simply check
257             // which version of the facelets taglib document we are dealing with.
258 
259             factory.setNamespaceAware(false);
260             factory.setFeature("http://xml.org/sax/features/validation", false);
261             factory.setValidating(false);
262 
263             parser = factory.newSAXParser();
264 
265             conn = url.openConnection();
266             conn.setUseCaches(false);
267             input = conn.getInputStream();
268 
269             try
270             {
271                 parser.parse(input, handler);
272             }
273 
274             catch (SAXException e)
275             {
276                 // This is as a result of our aborted parse, so ignore.
277             }
278 
279             result = handler.isVersion20OrLater() ? "2.0" : (handler
280                     .isVersion12() ? "1.2" : "1.1");
281         }
282 
283         catch (Throwable e)
284         {
285             // Most likely a result of our aborted parse, so ignore.
286         }
287 
288         finally
289         {
290             if (input != null)
291             {
292                 try
293                 {
294                     input.close();
295                 }
296 
297                 catch (Throwable e)
298                 {
299                 }
300             }
301         }
302 
303         return result;
304     }
305 
306     private static class FacesConfigVersionCheckHandler extends DefaultHandler
307     {
308         private boolean version12;
309         private boolean version20OrLater;
310 
311         public boolean isVersion12()
312         {
313             return this.version12;
314         }
315 
316         public boolean isVersion20OrLater()
317         {
318             return this.version20OrLater;
319         }
320 
321         @Override
322         public void startElement(String uri, String localName, String name,
323                 Attributes attributes) throws SAXException
324         {
325             if (name.equals("faces-config"))
326             {
327                 int length = attributes.getLength();
328 
329                 for (int i = 0; i < length; i++)
330                 {
331                     String attrName = attributes.getLocalName(i);
332                     attrName = (attrName != null) ? ((attrName.length() > 0) ? attrName
333                             : attributes.getQName(i))
334                             : attributes.getQName(i);
335                     if (attrName.equals("version"))
336                     {
337                         if (attributes.getValue(i).equals("1.2"))
338                         {
339                             this.version12 = true;
340                             this.version20OrLater = false;
341                         }
342                         else
343                         {
344                             this.version20OrLater = true;
345                             this.version12 = false;
346                         }
347                     }
348                 }
349             }
350         }
351     }
352     
353     public static void validateFaceletTagLibFile(URL xmlFile, ExternalContext externalContext, String version)
354         throws SAXException, IOException, ParserConfigurationException
355     {
356         SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
357         
358         Source schemaFile = getFaceletSchemaFileAsSource(externalContext);
359         if (schemaFile == null)
360         {
361             throw new IOException("Could not find schema file for validation.");
362         }
363         schemaFactory.setResourceResolver(ConfigFilesXmlValidationUtils.JAVAEE_5_LS_RESOURCE_RESOLVER);
364         Schema schema = schemaFactory.newSchema(schemaFile);
365     
366         Validator validator = schema.newValidator();
367         URLConnection conn = xmlFile.openConnection();
368         conn.setUseCaches(false);
369         InputStream is = conn.getInputStream();
370         Source source = new StreamSource(is);
371         validator.setErrorHandler(VALIDATION_ERROR_HANDLER);
372         validator.validate(source);
373     }
374     
375     private static Source getFaceletSchemaFileAsSource(ExternalContext externalContext)
376     {
377         InputStream stream = ClassUtils.getResourceAsStream(FACES_TAGLIB_SCHEMA_PATH);
378         
379         if (stream == null)
380         {
381            stream = externalContext.getResourceAsStream(FACES_TAGLIB_SCHEMA_PATH);
382         }
383     
384         if (stream == null) {
385             return null;
386         }
387         
388         return new StreamSource(stream);
389     }
390     
391     public static final String getFaceletTagLibVersion(URL url)
392     {
393         if (isTaglibDocument20OrLater(url))
394         {
395             return "2.0";
396         }
397         else
398         {
399             return "1.0";
400         }
401     }
402 
403     private static final boolean isTaglibDocument20OrLater (URL url)
404     {
405         URLConnection conn = null;
406         InputStream input = null;
407         boolean result = false;
408         
409         try
410         {
411             SAXParserFactory factory = SAXParserFactory.newInstance();
412             SAXParser parser;
413             VersionCheckHandler handler = new VersionCheckHandler();
414             
415             // We need to create a non-validating, non-namespace aware parser used to simply check
416             // which version of the facelets taglib document we are dealing with.
417 
418             factory.setNamespaceAware(false);
419             factory.setFeature("http://xml.org/sax/features/validation", false);
420             factory.setValidating(false);
421             
422             parser = factory.newSAXParser();
423             
424             conn = url.openConnection();
425             conn.setUseCaches(false);
426             input = conn.getInputStream();
427             
428             try
429             {
430                 parser.parse (input, handler);
431             }
432             
433             catch (SAXException e)
434             {
435                 // This is as a result of our aborted parse, so ignore.
436             }
437             
438             result = handler.isVersion20OrLater();
439         }
440         
441         catch (Throwable e)
442         {
443             // Most likely a result of our aborted parse, so ignore.
444         }
445         
446         finally
447         {
448             if (input != null)
449             {
450                 try
451                 {
452                     input.close();
453                 }
454                 
455                 catch (Throwable e)
456                 {
457                 }
458             }
459         }
460         
461         return result;
462     }
463 
464     
465     /*
466      * We need this class to do a quick check on a facelets taglib document to see if it's
467      * a pre-2.0 document.  If it is, we really need to construct a DTD validating, non-namespace
468      * aware parser. Otherwise, we have to construct a schema validating, namespace-aware parser.
469      */
470     private static class VersionCheckHandler extends DefaultHandler
471     {
472         private boolean version20OrLater;
473         
474         public boolean isVersion20OrLater ()
475         {
476             return this.version20OrLater;
477         }
478         
479         @Override
480         public void startElement (String uri, String localName, String name, Attributes attributes) throws SAXException
481         {
482             if (name.equals ("facelet-taglib"))
483             {
484                 int length = attributes.getLength();
485                 
486                 for (int i = 0; i < length; i++)
487                 {
488                     String attrName = attributes.getLocalName(i);
489                     attrName = (attrName != null) ? ( (attrName.length() > 0) ? attrName : attributes.getQName(i)) : attributes.getQName(i);
490                     if (attrName.equals ("version"))
491                     {
492                         // This document has a "version" attribute in the <facelet-taglib> element, so
493                         // it must be a 2.0 or later document as this attribute was never required before.
494 
495                         this.version20OrLater = true;
496                     }
497                 }
498                 
499                 // Throw a dummy parsing exception to terminate parsing as there really isn't any need to go any
500                 // further.
501                 // -= Leonardo Uribe =- THIS IS NOT GOOD PRACTICE! It is better to let the checker continue that                
502                 // throw an exception, and run this one only when project stage != production.
503                 //throw new SAXException();
504             }
505         }
506     }
507 }