View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.struts2.jasper.compiler;
19  
20  import com.opensymphony.xwork2.util.logging.Logger;
21  import com.opensymphony.xwork2.util.logging.LoggerFactory;
22  import org.apache.struts2.jasper.JasperException;
23  import org.apache.struts2.jasper.xmlparser.ParserUtils;
24  import org.apache.struts2.jasper.xmlparser.TreeNode;
25  import org.xml.sax.InputSource;
26  
27  import javax.servlet.ServletContext;
28  import java.io.InputStream;
29  import java.net.URL;
30  import java.util.Iterator;
31  import java.util.Vector;
32  
33  /***
34   * Handles the jsp-config element in WEB_INF/web.xml.  This is used
35   * for specifying the JSP configuration information on a JSP page
36   *
37   * @author Kin-man Chung
38   */
39  
40  public class JspConfig {
41  
42      private static final String WEB_XML = "/WEB-INF/web.xml";
43  
44      // Logger
45      private Logger log = LoggerFactory.getLogger(JspConfig.class);
46  
47      private Vector jspProperties = null;
48      private ServletContext ctxt;
49      private boolean initialized = false;
50  
51      private String defaultIsXml = null;                // unspecified
52      private String defaultIsELIgnored = null;        // unspecified
53      private String defaultIsScriptingInvalid = "false";
54      private JspProperty defaultJspProperty;
55  
56      public JspConfig(ServletContext ctxt) {
57          this.ctxt = ctxt;
58      }
59  
60      private void processWebDotXml(ServletContext ctxt) throws JasperException {
61  
62          InputStream is = null;
63  
64          try {
65              URL uri = ctxt.getResource(WEB_XML);
66              if (uri == null) {
67                  // no web.xml
68                  return;
69              }
70  
71              is = uri.openStream();
72              InputSource ip = new InputSource(is);
73              ip.setSystemId(uri.toExternalForm());
74  
75              ParserUtils pu = new ParserUtils();
76              TreeNode webApp = pu.parseXMLDocument(WEB_XML, ip);
77  
78              if (webApp == null
79                      || !"2.4".equals(webApp.findAttribute("version"))) {
80                  defaultIsELIgnored = "true";
81                  return;
82              }
83              TreeNode jspConfig = webApp.findChild("jsp-config");
84              if (jspConfig == null) {
85                  return;
86              }
87  
88              jspProperties = new Vector();
89              Iterator jspPropertyList = jspConfig.findChildren("jsp-property-group");
90              while (jspPropertyList.hasNext()) {
91  
92                  TreeNode element = (TreeNode) jspPropertyList.next();
93                  Iterator list = element.findChildren();
94  
95                  Vector urlPatterns = new Vector();
96                  String pageEncoding = null;
97                  String scriptingInvalid = null;
98                  String elIgnored = null;
99                  String isXml = null;
100                 Vector includePrelude = new Vector();
101                 Vector includeCoda = new Vector();
102 
103                 while (list.hasNext()) {
104 
105                     element = (TreeNode) list.next();
106                     String tname = element.getName();
107 
108                     if ("url-pattern".equals(tname))
109                         urlPatterns.addElement(element.getBody());
110                     else if ("page-encoding".equals(tname))
111                         pageEncoding = element.getBody();
112                     else if ("is-xml".equals(tname))
113                         isXml = element.getBody();
114                     else if ("el-ignored".equals(tname))
115                         elIgnored = element.getBody();
116                     else if ("scripting-invalid".equals(tname))
117                         scriptingInvalid = element.getBody();
118                     else if ("include-prelude".equals(tname))
119                         includePrelude.addElement(element.getBody());
120                     else if ("include-coda".equals(tname))
121                         includeCoda.addElement(element.getBody());
122                 }
123 
124                 if (urlPatterns.size() == 0) {
125                     continue;
126                 }
127 
128                 // Add one JspPropertyGroup for each URL Pattern.  This makes
129                 // the matching logic easier.
130                 for (int p = 0; p < urlPatterns.size(); p++) {
131                     String urlPattern = (String) urlPatterns.elementAt(p);
132                     String path = null;
133                     String extension = null;
134 
135                     if (urlPattern.indexOf('*') < 0) {
136                         // Exact match
137                         path = urlPattern;
138                     } else {
139                         int i = urlPattern.lastIndexOf('/');
140                         String file;
141                         if (i >= 0) {
142                             path = urlPattern.substring(0, i + 1);
143                             file = urlPattern.substring(i + 1);
144                         } else {
145                             file = urlPattern;
146                         }
147 
148                         // pattern must be "*", or of the form "*.jsp"
149                         if (file.equals("*")) {
150                             extension = "*";
151                         } else if (file.startsWith("*.")) {
152                             extension = file.substring(file.indexOf('.') + 1);
153                         }
154 
155                         // The url patterns are reconstructed as the follwoing:
156                         // path != null, extension == null:  / or /foo/bar.ext
157                         // path == null, extension != null:  *.ext
158                         // path != null, extension == "*":   /foo/*
159                         boolean isStar = "*".equals(extension);
160                         if ((path == null && (extension == null || isStar))
161                                 || (path != null && !isStar)) {
162                             if (log.isWarnEnabled()) {
163                                 log.warn(Localizer.getMessage(
164                                         "jsp.warning.bad.urlpattern.propertygroup",
165                                         urlPattern));
166                             }
167                             continue;
168                         }
169                     }
170 
171                     JspProperty property = new JspProperty(isXml,
172                             elIgnored,
173                             scriptingInvalid,
174                             pageEncoding,
175                             includePrelude,
176                             includeCoda);
177                     JspPropertyGroup propertyGroup =
178                             new JspPropertyGroup(path, extension, property);
179 
180                     jspProperties.addElement(propertyGroup);
181                 }
182             }
183         } catch (Exception ex) {
184             throw new JasperException(ex);
185         } finally {
186             if (is != null) {
187                 try {
188                     is.close();
189                 } catch (Throwable t) {
190                 }
191             }
192         }
193     }
194 
195     private void init() throws JasperException {
196 
197         if (!initialized) {
198             processWebDotXml(ctxt);
199             defaultJspProperty = new JspProperty(defaultIsXml,
200                     defaultIsELIgnored,
201                     defaultIsScriptingInvalid,
202                     null, null, null);
203             initialized = true;
204         }
205     }
206 
207     /**
208      * Select the property group that has more restrictive url-pattern.
209      * In case of tie, select the first.
210      */
211     private JspPropertyGroup selectProperty(JspPropertyGroup prev,
212                                             JspPropertyGroup curr) {
213         if (prev == null) {
214             return curr;
215         }
216         if (prev.getExtension() == null) {
217             // exact match
218             return prev;
219         }
220         if (curr.getExtension() == null) {
221             // exact match
222             return curr;
223         }
224         String prevPath = prev.getPath();
225         String currPath = curr.getPath();
226         if (prevPath == null && currPath == null) {
227             // Both specifies a *.ext, keep the first one
228             return prev;
229         }
230         if (prevPath == null && currPath != null) {
231             return curr;
232         }
233         if (prevPath != null && currPath == null) {
234             return prev;
235         }
236         if (prevPath.length() >= currPath.length()) {
237             return prev;
238         }
239         return curr;
240     }
241 
242 
243     /***
244      * Find a property that best matches the supplied resource.
245      *
246      * @param uri the resource supplied.
247      * @return a JspProperty indicating the best match, or some default.
248      */
249     public JspProperty findJspProperty(String uri) throws JasperException {
250 
251         init();
252 
253         // JSP Configuration settings do not apply to tag files            
254         if (jspProperties == null || uri.endsWith(".tag")
255                 || uri.endsWith(".tagx")) {
256             return defaultJspProperty;
257         }
258 
259         String uriPath = null;
260         int index = uri.lastIndexOf('/');
261         if (index >= 0) {
262             uriPath = uri.substring(0, index + 1);
263         }
264         String uriExtension = null;
265         index = uri.lastIndexOf('.');
266         if (index >= 0) {
267             uriExtension = uri.substring(index + 1);
268         }
269 
270         Vector includePreludes = new Vector();
271         Vector includeCodas = new Vector();
272 
273         JspPropertyGroup isXmlMatch = null;
274         JspPropertyGroup elIgnoredMatch = null;
275         JspPropertyGroup scriptingInvalidMatch = null;
276         JspPropertyGroup pageEncodingMatch = null;
277 
278         Iterator iter = jspProperties.iterator();
279         while (iter.hasNext()) {
280 
281             JspPropertyGroup jpg = (JspPropertyGroup) iter.next();
282             JspProperty jp = jpg.getJspProperty();
283 
284             // (arrays will be the same length)
285             String extension = jpg.getExtension();
286             String path = jpg.getPath();
287 
288             if (extension == null) {
289                 // exact match pattern: /a/foo.jsp
290                 if (!uri.equals(path)) {
291                     // not matched;
292                     continue;
293                 }
294             } else {
295                 // Matching patterns *.ext or /p/*
296                 if (path != null && uriPath != null &&
297                         !uriPath.startsWith(path)) {
298                     // not matched
299                     continue;
300                 }
301                 if (!extension.equals("*") &&
302                         !extension.equals(uriExtension)) {
303                     // not matched
304                     continue;
305                 }
306             }
307             // We have a match
308             // Add include-preludes and include-codas
309             if (jp.getIncludePrelude() != null) {
310                 includePreludes.addAll(jp.getIncludePrelude());
311             }
312             if (jp.getIncludeCoda() != null) {
313                 includeCodas.addAll(jp.getIncludeCoda());
314             }
315 
316             // If there is a previous match for the same property, remember
317             // the one that is more restrictive.
318             if (jp.isXml() != null) {
319                 isXmlMatch = selectProperty(isXmlMatch, jpg);
320             }
321             if (jp.isELIgnored() != null) {
322                 elIgnoredMatch = selectProperty(elIgnoredMatch, jpg);
323             }
324             if (jp.isScriptingInvalid() != null) {
325                 scriptingInvalidMatch =
326                         selectProperty(scriptingInvalidMatch, jpg);
327             }
328             if (jp.getPageEncoding() != null) {
329                 pageEncodingMatch = selectProperty(pageEncodingMatch, jpg);
330             }
331         }
332 
333 
334         String isXml = defaultIsXml;
335         String isELIgnored = defaultIsELIgnored;
336         String isScriptingInvalid = defaultIsScriptingInvalid;
337         String pageEncoding = null;
338 
339         if (isXmlMatch != null) {
340             isXml = isXmlMatch.getJspProperty().isXml();
341         }
342         if (elIgnoredMatch != null) {
343             isELIgnored = elIgnoredMatch.getJspProperty().isELIgnored();
344         }
345         if (scriptingInvalidMatch != null) {
346             isScriptingInvalid =
347                     scriptingInvalidMatch.getJspProperty().isScriptingInvalid();
348         }
349         if (pageEncodingMatch != null) {
350             pageEncoding = pageEncodingMatch.getJspProperty().getPageEncoding();
351         }
352 
353         return new JspProperty(isXml, isELIgnored, isScriptingInvalid,
354                 pageEncoding, includePreludes, includeCodas);
355     }
356 
357     /**
358      * To find out if an uri matches an url pattern in jsp config.  If so,
359      * then the uri is a JSP page.  This is used primarily for jspc.
360      */
361     public boolean isJspPage(String uri) throws JasperException {
362 
363         init();
364         if (jspProperties == null) {
365             return false;
366         }
367 
368         String uriPath = null;
369         int index = uri.lastIndexOf('/');
370         if (index >= 0) {
371             uriPath = uri.substring(0, index + 1);
372         }
373         String uriExtension = null;
374         index = uri.lastIndexOf('.');
375         if (index >= 0) {
376             uriExtension = uri.substring(index + 1);
377         }
378 
379         Iterator iter = jspProperties.iterator();
380         while (iter.hasNext()) {
381 
382             JspPropertyGroup jpg = (JspPropertyGroup) iter.next();
383 
384             String extension = jpg.getExtension();
385             String path = jpg.getPath();
386 
387             if (extension == null) {
388                 if (uri.equals(path)) {
389                     // There is an exact match
390                     return true;
391                 }
392             } else {
393                 if ((path == null || path.equals(uriPath)) &&
394                         (extension.equals("*") || extension.equals(uriExtension))) {
395                     // Matches *, *.ext, /p/*, or /p/*.ext
396                     return true;
397                 }
398             }
399         }
400         return false;
401     }
402 
403     static class JspPropertyGroup {
404         private String path;
405         private String extension;
406         private JspProperty jspProperty;
407 
408         JspPropertyGroup(String path, String extension,
409                          JspProperty jspProperty) {
410             this.path = path;
411             this.extension = extension;
412             this.jspProperty = jspProperty;
413         }
414 
415         public String getPath() {
416             return path;
417         }
418 
419         public String getExtension() {
420             return extension;
421         }
422 
423         public JspProperty getJspProperty() {
424             return jspProperty;
425         }
426     }
427 
428     static public class JspProperty {
429 
430         private String isXml;
431         private String elIgnored;
432         private String scriptingInvalid;
433         private String pageEncoding;
434         private Vector includePrelude;
435         private Vector includeCoda;
436 
437         public JspProperty(String isXml, String elIgnored,
438                            String scriptingInvalid, String pageEncoding,
439                            Vector includePrelude, Vector includeCoda) {
440 
441             this.isXml = isXml;
442             this.elIgnored = elIgnored;
443             this.scriptingInvalid = scriptingInvalid;
444             this.pageEncoding = pageEncoding;
445             this.includePrelude = includePrelude;
446             this.includeCoda = includeCoda;
447         }
448 
449         public String isXml() {
450             return isXml;
451         }
452 
453         public String isELIgnored() {
454             return elIgnored;
455         }
456 
457         public String isScriptingInvalid() {
458             return scriptingInvalid;
459         }
460 
461         public String getPageEncoding() {
462             return pageEncoding;
463         }
464 
465         public Vector getIncludePrelude() {
466             return includePrelude;
467         }
468 
469         public Vector getIncludeCoda() {
470             return includeCoda;
471         }
472     }
473 }