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.commons.resourcehandler.config;
20  
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.net.URL;
24  import java.net.URLConnection;
25  import java.util.ArrayList;
26  import java.util.List;
27  
28  import javax.faces.FacesException;
29  import javax.faces.context.ExternalContext;
30  import javax.faces.context.FacesContext;
31  import javax.xml.parsers.ParserConfigurationException;
32  import javax.xml.parsers.SAXParser;
33  import javax.xml.parsers.SAXParserFactory;
34  
35  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
36  import org.apache.myfaces.commons.resourcehandler.config.element.Library;
37  import org.apache.myfaces.commons.resourcehandler.config.element.MyFacesResourcesConfig;
38  import org.apache.myfaces.commons.util.ClassUtils;
39  import org.apache.myfaces.commons.util.WebConfigParamUtils;
40  import org.xml.sax.Attributes;
41  import org.xml.sax.InputSource;
42  import org.xml.sax.Locator;
43  import org.xml.sax.SAXException;
44  import org.xml.sax.SAXParseException;
45  import org.xml.sax.XMLReader;
46  import org.xml.sax.helpers.DefaultHandler;
47  
48  /**
49   * XML-parser for MyFacesResourcesConfig.
50   *
51   * @author Jakob Korherr
52   * @author Leonardo Uribe
53   */
54  public class MyFacesResourceHandlerConfigParser
55  {
56      
57      /**
58       * This param allow to override the default strategy to locate myfaces-resources-config.xml files, that will be parsed later. In this way
59       * it is possible to include new source locations or handle cases like OSGi specific setup.   
60       */
61      @JSFWebConfigParam
62      public static final String INIT_PARAM_EXTENDED_RESOURCE_HANDLER_CONFIG_URL_PROVIDER = "org.apache.myfaces.commons.EXTENDED_RESOURCE_HANDLER_CONFIG_URL_PROVIDER";
63      public static final String INIT_PARAM_EXTENDED_RESOURCE_HANDLER_CONFIG_URL_PROVIDER_DEFAULT = DefaultMyFacesResourceHandlerUrlProvider.class.getName();
64      
65      public MyFacesResourcesConfig parse(FacesContext facesContext)
66      {
67          String resourceHandlerUrlProviderClassName = WebConfigParamUtils.getStringInitParameter(facesContext.getExternalContext(),
68                  INIT_PARAM_EXTENDED_RESOURCE_HANDLER_CONFIG_URL_PROVIDER, INIT_PARAM_EXTENDED_RESOURCE_HANDLER_CONFIG_URL_PROVIDER_DEFAULT);
69          
70          MyFacesResourceHandlerUrlProvider urlProvider = (MyFacesResourceHandlerUrlProvider) ClassUtils.newInstance(resourceHandlerUrlProviderClassName);
71          
72          List<URL> configUrls = new ArrayList<URL>(); 
73  
74          try
75          {
76              configUrls.addAll(urlProvider.getMetaInfConfigurationResources(facesContext.getExternalContext()));
77          }
78          catch(IOException e)
79          {
80              throw new FacesException("Cannot get META-INF/myfaces-resources-config.xml urls", e);
81          }
82          
83          try
84          {
85              URL webInfConfig = urlProvider.getWebInfConfigurationResource(facesContext.getExternalContext());
86              if (webInfConfig != null)
87              {
88                  configUrls.add(webInfConfig);
89              }
90          }
91          catch(IOException e)
92          {
93              throw new FacesException(e);
94          }
95          
96          List<MyFacesResourcesConfig> configList = new ArrayList<MyFacesResourcesConfig>();
97          // we have at least one config file and thus can start parsing
98          for (URL url : configUrls)
99          {
100             try
101             {
102                 MyFacesResourcesConfig mrc = parseFile(url);
103                 if (mrc != null)
104                 {
105                     configList.add(mrc);
106                 }
107             }
108             catch (IOException e) {
109                 throw new FacesException("Cannot parse file "+url);
110             }
111         }
112         
113         MyFacesResourcesConfig finalConfig = new MyFacesResourcesConfig();
114         for (MyFacesResourcesConfig cfg : configList)
115         {
116             finalConfig.merge(cfg);
117         }
118         return finalConfig;
119     }
120     
121     private MyFacesResourcesConfig parseFile(URL configFileUrl) throws IOException
122     {
123         InputStream is = null;
124         URLConnection conn = null;
125         try
126         {
127             ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
128             boolean schemaValidating = false;
129 
130             // parse file
131             LibraryHandler handler = new LibraryHandler(configFileUrl);
132             SAXParser parser = createSAXParser(handler, externalContext, schemaValidating);
133             conn = configFileUrl.openConnection();
134             conn.setUseCaches(false);
135             is = conn.getInputStream();
136             parser.parse(is, handler);
137             return handler.getMyFacesResourcesConfig();
138         }
139         catch (SAXException e)
140         {
141             IOException ioe = new IOException("Error parsing [" + configFileUrl + "]: ");
142             ioe.initCause(e);
143             throw ioe;
144         }
145         catch (ParserConfigurationException e)
146         {
147             IOException ioe = new IOException("Error parsing [" + configFileUrl + "]: ");
148             ioe.initCause(e);
149             throw ioe;
150         }
151         finally
152         {
153             if (is != null)
154                 is.close();
155         }
156     }
157 
158     private static final SAXParser createSAXParser(LibraryHandler handler, ExternalContext externalContext, boolean schemaValidating) throws SAXException,
159     ParserConfigurationException
160     {
161         SAXParserFactory factory = SAXParserFactory.newInstance();
162         
163         //Just parse it and do not validate, because it is not necessary.
164         factory.setNamespaceAware(true);
165         factory.setFeature("http://xml.org/sax/features/validation", false);
166         factory.setValidating(false);
167         
168         SAXParser parser = factory.newSAXParser();
169         XMLReader reader = parser.getXMLReader();
170         reader.setErrorHandler(handler);
171         reader.setEntityResolver(handler);
172         return parser;
173     }
174     
175     private static class LibraryHandler extends DefaultHandler
176     {
177         private final URL source;
178 
179         private final StringBuffer buffer;
180         
181         private Locator locator;
182         
183         private MyFacesResourcesConfig config;
184         
185         private String libraryName;
186         
187         private String redirectName;
188         
189         private String requestPath;
190         
191         public LibraryHandler(URL source)
192         {
193             this.source = source;
194             this.buffer = new StringBuffer(64);
195             this.config = null;
196         }
197         
198         public MyFacesResourcesConfig getMyFacesResourcesConfig()
199         {
200             return this.config;
201         }
202 
203         public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException
204         {
205             this.buffer.setLength(0);
206             if ("myfaces-resources-config".equals(qName))
207             {
208                 this.config = new MyFacesResourcesConfig();
209             }
210             else if ("library".equals(qName))
211             {
212                 this.libraryName = null;
213                 this.redirectName = null;
214                 this.requestPath = null;
215             }
216         }
217         
218         public void endElement(String uri, String localName, String qName) throws SAXException
219         {
220             try
221             {
222                 if (this.config == null)
223                 {
224                     return;
225                 }
226                 
227                 if ("library".equals(localName))
228                 {
229                     Library l = new Library();
230                     l.setName(libraryName);
231                     l.setRedirectName(redirectName);
232                     l.setRequestPath(requestPath);
233                     config.addLibrary(l);
234                 }
235                 else if ("library-name".equals(localName))
236                 {
237                     libraryName = captureBuffer();
238                 }
239                 else if ("redirect-name".equals(localName))
240                 {
241                     redirectName = captureBuffer();
242                 }
243                 else if ("request-path".equals(localName))
244                 {
245                     requestPath = captureBuffer();
246                 }
247             }
248             catch (Exception e)
249             {
250                 SAXException saxe = new SAXException("Error Handling [" + this.source + "@"
251                         + this.locator.getLineNumber() + "," + this.locator.getColumnNumber() + "] <" + qName + ">");
252                 saxe.initCause(e);
253                 throw saxe;
254             }
255         }
256 
257         public void characters(char[] ch, int start, int length) throws SAXException
258         {
259             this.buffer.append(ch, start, length);
260         }
261         
262         private String captureBuffer() throws Exception
263         {
264             String s = this.buffer.toString().trim();
265             if (s.length() == 0)
266             {
267                 throw new Exception("Value Cannot be Empty");
268             }
269             this.buffer.setLength(0);
270             return s;
271         }        
272         
273         public InputSource resolveEntity(String publicId, String systemId) throws SAXException
274         {
275             return null;
276         }
277 
278         public void error(SAXParseException e) throws SAXException
279         {
280             SAXException saxe = new SAXException("Error Handling [" + this.source + "@" + e.getLineNumber() + ","
281                     + e.getColumnNumber() + "]");
282             saxe.initCause(e);
283             throw saxe;
284         }
285 
286         public void setDocumentLocator(Locator locator)
287         {
288             this.locator = locator;
289         }
290 
291         public void fatalError(SAXParseException e) throws SAXException
292         {
293             throw e;
294         }
295 
296         public void warning(SAXParseException e) throws SAXException
297         {
298             throw e;
299         }
300     }
301 
302 }