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