001: /*
002: * Copyright 1999,2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.apache.jasper.compiler;
018:
019: import java.io.InputStream;
020: import java.util.Iterator;
021: import java.util.Vector;
022:
023: import javax.servlet.ServletContext;
024:
025: import org.apache.commons.logging.Log;
026: import org.apache.commons.logging.LogFactory;
027: import org.apache.jasper.JasperException;
028: import org.apache.jasper.xmlparser.ParserUtils;
029: import org.apache.jasper.xmlparser.TreeNode;
030:
031: /**
032: * Handles the jsp-config element in WEB_INF/web.xml. This is used
033: * for specifying the JSP configuration information on a JSP page
034: *
035: * @author Kin-man Chung
036: */
037:
038: public class JspConfig {
039:
040: private static final String WEB_XML = "/WEB-INF/web.xml";
041:
042: // Logger
043: private static Log log = LogFactory.getLog(JspConfig.class);
044:
045: private Vector jspProperties = null;
046: private ServletContext ctxt;
047: private boolean initialized = false;
048:
049: private String defaultIsXml = null; // unspecified
050: private String defaultIsELIgnored = null; // unspecified
051: private String defaultIsScriptingInvalid = "false";
052: private JspProperty defaultJspProperty;
053:
054: public JspConfig(ServletContext ctxt) {
055: this .ctxt = ctxt;
056: }
057:
058: private void processWebDotXml(ServletContext ctxt)
059: throws JasperException {
060:
061: InputStream is = null;
062:
063: try {
064: is = ctxt.getResourceAsStream(WEB_XML);
065: if (is == null) {
066: // no web.xml
067: return;
068: }
069:
070: ParserUtils pu = new ParserUtils();
071: TreeNode webApp = pu.parseXMLDocument(WEB_XML, is);
072: if (webApp == null
073: || !"2.4".equals(webApp.findAttribute("version"))) {
074: defaultIsELIgnored = "true";
075: return;
076: }
077: TreeNode jspConfig = webApp.findChild("jsp-config");
078: if (jspConfig == null) {
079: return;
080: }
081:
082: jspProperties = new Vector();
083: Iterator jspPropertyList = jspConfig
084: .findChildren("jsp-property-group");
085: while (jspPropertyList.hasNext()) {
086:
087: TreeNode element = (TreeNode) jspPropertyList.next();
088: Iterator list = element.findChildren();
089:
090: Vector urlPatterns = new Vector();
091: String pageEncoding = null;
092: String scriptingInvalid = null;
093: String elIgnored = null;
094: String isXml = null;
095: Vector includePrelude = new Vector();
096: Vector includeCoda = new Vector();
097:
098: while (list.hasNext()) {
099:
100: element = (TreeNode) list.next();
101: String tname = element.getName();
102:
103: if ("url-pattern".equals(tname))
104: urlPatterns.addElement(element.getBody());
105: else if ("page-encoding".equals(tname))
106: pageEncoding = element.getBody();
107: else if ("is-xml".equals(tname))
108: isXml = element.getBody();
109: else if ("el-ignored".equals(tname))
110: elIgnored = element.getBody();
111: else if ("scripting-invalid".equals(tname))
112: scriptingInvalid = element.getBody();
113: else if ("include-prelude".equals(tname))
114: includePrelude.addElement(element.getBody());
115: else if ("include-coda".equals(tname))
116: includeCoda.addElement(element.getBody());
117: }
118:
119: if (urlPatterns.size() == 0) {
120: continue;
121: }
122:
123: // Add one JspPropertyGroup for each URL Pattern. This makes
124: // the matching logic easier.
125: for (int p = 0; p < urlPatterns.size(); p++) {
126: String urlPattern = (String) urlPatterns
127: .elementAt(p);
128: String path = null;
129: String extension = null;
130:
131: if (urlPattern.indexOf('*') < 0) {
132: // Exact match
133: path = urlPattern;
134: } else {
135: int i = urlPattern.lastIndexOf('/');
136: String file;
137: if (i >= 0) {
138: path = urlPattern.substring(0, i + 1);
139: file = urlPattern.substring(i + 1);
140: } else {
141: file = urlPattern;
142: }
143:
144: // pattern must be "*", or of the form "*.jsp"
145: if (file.equals("*")) {
146: extension = "*";
147: } else if (file.startsWith("*.")) {
148: extension = file.substring(file
149: .indexOf('.') + 1);
150: }
151:
152: // The url patterns are reconstructed as the follwoing:
153: // path != null, extension == null: / or /foo/bar.ext
154: // path == null, extension != null: *.ext
155: // path != null, extension == "*": /foo/*
156: boolean isStar = "*".equals(extension);
157: if ((path == null && (extension == null || isStar))
158: || (path != null && !isStar)) {
159: if (log.isWarnEnabled()) {
160: log
161: .warn(Localizer
162: .getMessage(
163: "jsp.warning.bad.urlpattern.propertygroup",
164: urlPattern));
165: }
166: continue;
167: }
168: }
169:
170: JspProperty property = new JspProperty(isXml,
171: elIgnored, scriptingInvalid, pageEncoding,
172: includePrelude, includeCoda);
173: JspPropertyGroup propertyGroup = new JspPropertyGroup(
174: path, extension, property);
175:
176: jspProperties.addElement(propertyGroup);
177: }
178: }
179: } finally {
180: if (is != null) {
181: try {
182: is.close();
183: } catch (Throwable t) {
184: }
185: }
186: }
187: }
188:
189: private void init() throws JasperException {
190:
191: if (!initialized) {
192: processWebDotXml(ctxt);
193: defaultJspProperty = new JspProperty(defaultIsXml,
194: defaultIsELIgnored, defaultIsScriptingInvalid,
195: null, null, null);
196: initialized = true;
197: }
198: }
199:
200: /**
201: * Select the property group that has more restrictive url-pattern.
202: * In case of tie, select the first.
203: */
204: private JspPropertyGroup selectProperty(JspPropertyGroup prev,
205: JspPropertyGroup curr) {
206: if (prev == null) {
207: return curr;
208: }
209: if (prev.getExtension() == null) {
210: // exact match
211: return prev;
212: }
213: if (curr.getExtension() == null) {
214: // exact match
215: return curr;
216: }
217: String prevPath = prev.getPath();
218: String currPath = curr.getPath();
219: if (prevPath == null && currPath == null) {
220: // Both specifies a *.ext, keep the first one
221: return prev;
222: }
223: if (prevPath == null && currPath != null) {
224: return curr;
225: }
226: if (prevPath != null && currPath == null) {
227: return prev;
228: }
229: if (prevPath.length() >= currPath.length()) {
230: return prev;
231: }
232: return curr;
233: }
234:
235: /**
236: * Find a property that best matches the supplied resource.
237: * @param uri the resource supplied.
238: * @return a JspProperty indicating the best match, or some default.
239: */
240: public JspProperty findJspProperty(String uri)
241: throws JasperException {
242:
243: init();
244:
245: // JSP Configuration settings do not apply to tag files
246: if (jspProperties == null || uri.endsWith(".tag")
247: || uri.endsWith(".tagx")) {
248: return defaultJspProperty;
249: }
250:
251: String uriPath = null;
252: int index = uri.lastIndexOf('/');
253: if (index >= 0) {
254: uriPath = uri.substring(0, index + 1);
255: }
256: String uriExtension = null;
257: index = uri.lastIndexOf('.');
258: if (index >= 0) {
259: uriExtension = uri.substring(index + 1);
260: }
261:
262: Vector includePreludes = new Vector();
263: Vector includeCodas = new Vector();
264:
265: JspPropertyGroup isXmlMatch = null;
266: JspPropertyGroup elIgnoredMatch = null;
267: JspPropertyGroup scriptingInvalidMatch = null;
268: JspPropertyGroup pageEncodingMatch = null;
269:
270: Iterator iter = jspProperties.iterator();
271: while (iter.hasNext()) {
272:
273: JspPropertyGroup jpg = (JspPropertyGroup) iter.next();
274: JspProperty jp = jpg.getJspProperty();
275:
276: // (arrays will be the same length)
277: String extension = jpg.getExtension();
278: String path = jpg.getPath();
279:
280: if (extension == null) {
281: // exact match pattern: /a/foo.jsp
282: if (!uri.equals(path)) {
283: // not matched;
284: continue;
285: }
286: } else {
287: // Matching patterns *.ext or /p/*
288: if (path != null && uriPath != null
289: && !uriPath.startsWith(path)) {
290: // not matched
291: continue;
292: }
293: if (!extension.equals("*")
294: && !extension.equals(uriExtension)) {
295: // not matched
296: continue;
297: }
298: }
299: // We have a match
300: // Add include-preludes and include-codas
301: if (jp.getIncludePrelude() != null) {
302: includePreludes.addAll(jp.getIncludePrelude());
303: }
304: if (jp.getIncludeCoda() != null) {
305: includeCodas.addAll(jp.getIncludeCoda());
306: }
307:
308: // If there is a previous match for the same property, remember
309: // the one that is more restrictive.
310: if (jp.isXml() != null) {
311: isXmlMatch = selectProperty(isXmlMatch, jpg);
312: }
313: if (jp.isELIgnored() != null) {
314: elIgnoredMatch = selectProperty(elIgnoredMatch, jpg);
315: }
316: if (jp.isScriptingInvalid() != null) {
317: scriptingInvalidMatch = selectProperty(
318: scriptingInvalidMatch, jpg);
319: }
320: if (jp.getPageEncoding() != null) {
321: pageEncodingMatch = selectProperty(pageEncodingMatch,
322: jpg);
323: }
324: }
325:
326: String isXml = defaultIsXml;
327: String isELIgnored = defaultIsELIgnored;
328: String isScriptingInvalid = defaultIsScriptingInvalid;
329: String pageEncoding = null;
330:
331: if (isXmlMatch != null) {
332: isXml = isXmlMatch.getJspProperty().isXml();
333: }
334: if (elIgnoredMatch != null) {
335: isELIgnored = elIgnoredMatch.getJspProperty().isELIgnored();
336: }
337: if (scriptingInvalidMatch != null) {
338: isScriptingInvalid = scriptingInvalidMatch.getJspProperty()
339: .isScriptingInvalid();
340: }
341: if (pageEncodingMatch != null) {
342: pageEncoding = pageEncodingMatch.getJspProperty()
343: .getPageEncoding();
344: }
345:
346: return new JspProperty(isXml, isELIgnored, isScriptingInvalid,
347: pageEncoding, includePreludes, includeCodas);
348: }
349:
350: /**
351: * To find out if an uri matches an url pattern in jsp config. If so,
352: * then the uri is a JSP page. This is used primarily for jspc.
353: */
354: public boolean isJspPage(String uri) throws JasperException {
355:
356: init();
357: if (jspProperties == null) {
358: return false;
359: }
360:
361: String uriPath = null;
362: int index = uri.lastIndexOf('/');
363: if (index >= 0) {
364: uriPath = uri.substring(0, index + 1);
365: }
366: String uriExtension = null;
367: index = uri.lastIndexOf('.');
368: if (index >= 0) {
369: uriExtension = uri.substring(index + 1);
370: }
371:
372: Iterator iter = jspProperties.iterator();
373: while (iter.hasNext()) {
374:
375: JspPropertyGroup jpg = (JspPropertyGroup) iter.next();
376: JspProperty jp = jpg.getJspProperty();
377:
378: String extension = jpg.getExtension();
379: String path = jpg.getPath();
380:
381: if (extension == null) {
382: if (uri.equals(path)) {
383: // There is an exact match
384: return true;
385: }
386: } else {
387: if ((path == null || path.equals(uriPath))
388: && (extension.equals("*") || extension
389: .equals(uriExtension))) {
390: // Matches *, *.ext, /p/*, or /p/*.ext
391: return true;
392: }
393: }
394: }
395: return false;
396: }
397:
398: static class JspPropertyGroup {
399: private String path;
400: private String extension;
401: private JspProperty jspProperty;
402:
403: JspPropertyGroup(String path, String extension,
404: JspProperty jspProperty) {
405: this .path = path;
406: this .extension = extension;
407: this .jspProperty = jspProperty;
408: }
409:
410: public String getPath() {
411: return path;
412: }
413:
414: public String getExtension() {
415: return extension;
416: }
417:
418: public JspProperty getJspProperty() {
419: return jspProperty;
420: }
421: }
422:
423: static public class JspProperty {
424:
425: private String isXml;
426: private String elIgnored;
427: private String scriptingInvalid;
428: private String pageEncoding;
429: private Vector includePrelude;
430: private Vector includeCoda;
431:
432: public JspProperty(String isXml, String elIgnored,
433: String scriptingInvalid, String pageEncoding,
434: Vector includePrelude, Vector includeCoda) {
435:
436: this .isXml = isXml;
437: this .elIgnored = elIgnored;
438: this .scriptingInvalid = scriptingInvalid;
439: this .pageEncoding = pageEncoding;
440: this .includePrelude = includePrelude;
441: this .includeCoda = includeCoda;
442: }
443:
444: public String isXml() {
445: return isXml;
446: }
447:
448: public String isELIgnored() {
449: return elIgnored;
450: }
451:
452: public String isScriptingInvalid() {
453: return scriptingInvalid;
454: }
455:
456: public String getPageEncoding() {
457: return pageEncoding;
458: }
459:
460: public Vector getIncludePrelude() {
461: return includePrelude;
462: }
463:
464: public Vector getIncludeCoda() {
465: return includeCoda;
466: }
467: }
468: }
|