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  
20  package org.apache.myfaces.tobago.util;
21  
22  import org.w3c.dom.Document;
23  import org.w3c.dom.Node;
24  import org.w3c.dom.NodeList;
25  import org.xml.sax.SAXException;
26  
27  import javax.faces.application.ViewHandler;
28  import javax.faces.context.ExternalContext;
29  import javax.faces.context.FacesContext;
30  import javax.servlet.ServletContext;
31  import javax.xml.parsers.DocumentBuilder;
32  import javax.xml.parsers.DocumentBuilderFactory;
33  import javax.xml.parsers.ParserConfigurationException;
34  import java.io.IOException;
35  import java.io.InputStream;
36  import java.net.URL;
37  import java.net.URLConnection;
38  import java.util.ArrayList;
39  import java.util.Enumeration;
40  import java.util.HashMap;
41  import java.util.List;
42  import java.util.Map;
43  
44  public class WebXmlUtils {
45  
46    private static final Map<Class<Throwable>, String> ERROR_PAGE_LOCATIONS = new HashMap<>();
47  
48    public static String getErrorPageLocation(final Throwable exception) {
49      if (ERROR_PAGE_LOCATIONS.size() <= 0) {
50        init();
51      }
52  
53      String location = null;
54  
55      Class<?> exceptionClass = exception.getClass();
56      while (exceptionClass != null && location == null) {
57        location = ERROR_PAGE_LOCATIONS.get(exceptionClass);
58        exceptionClass = exceptionClass.getSuperclass();
59      }
60  
61      if (location == null) {
62        location = ERROR_PAGE_LOCATIONS.get(null);
63      }
64  
65      return location;
66    }
67  
68    private static void init() {
69      final FacesContext facesContext = FacesContext.getCurrentInstance();
70      final ExternalContext externalContext = facesContext.getExternalContext();
71  
72      try {
73        final List<Document> webXmls = getWebXmls(facesContext);
74  
75        String locationDefault = null;
76        String location500 = null;
77  
78        for (final Document document : webXmls) {
79          final NodeList errorPages = document.getElementsByTagName("error-page");
80  
81          for (int i = 0; i < errorPages.getLength(); i++) {
82            final Node errorPage = errorPages.item(i);
83  
84            String errorCode = null;
85            String exceptionType = null;
86            String location = null;
87  
88            final NodeList children = errorPage.getChildNodes();
89            for (int j = 0; j < children.getLength(); j++) {
90              final Node child = children.item(j);
91              final String name = child.getNodeName();
92  
93              if ("error-code".equals(name)) {
94                errorCode = child.getFirstChild().getNodeValue().trim();
95              } else if ("exception-type".equals(name)) {
96                exceptionType = child.getFirstChild().getNodeValue().trim();
97              } else if ("location".equals(name)) {
98                location = child.getFirstChild().getNodeValue().trim();
99              }
100           }
101 
102           if (exceptionType != null) {
103             final Class<Throwable> key = (Class<Throwable>) Class.forName(exceptionType);
104             final String value = normalizePath(externalContext, location);
105             ERROR_PAGE_LOCATIONS.put(key, value);
106           } else if ("500".equals(errorCode)) {
107             location500 = location;
108           } else if (errorCode == null && exceptionType == null) {
109             locationDefault = location;
110           }
111         }
112       }
113 
114       if (!ERROR_PAGE_LOCATIONS.containsKey(null)) {
115         final String value = normalizePath(externalContext, location500 != null ? location500 : locationDefault);
116         ERROR_PAGE_LOCATIONS.put(null, value);
117       }
118     } catch (IOException | ParserConfigurationException | ClassNotFoundException | SAXException e) {
119       throw new UnsupportedOperationException(e);
120     }
121   }
122 
123   private static List<Document> getWebXmls(final FacesContext facesContext)
124       throws ParserConfigurationException, IOException, SAXException {
125     final List<Document> webXmls = new ArrayList<>();
126 
127     final DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
128     for (final URL url : getWebXmlUrls(facesContext)) {
129       webXmls.add(getWebXml(documentBuilder, url));
130     }
131 
132     return webXmls;
133   }
134 
135   private static List<URL> getWebXmlUrls(final FacesContext facesContext) throws IOException {
136     final List<URL> urls = new ArrayList<>();
137     final ServletContext servletContext = (ServletContext) facesContext.getExternalContext().getContext();
138     urls.add(servletContext.getResource("/WEB-INF/web.xml"));
139 
140     final Enumeration<URL> webFragments = Thread.currentThread().getContextClassLoader()
141         .getResources("META-INF/web-fragment.xml");
142     while (webFragments.hasMoreElements()) {
143       urls.add(webFragments.nextElement());
144     }
145 
146     return urls;
147   }
148 
149   private static Document getWebXml(final DocumentBuilder documentBuilder, final URL url)
150       throws ParserConfigurationException, IOException, SAXException {
151     if (url != null) {
152       final URLConnection connection = url.openConnection();
153       connection.setUseCaches(false);
154 
155       try (final InputStream input = connection.getInputStream()) {
156         final Document document = documentBuilder.parse(input);
157         document.getDocumentElement().normalize();
158         return document;
159       }
160     } else {
161       return null;
162     }
163   }
164 
165   private static String normalizePath(final ExternalContext externalContext, final String path) {
166     if (path == null) {
167       return null;
168     }
169 
170     if (externalContext.getRequestPathInfo() != null) {
171       final String prefix = externalContext.getRequestServletPath();
172       if (path.startsWith(prefix)) {
173         return path.substring(prefix.length());
174       } else {
175         return path;
176       }
177     } else {
178       final String suffixInitParam = externalContext.getInitParameter(ViewHandler.FACELETS_SUFFIX_PARAM_NAME);
179       final String suffix = suffixInitParam != null ? suffixInitParam : ViewHandler.DEFAULT_FACELETS_SUFFIX;
180 
181       if (path.endsWith(suffix)) {
182         return path;
183       } else {
184         return path.substring(0, path.lastIndexOf('.')) + suffix;
185       }
186     }
187   }
188 }