2270 lines
87 KiB
Diff
2270 lines
87 KiB
Diff
Description: Fix CVE-2015-0254 XXE and RCE via XSL extension in JSTL XML tags
|
|
When an application uses <x:parse> or <x:transform> tags to process
|
|
untrusted XML documents, a request may utilize external entity
|
|
references to access resources on the host system or utilize XSLT
|
|
extensions that may allow remote execution. For more information, just go
|
|
to: http://www.securityfocus.com/archive/1/534772.
|
|
Author: The Apache Software Foundation
|
|
Bug-Debian: https://bugs.debian.org/779621
|
|
Origin: upstream, http://svn.apache.org/r1642442, http://svn.apache.org/r1642613
|
|
Forwarded: not-needed
|
|
Last-Update: 2015-03-14
|
|
|
|
Index: jakarta-taglibs-standard-1.1.2-src/standard/src/javax/servlet/jsp/jstl/tlv/ParserUtil.java
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ jakarta-taglibs-standard-1.1.2-src/standard/src/javax/servlet/jsp/jstl/tlv/ParserUtil.java
|
|
@@ -0,0 +1,86 @@
|
|
+/*
|
|
+ * Licensed to the Apache Software Foundation (ASF) under one or more
|
|
+ * contributor license agreements. See the NOTICE file distributed with
|
|
+ * this work for additional information regarding copyright ownership.
|
|
+ * The ASF licenses this file to You under the Apache License, Version 2.0
|
|
+ * (the "License"); you may not use this file except in compliance with
|
|
+ * the License. You may obtain a copy of the License at
|
|
+ *
|
|
+ * http://www.apache.org/licenses/LICENSE-2.0
|
|
+ *
|
|
+ * Unless required by applicable law or agreed to in writing, software
|
|
+ * distributed under the License is distributed on an "AS IS" BASIS,
|
|
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
+ * See the License for the specific language governing permissions and
|
|
+ * limitations under the License.
|
|
+ */
|
|
+package javax.servlet.jsp.jstl.tlv;
|
|
+
|
|
+import java.io.IOException;
|
|
+import java.io.InputStream;
|
|
+import java.security.AccessController;
|
|
+import java.security.PrivilegedAction;
|
|
+
|
|
+import javax.servlet.jsp.tagext.PageData;
|
|
+import javax.xml.XMLConstants;
|
|
+import javax.xml.parsers.ParserConfigurationException;
|
|
+import javax.xml.parsers.SAXParser;
|
|
+import javax.xml.parsers.SAXParserFactory;
|
|
+
|
|
+import org.xml.sax.SAXException;
|
|
+import org.xml.sax.SAXNotRecognizedException;
|
|
+import org.xml.sax.SAXNotSupportedException;
|
|
+import org.xml.sax.helpers.DefaultHandler;
|
|
+
|
|
+/**
|
|
+ * Support class for working with the SAX Parser.
|
|
+ */
|
|
+class ParserUtil {
|
|
+
|
|
+ private static final SAXParserFactory PARSER_FACTORY;
|
|
+ static {
|
|
+ PARSER_FACTORY = AccessController.doPrivileged(new PrivilegedAction<SAXParserFactory>() {
|
|
+ public SAXParserFactory run() {
|
|
+ ClassLoader original = Thread.currentThread().getContextClassLoader();
|
|
+ ClassLoader ours = ParserUtil.class.getClassLoader();
|
|
+ try {
|
|
+ if (original != ours) {
|
|
+ Thread.currentThread().setContextClassLoader(ours);
|
|
+ }
|
|
+ return SAXParserFactory.newInstance();
|
|
+ } finally {
|
|
+ if (original != ours) {
|
|
+ Thread.currentThread().setContextClassLoader(original);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ });
|
|
+ try {
|
|
+ PARSER_FACTORY.setValidating(true);
|
|
+ PARSER_FACTORY.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
|
|
+ } catch (ParserConfigurationException e) {
|
|
+ throw new ExceptionInInitializerError(e);
|
|
+ } catch (SAXNotRecognizedException e) {
|
|
+ throw new ExceptionInInitializerError(e);
|
|
+ } catch (SAXNotSupportedException e) {
|
|
+ throw new ExceptionInInitializerError(e);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private ParserUtil() {
|
|
+ }
|
|
+
|
|
+ static void parse(PageData pageData, DefaultHandler handler) throws ParserConfigurationException, SAXException, IOException {
|
|
+ SAXParser parser = PARSER_FACTORY.newSAXParser();
|
|
+ InputStream is = pageData.getInputStream();
|
|
+ try {
|
|
+ parser.parse(is, handler);
|
|
+ } finally {
|
|
+ try {
|
|
+ is.close();
|
|
+ } catch (IOException e) {
|
|
+ // Suppress.
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
Index: jakarta-taglibs-standard-1.1.2-src/standard/src/javax/servlet/jsp/jstl/tlv/PermittedTaglibsTLV.java
|
|
===================================================================
|
|
--- jakarta-taglibs-standard-1.1.2-src.orig/standard/src/javax/servlet/jsp/jstl/tlv/PermittedTaglibsTLV.java
|
|
+++ jakarta-taglibs-standard-1.1.2-src/standard/src/javax/servlet/jsp/jstl/tlv/PermittedTaglibsTLV.java
|
|
@@ -17,6 +17,7 @@
|
|
package javax.servlet.jsp.jstl.tlv;
|
|
|
|
import java.io.IOException;
|
|
+import java.io.InputStream;
|
|
import java.util.HashSet;
|
|
import java.util.Set;
|
|
import java.util.StringTokenizer;
|
|
@@ -92,8 +93,7 @@ public class PermittedTaglibsTLV extends
|
|
//*********************************************************************
|
|
// Validation entry point
|
|
|
|
- public synchronized ValidationMessage[] validate(
|
|
- String prefix, String uri, PageData page) {
|
|
+ public synchronized ValidationMessage[] validate(String prefix, String uri, PageData page) {
|
|
try {
|
|
|
|
// initialize
|
|
@@ -104,10 +104,7 @@ public class PermittedTaglibsTLV extends
|
|
DefaultHandler h = new PermittedTaglibsHandler();
|
|
|
|
// parse the page
|
|
- SAXParserFactory f = SAXParserFactory.newInstance();
|
|
- f.setValidating(true);
|
|
- SAXParser p = f.newSAXParser();
|
|
- p.parse(page.getInputStream(), h);
|
|
+ ParserUtil.parse(page, h);
|
|
|
|
if (failed)
|
|
return vmFromString(
|
|
Index: jakarta-taglibs-standard-1.1.2-src/standard/src/javax/servlet/jsp/jstl/tlv/ScriptFreeTLV.java
|
|
===================================================================
|
|
--- jakarta-taglibs-standard-1.1.2-src.orig/standard/src/javax/servlet/jsp/jstl/tlv/ScriptFreeTLV.java
|
|
+++ jakarta-taglibs-standard-1.1.2-src/standard/src/javax/servlet/jsp/jstl/tlv/ScriptFreeTLV.java
|
|
@@ -24,7 +24,6 @@ import javax.servlet.jsp.tagext.PageData
|
|
import javax.servlet.jsp.tagext.TagLibraryValidator;
|
|
import javax.servlet.jsp.tagext.ValidationMessage;
|
|
import javax.xml.parsers.ParserConfigurationException;
|
|
-import javax.xml.parsers.SAXParser;
|
|
import javax.xml.parsers.SAXParserFactory;
|
|
|
|
import org.xml.sax.Attributes;
|
|
@@ -100,32 +99,19 @@ public class ScriptFreeTLV extends TagLi
|
|
* @return null, if the page is valid; otherwise, a ValidationMessage[]
|
|
* containing one or more messages indicating why the page is not valid.
|
|
*/
|
|
- public ValidationMessage[] validate
|
|
- (String prefix, String uri, PageData page) {
|
|
- InputStream in = null;
|
|
- SAXParser parser;
|
|
- MyContentHandler handler = new MyContentHandler();
|
|
- try {
|
|
- synchronized (factory) {
|
|
- parser = factory.newSAXParser();
|
|
- }
|
|
- in = page.getInputStream();
|
|
- parser.parse(in, handler);
|
|
+ public ValidationMessage[] validate(String prefix, String uri, PageData page) {
|
|
+ try {
|
|
+ MyContentHandler handler = new MyContentHandler();
|
|
+ ParserUtil.parse(page, handler);
|
|
+ return handler.reportResults();
|
|
+ } catch (ParserConfigurationException e) {
|
|
+ return vmFromString(e.toString());
|
|
+ } catch (SAXException e) {
|
|
+ return vmFromString(e.toString());
|
|
+ } catch (IOException e) {
|
|
+ return vmFromString(e.toString());
|
|
+ }
|
|
}
|
|
- catch (ParserConfigurationException e) {
|
|
- return vmFromString(e.toString());
|
|
- }
|
|
- catch (SAXException e) {
|
|
- return vmFromString(e.toString());
|
|
- }
|
|
- catch (IOException e) {
|
|
- return vmFromString(e.toString());
|
|
- }
|
|
- finally {
|
|
- if (in != null) try { in.close(); } catch (IOException e) {}
|
|
- }
|
|
- return handler.reportResults();
|
|
- }
|
|
|
|
/**
|
|
* Handler for SAX events.
|
|
Index: jakarta-taglibs-standard-1.1.2-src/standard/src/org/apache/taglibs/standard/tag/common/core/ImportSupport.java
|
|
===================================================================
|
|
--- jakarta-taglibs-standard-1.1.2-src.orig/standard/src/org/apache/taglibs/standard/tag/common/core/ImportSupport.java
|
|
+++ jakarta-taglibs-standard-1.1.2-src/standard/src/org/apache/taglibs/standard/tag/common/core/ImportSupport.java
|
|
@@ -45,6 +45,7 @@ import javax.servlet.jsp.tagext.BodyTagS
|
|
import javax.servlet.jsp.tagext.TryCatchFinally;
|
|
|
|
import org.apache.taglibs.standard.resources.Resources;
|
|
+import org.apache.taglibs.standard.util.UrlUtil;
|
|
|
|
/**
|
|
* <p>Support for tag handlers for <import>, the general-purpose
|
|
@@ -60,22 +61,6 @@ public abstract class ImportSupport exte
|
|
//*********************************************************************
|
|
// Public constants
|
|
|
|
- /** <p>Valid characters in a scheme.</p>
|
|
- * <p>RFC 1738 says the following:</p>
|
|
- * <blockquote>
|
|
- * Scheme names consist of a sequence of characters. The lower
|
|
- * case letters "a"--"z", digits, and the characters plus ("+"),
|
|
- * period ("."), and hyphen ("-") are allowed. For resiliency,
|
|
- * programs interpreting URLs should treat upper case letters as
|
|
- * equivalent to lower case in scheme names (e.g., allow "HTTP" as
|
|
- * well as "http").
|
|
- * </blockquote>
|
|
- * <p>We treat as absolute any URL that begins with such a scheme name,
|
|
- * followed by a colon.</p>
|
|
- */
|
|
- public static final String VALID_SCHEME_CHARS =
|
|
- "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+.-";
|
|
-
|
|
/** Default character encoding for response. */
|
|
public static final String DEFAULT_ENCODING = "ISO-8859-1";
|
|
|
|
@@ -133,7 +118,7 @@ public abstract class ImportSupport exte
|
|
throw new NullAttributeException("import", "url");
|
|
|
|
// Record whether our URL is absolute or relative
|
|
- isAbsoluteUrl = isAbsoluteUrl();
|
|
+ isAbsoluteUrl = UrlUtil.isAbsoluteUrl(url);
|
|
|
|
try {
|
|
// If we need to expose a Reader, we've got to do it right away
|
|
@@ -494,43 +479,10 @@ public abstract class ImportSupport exte
|
|
return urlWithParams;
|
|
}
|
|
|
|
- /**
|
|
- * Returns <tt>true</tt> if our current URL is absolute,
|
|
- * <tt>false</tt> otherwise.
|
|
- */
|
|
- private boolean isAbsoluteUrl() throws JspTagException {
|
|
- return isAbsoluteUrl(url);
|
|
- }
|
|
-
|
|
-
|
|
//*********************************************************************
|
|
// Public utility methods
|
|
|
|
/**
|
|
- * Returns <tt>true</tt> if our current URL is absolute,
|
|
- * <tt>false</tt> otherwise.
|
|
- */
|
|
- public static boolean isAbsoluteUrl(String url) {
|
|
- // a null URL is not absolute, by our definition
|
|
- if (url == null)
|
|
- return false;
|
|
-
|
|
- // do a fast, simple check first
|
|
- int colonPos;
|
|
- if ((colonPos = url.indexOf(":")) == -1)
|
|
- return false;
|
|
-
|
|
- // if we DO have a colon, make sure that every character
|
|
- // leading up to it is a valid scheme character
|
|
- for (int i = 0; i < colonPos; i++)
|
|
- if (VALID_SCHEME_CHARS.indexOf(url.charAt(i)) == -1)
|
|
- return false;
|
|
-
|
|
- // if so, we've got an absolute url
|
|
- return true;
|
|
- }
|
|
-
|
|
- /**
|
|
* Strips a servlet session ID from <tt>url</tt>. The session ID
|
|
* is encoded as a URL "path parameter" beginning with "jsessionid=".
|
|
* We thus remove anything we find between ";jsessionid=" (inclusive)
|
|
Index: jakarta-taglibs-standard-1.1.2-src/standard/src/org/apache/taglibs/standard/tag/common/core/RedirectSupport.java
|
|
===================================================================
|
|
--- jakarta-taglibs-standard-1.1.2-src.orig/standard/src/org/apache/taglibs/standard/tag/common/core/RedirectSupport.java
|
|
+++ jakarta-taglibs-standard-1.1.2-src/standard/src/org/apache/taglibs/standard/tag/common/core/RedirectSupport.java
|
|
@@ -22,6 +22,8 @@ import javax.servlet.jsp.JspTagException
|
|
import javax.servlet.jsp.PageContext;
|
|
import javax.servlet.jsp.tagext.BodyTagSupport;
|
|
|
|
+import org.apache.taglibs.standard.util.UrlUtil;
|
|
+
|
|
/**
|
|
* <p>Support for tag handlers for <redirect>, JSTL 1.0's tag
|
|
* for redirecting to a new URL (with optional query parameters).</p>
|
|
@@ -90,29 +92,30 @@ public abstract class RedirectSupport ex
|
|
return EVAL_BODY_BUFFERED;
|
|
}
|
|
|
|
-
|
|
// gets the right value, encodes it, and prints or stores it
|
|
+
|
|
public int doEndTag() throws JspException {
|
|
- String result; // the eventual result
|
|
+ String result; // the eventual result
|
|
|
|
- // add (already encoded) parameters
|
|
+ // add (already encoded) parameters
|
|
String baseUrl = UrlSupport.resolveUrl(url, context, pageContext);
|
|
result = params.aggregateParams(baseUrl);
|
|
|
|
// if the URL is relative, rewrite it with 'redirect' encoding rules
|
|
HttpServletResponse response =
|
|
- ((HttpServletResponse) pageContext.getResponse());
|
|
- if (!ImportSupport.isAbsoluteUrl(result))
|
|
+ ((HttpServletResponse) pageContext.getResponse());
|
|
+ if (!UrlUtil.isAbsoluteUrl(result)) {
|
|
result = response.encodeRedirectURL(result);
|
|
+ }
|
|
|
|
- // redirect!
|
|
- try {
|
|
- response.sendRedirect(result);
|
|
- } catch (java.io.IOException ex) {
|
|
- throw new JspTagException(ex.toString(), ex);
|
|
- }
|
|
+ // redirect!
|
|
+ try {
|
|
+ response.sendRedirect(result);
|
|
+ } catch (java.io.IOException ex) {
|
|
+ throw new JspTagException(ex.toString(), ex);
|
|
+ }
|
|
|
|
- return SKIP_PAGE;
|
|
+ return SKIP_PAGE;
|
|
}
|
|
|
|
// Releases any resources we may have (or inherit)
|
|
Index: jakarta-taglibs-standard-1.1.2-src/standard/src/org/apache/taglibs/standard/tag/common/core/UrlSupport.java
|
|
===================================================================
|
|
--- jakarta-taglibs-standard-1.1.2-src.orig/standard/src/org/apache/taglibs/standard/tag/common/core/UrlSupport.java
|
|
+++ jakarta-taglibs-standard-1.1.2-src/standard/src/org/apache/taglibs/standard/tag/common/core/UrlSupport.java
|
|
@@ -24,6 +24,7 @@ import javax.servlet.jsp.PageContext;
|
|
import javax.servlet.jsp.tagext.BodyTagSupport;
|
|
|
|
import org.apache.taglibs.standard.resources.Resources;
|
|
+import org.apache.taglibs.standard.util.UrlUtil;
|
|
|
|
/**
|
|
* <p>Support for tag handlers for <url>, the URL creation
|
|
@@ -104,7 +105,7 @@ public abstract class UrlSupport extends
|
|
result = params.aggregateParams(baseUrl);
|
|
|
|
// if the URL is relative, rewrite it
|
|
- if (!ImportSupport.isAbsoluteUrl(result)) {
|
|
+ if (!UrlUtil.isAbsoluteUrl(result)) {
|
|
HttpServletResponse response =
|
|
((HttpServletResponse) pageContext.getResponse());
|
|
result = response.encodeURL(result);
|
|
@@ -134,29 +135,32 @@ public abstract class UrlSupport extends
|
|
|
|
public static String resolveUrl(
|
|
String url, String context, PageContext pageContext)
|
|
- throws JspException {
|
|
- // don't touch absolute URLs
|
|
- if (ImportSupport.isAbsoluteUrl(url))
|
|
- return url;
|
|
-
|
|
- // normalize relative URLs against a context root
|
|
- HttpServletRequest request =
|
|
- (HttpServletRequest) pageContext.getRequest();
|
|
- if (context == null) {
|
|
- if (url.startsWith("/"))
|
|
- return (request.getContextPath() + url);
|
|
- else
|
|
- return url;
|
|
- } else {
|
|
+ throws JspException {
|
|
+ // don't touch absolute URLs
|
|
+ if (UrlUtil.isAbsoluteUrl(url)) {
|
|
+ return url;
|
|
+ }
|
|
+
|
|
+ // normalize relative URLs against a context root
|
|
+ HttpServletRequest request =
|
|
+ (HttpServletRequest) pageContext.getRequest();
|
|
+ if (context == null) {
|
|
+ if (url.startsWith("/")) {
|
|
+ return (request.getContextPath() + url);
|
|
+ } else {
|
|
+ return url;
|
|
+ }
|
|
+ } else {
|
|
if (!context.startsWith("/") || !url.startsWith("/")) {
|
|
throw new JspTagException(
|
|
- Resources.getMessage("IMPORT_BAD_RELATIVE"));
|
|
+ Resources.getMessage("IMPORT_BAD_RELATIVE"));
|
|
}
|
|
- if (context.equals("/")) {
|
|
+ if (context.endsWith("/") && url.startsWith("/")) {
|
|
// Don't produce string starting with '//', many
|
|
// browsers interpret this as host name, not as
|
|
- // path on same host.
|
|
- return url;
|
|
+ // path on same host. Bug 22860
|
|
+ // Also avoid // inside the url. Bug 34109
|
|
+ return (context.substring(0, context.length() - 1) + url);
|
|
} else {
|
|
return (context + url);
|
|
}
|
|
Index: jakarta-taglibs-standard-1.1.2-src/standard/src/org/apache/taglibs/standard/tag/common/xml/JSTLVariableStack.java
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ jakarta-taglibs-standard-1.1.2-src/standard/src/org/apache/taglibs/standard/tag/common/xml/JSTLVariableStack.java
|
|
@@ -0,0 +1,132 @@
|
|
+/*
|
|
+ * Licensed to the Apache Software Foundation (ASF) under one or more
|
|
+ * contributor license agreements. See the NOTICE file distributed with
|
|
+ * this work for additional information regarding copyright ownership.
|
|
+ * The ASF licenses this file to You under the Apache License, Version 2.0
|
|
+ * (the "License"); you may not use this file except in compliance with
|
|
+ * the License. You may obtain a copy of the License at
|
|
+ *
|
|
+ * http://www.apache.org/licenses/LICENSE-2.0
|
|
+ *
|
|
+ * Unless required by applicable law or agreed to in writing, software
|
|
+ * distributed under the License is distributed on an "AS IS" BASIS,
|
|
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
+ * See the License for the specific language governing permissions and
|
|
+ * limitations under the License.
|
|
+ */
|
|
+package org.apache.taglibs.standard.tag.common.xml;
|
|
+
|
|
+import java.util.HashMap;
|
|
+import java.util.Map;
|
|
+
|
|
+import javax.servlet.http.Cookie;
|
|
+import javax.servlet.http.HttpServletRequest;
|
|
+import javax.servlet.jsp.PageContext;
|
|
+import javax.xml.transform.TransformerException;
|
|
+
|
|
+import org.apache.taglibs.standard.resources.Resources;
|
|
+import org.apache.xml.utils.QName;
|
|
+import org.apache.xpath.VariableStack;
|
|
+import org.apache.xpath.XPathContext;
|
|
+import org.apache.xpath.objects.XObject;
|
|
+import org.apache.xpath.objects.XObjectFactory;
|
|
+
|
|
+/**
|
|
+ */
|
|
+public class JSTLVariableStack extends VariableStack {
|
|
+
|
|
+ private static enum Scope {
|
|
+ PARAM,
|
|
+ HEADER,
|
|
+ COOKIE,
|
|
+ INITPARAM,
|
|
+ PAGE,
|
|
+ REQUEST,
|
|
+ SESSION,
|
|
+ APPLICATION
|
|
+ }
|
|
+
|
|
+ // Prefixes for JSTL implicit variables
|
|
+ private static final String PARAM_PREFIX = "param";
|
|
+ private static final String HEADER_PREFIX = "header";
|
|
+ private static final String COOKIE_PREFIX = "cookie";
|
|
+ private static final String INITPARAM_PREFIX = "initParam";
|
|
+ private static final String PAGE_PREFIX = "pageScope";
|
|
+ private static final String REQUEST_PREFIX = "requestScope";
|
|
+ private static final String SESSION_PREFIX = "sessionScope";
|
|
+ private static final String APP_PREFIX = "applicationScope";
|
|
+
|
|
+ // map prefixes to scopes
|
|
+ private static final Map<String, Scope> SCOPES;
|
|
+ static {
|
|
+ SCOPES = new HashMap<String, Scope>(8);
|
|
+ SCOPES.put(PARAM_PREFIX, Scope.PARAM);
|
|
+ SCOPES.put(HEADER_PREFIX, Scope.HEADER);
|
|
+ SCOPES.put(COOKIE_PREFIX, Scope.COOKIE);
|
|
+ SCOPES.put(INITPARAM_PREFIX, Scope.INITPARAM);
|
|
+ SCOPES.put(PAGE_PREFIX, Scope.PAGE);
|
|
+ SCOPES.put(REQUEST_PREFIX, Scope.REQUEST);
|
|
+ SCOPES.put(SESSION_PREFIX, Scope.SESSION);
|
|
+ SCOPES.put(APP_PREFIX, Scope.APPLICATION);
|
|
+ }
|
|
+
|
|
+ private final PageContext pageContext;
|
|
+
|
|
+ public JSTLVariableStack(PageContext pageContext) {
|
|
+ super(2);
|
|
+ this.pageContext = pageContext;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public XObject getVariableOrParam(XPathContext xctxt, QName qname) throws TransformerException {
|
|
+ String prefix = qname.getNamespaceURI();
|
|
+ String name = qname.getLocalPart();
|
|
+ Object value = getValue(prefix, name);
|
|
+ if (value == null) {
|
|
+ StringBuilder var = new StringBuilder();
|
|
+ var.append('$');
|
|
+ if (prefix != null) {
|
|
+ var.append(prefix);
|
|
+ var.append(':');
|
|
+ }
|
|
+ var.append(name);
|
|
+ throw new TransformerException(Resources.getMessage("XPATH_UNABLE_TO_RESOLVE_VARIABLE", var.toString()));
|
|
+ }
|
|
+ return XObjectFactory.create(value, xctxt);
|
|
+ }
|
|
+
|
|
+ private Object getValue(String prefix, String name) {
|
|
+ if (prefix == null) {
|
|
+ return pageContext.findAttribute(name);
|
|
+ }
|
|
+ Scope scope = SCOPES.get(prefix);
|
|
+ switch (scope) {
|
|
+ case PARAM:
|
|
+ return pageContext.getRequest().getParameter(name);
|
|
+ case HEADER:
|
|
+ return ((HttpServletRequest) pageContext.getRequest()).getHeader(name);
|
|
+ case COOKIE:
|
|
+ Cookie[] cookies = ((HttpServletRequest) pageContext.getRequest()).getCookies();
|
|
+ if (cookies != null) {
|
|
+ for (Cookie cookie : cookies) {
|
|
+ if (cookie.getName().equals(name)) {
|
|
+ return cookie.getValue();
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return null;
|
|
+ case INITPARAM:
|
|
+ return pageContext.getServletContext().getInitParameter(name);
|
|
+ case PAGE:
|
|
+ return pageContext.getAttribute(name, PageContext.PAGE_SCOPE);
|
|
+ case REQUEST:
|
|
+ return pageContext.getAttribute(name, PageContext.REQUEST_SCOPE);
|
|
+ case SESSION:
|
|
+ return pageContext.getAttribute(name, PageContext.SESSION_SCOPE);
|
|
+ case APPLICATION:
|
|
+ return pageContext.getAttribute(name, PageContext.APPLICATION_SCOPE);
|
|
+ default:
|
|
+ throw new AssertionError();
|
|
+ }
|
|
+ }
|
|
+}
|
|
Index: jakarta-taglibs-standard-1.1.2-src/standard/src/org/apache/taglibs/standard/tag/common/xml/ParseSupport.java
|
|
===================================================================
|
|
--- jakarta-taglibs-standard-1.1.2-src.orig/standard/src/org/apache/taglibs/standard/tag/common/xml/ParseSupport.java
|
|
+++ jakarta-taglibs-standard-1.1.2-src/standard/src/org/apache/taglibs/standard/tag/common/xml/ParseSupport.java
|
|
@@ -16,36 +16,26 @@
|
|
|
|
package org.apache.taglibs.standard.tag.common.xml;
|
|
|
|
-import java.io.FileNotFoundException;
|
|
import java.io.IOException;
|
|
-import java.io.InputStream;
|
|
import java.io.Reader;
|
|
import java.io.StringReader;
|
|
|
|
-import javax.servlet.http.HttpServletRequest;
|
|
import javax.servlet.jsp.JspException;
|
|
import javax.servlet.jsp.JspTagException;
|
|
import javax.servlet.jsp.PageContext;
|
|
import javax.servlet.jsp.tagext.BodyTagSupport;
|
|
import javax.xml.parsers.DocumentBuilder;
|
|
-import javax.xml.parsers.DocumentBuilderFactory;
|
|
-import javax.xml.parsers.ParserConfigurationException;
|
|
import javax.xml.transform.TransformerConfigurationException;
|
|
-import javax.xml.transform.TransformerFactory;
|
|
import javax.xml.transform.dom.DOMResult;
|
|
-import javax.xml.transform.sax.SAXTransformerFactory;
|
|
import javax.xml.transform.sax.TransformerHandler;
|
|
|
|
import org.apache.taglibs.standard.resources.Resources;
|
|
-import org.apache.taglibs.standard.tag.common.core.ImportSupport;
|
|
import org.apache.taglibs.standard.tag.common.core.Util;
|
|
import org.w3c.dom.Document;
|
|
-import org.xml.sax.EntityResolver;
|
|
import org.xml.sax.InputSource;
|
|
import org.xml.sax.SAXException;
|
|
import org.xml.sax.XMLFilter;
|
|
import org.xml.sax.XMLReader;
|
|
-import org.xml.sax.helpers.XMLReaderFactory;
|
|
|
|
/**
|
|
* <p>Support for tag handlers for <parse>, the XML parsing tag.</p>
|
|
@@ -68,12 +58,7 @@ public abstract class ParseSupport exten
|
|
private String varDom; // 'varDom' attribute
|
|
private int scope; // processed 'scope' attr
|
|
private int scopeDom; // processed 'scopeDom' attr
|
|
-
|
|
- // state in support of XML parsing...
|
|
- private DocumentBuilderFactory dbf;
|
|
- private DocumentBuilder db;
|
|
- private TransformerFactory tf;
|
|
- private TransformerHandler th;
|
|
+ private XmlUtil.JstlEntityResolver entityResolver;
|
|
|
|
|
|
//*********************************************************************
|
|
@@ -89,76 +74,50 @@ public abstract class ParseSupport exten
|
|
xml = null;
|
|
systemId = null;
|
|
filter = null;
|
|
- dbf = null;
|
|
- db = null;
|
|
- tf = null;
|
|
- th = null;
|
|
scope = PageContext.PAGE_SCOPE;
|
|
scopeDom = PageContext.PAGE_SCOPE;
|
|
}
|
|
|
|
-
|
|
//*********************************************************************
|
|
// Tag logic
|
|
|
|
// parse 'source' or body, storing result in 'var'
|
|
public int doEndTag() throws JspException {
|
|
- try {
|
|
-
|
|
- // set up our DocumentBuilder
|
|
- if (dbf == null) {
|
|
- dbf = DocumentBuilderFactory.newInstance();
|
|
- dbf.setNamespaceAware(true);
|
|
- dbf.setValidating(false);
|
|
+ // produce a Document by parsing whatever the attributes tell us to use
|
|
+ Object xmlText = this.xml;
|
|
+ if (xmlText == null) {
|
|
+ // if the attribute was specified, use the body as 'xml'
|
|
+ if (bodyContent != null && bodyContent.getString() != null) {
|
|
+ xmlText = bodyContent.getString().trim();
|
|
+ } else {
|
|
+ xmlText = "";
|
|
+ }
|
|
+ }
|
|
+ if (xmlText instanceof String) {
|
|
+ xmlText = new StringReader((String) xmlText);
|
|
+ }
|
|
+ if (!(xmlText instanceof Reader)) {
|
|
+ throw new JspTagException(Resources.getMessage("PARSE_INVALID_SOURCE"));
|
|
+ }
|
|
+ InputSource source = XmlUtil.newInputSource(((Reader) xmlText), systemId);
|
|
+
|
|
+ Document d;
|
|
+ if (filter != null) {
|
|
+ d = parseInputSourceWithFilter(source, filter);
|
|
+ } else {
|
|
+ d = parseInputSource(source);
|
|
+ }
|
|
+
|
|
+ // we've got a Document object; store it out as appropriate
|
|
+ // (let any exclusivity or other constraints be enforced by TEI/TLV)
|
|
+ if (var != null) {
|
|
+ pageContext.setAttribute(var, d, scope);
|
|
+ }
|
|
+ if (varDom != null) {
|
|
+ pageContext.setAttribute(varDom, d, scopeDom);
|
|
}
|
|
- db = dbf.newDocumentBuilder();
|
|
|
|
- // if we've gotten a filter, set up a transformer to support it
|
|
- if (filter != null) {
|
|
- if (tf == null)
|
|
- tf = TransformerFactory.newInstance();
|
|
- if (!tf.getFeature(SAXTransformerFactory.FEATURE))
|
|
- throw new JspTagException(
|
|
- Resources.getMessage("PARSE_NO_SAXTRANSFORMER"));
|
|
- SAXTransformerFactory stf = (SAXTransformerFactory) tf;
|
|
- th = stf.newTransformerHandler();
|
|
- }
|
|
-
|
|
- // produce a Document by parsing whatever the attributes tell us to use
|
|
- Document d;
|
|
- Object xmlText = this.xml;
|
|
- if (xmlText == null) {
|
|
- // if the attribute was specified, use the body as 'xml'
|
|
- if (bodyContent != null && bodyContent.getString() != null)
|
|
- xmlText = bodyContent.getString().trim();
|
|
- else
|
|
- xmlText = "";
|
|
- }
|
|
- if (xmlText instanceof String)
|
|
- d = parseStringWithFilter((String) xmlText, filter);
|
|
- else if (xmlText instanceof Reader)
|
|
- d = parseReaderWithFilter((Reader) xmlText, filter);
|
|
- else
|
|
- throw new JspTagException(
|
|
- Resources.getMessage("PARSE_INVALID_SOURCE"));
|
|
-
|
|
- // we've got a Document object; store it out as appropriate
|
|
- // (let any exclusivity or other constraints be enforced by TEI/TLV)
|
|
- if (var != null)
|
|
- pageContext.setAttribute(var, d, scope);
|
|
- if (varDom != null)
|
|
- pageContext.setAttribute(varDom, d, scopeDom);
|
|
-
|
|
- return EVAL_PAGE;
|
|
- } catch (SAXException ex) {
|
|
- throw new JspException(ex);
|
|
- } catch (IOException ex) {
|
|
- throw new JspException(ex);
|
|
- } catch (ParserConfigurationException ex) {
|
|
- throw new JspException(ex);
|
|
- } catch (TransformerConfigurationException ex) {
|
|
- throw new JspException(ex);
|
|
- }
|
|
+ return EVAL_PAGE;
|
|
}
|
|
|
|
// Releases any resources we may have (or inherit)
|
|
@@ -171,126 +130,48 @@ public abstract class ParseSupport exten
|
|
// Private utility methods
|
|
|
|
/** Parses the given InputSource after, applying the given XMLFilter. */
|
|
- private Document parseInputSourceWithFilter(InputSource s, XMLFilter f)
|
|
- throws SAXException, IOException {
|
|
- if (f != null) {
|
|
- // prepare an output Document
|
|
- Document o = db.newDocument();
|
|
-
|
|
- // use TrAX to adapt SAX events to a Document object
|
|
- th.setResult(new DOMResult(o));
|
|
- XMLReader xr = XMLReaderFactory.createXMLReader();
|
|
- xr.setEntityResolver(new JstlEntityResolver(pageContext));
|
|
+ private Document parseInputSourceWithFilter(InputSource s, XMLFilter f) throws JspException {
|
|
+ try {
|
|
+ XMLReader xr = XmlUtil.newXMLReader(entityResolver);
|
|
// (note that we overwrite the filter's parent. this seems
|
|
// to be expected usage. we could cache and reset the old
|
|
// parent, but you can't setParent(null), so this wouldn't
|
|
// be perfect.)
|
|
f.setParent(xr);
|
|
- f.setContentHandler(th);
|
|
- f.parse(s);
|
|
- return o;
|
|
- } else
|
|
- return parseInputSource(s);
|
|
- }
|
|
|
|
- /** Parses the given Reader after applying the given XMLFilter. */
|
|
- private Document parseReaderWithFilter(Reader r, XMLFilter f)
|
|
- throws SAXException, IOException {
|
|
- return parseInputSourceWithFilter(new InputSource(r), f);
|
|
- }
|
|
+ TransformerHandler th = XmlUtil.newTransformerHandler();
|
|
+ Document o = XmlUtil.newEmptyDocument();
|
|
+ th.setResult(new DOMResult(o));
|
|
|
|
- /** Parses the given String after applying the given XMLFilter. */
|
|
- private Document parseStringWithFilter(String s, XMLFilter f)
|
|
- throws SAXException, IOException {
|
|
- StringReader r = new StringReader(s);
|
|
- return parseReaderWithFilter(r, f);
|
|
- }
|
|
+ f.setContentHandler(th);
|
|
|
|
- /** Parses the given Reader after applying the given XMLFilter. */
|
|
- private Document parseURLWithFilter(String url, XMLFilter f)
|
|
- throws SAXException, IOException {
|
|
- return parseInputSourceWithFilter(new InputSource(url), f);
|
|
+ f.parse(s);
|
|
+ return o;
|
|
+ } catch (IOException e) {
|
|
+ throw new JspException(e);
|
|
+ } catch (SAXException e) {
|
|
+ throw new JspException(e);
|
|
+ } catch (TransformerConfigurationException e) {
|
|
+ throw new JspException(e);
|
|
+ }
|
|
}
|
|
|
|
/** Parses the given InputSource into a Document. */
|
|
- private Document parseInputSource(InputSource s)
|
|
- throws SAXException, IOException {
|
|
- db.setEntityResolver(new JstlEntityResolver(pageContext));
|
|
-
|
|
- // normalize URIs so they can be processed consistently by resolver
|
|
- if (systemId == null)
|
|
- s.setSystemId("jstl:");
|
|
- else if (ImportSupport.isAbsoluteUrl(systemId))
|
|
- s.setSystemId(systemId);
|
|
- else
|
|
- s.setSystemId("jstl:" + systemId);
|
|
- return db.parse(s);
|
|
- }
|
|
-
|
|
- /** Parses the given Reader into a Document. */
|
|
- private Document parseReader(Reader r) throws SAXException, IOException {
|
|
- return parseInputSource(new InputSource(r));
|
|
- }
|
|
-
|
|
- /** Parses the given String into a Document. */
|
|
- private Document parseString(String s) throws SAXException, IOException {
|
|
- StringReader r = new StringReader(s);
|
|
- return parseReader(r);
|
|
- }
|
|
-
|
|
- /** Parses the URL (passed as a String) into a Document. */
|
|
- private Document parseURL(String url) throws SAXException, IOException {
|
|
- return parseInputSource(new InputSource(url));
|
|
- }
|
|
-
|
|
- //*********************************************************************
|
|
- // JSTL-specific EntityResolver class
|
|
-
|
|
- /** Lets us resolve relative external entities. */
|
|
- public static class JstlEntityResolver implements EntityResolver {
|
|
- private final PageContext ctx;
|
|
- public JstlEntityResolver(PageContext ctx) {
|
|
- this.ctx = ctx;
|
|
+ private Document parseInputSource(InputSource s) throws JspException {
|
|
+ try {
|
|
+ DocumentBuilder db = XmlUtil.newDocumentBuilder();
|
|
+ db.setEntityResolver(entityResolver);
|
|
+ return db.parse(s);
|
|
+ } catch (SAXException e) {
|
|
+ throw new JspException(e);
|
|
+ } catch (IOException e) {
|
|
+ throw new JspException(e);
|
|
}
|
|
- public InputSource resolveEntity(String publicId, String systemId)
|
|
- throws FileNotFoundException {
|
|
+ }
|
|
|
|
- // pass if we don't have a systemId
|
|
- if (systemId == null)
|
|
- return null;
|
|
-
|
|
- // strip leading "jstl:" off URL if applicable
|
|
- if (systemId.startsWith("jstl:"))
|
|
- systemId = systemId.substring(5);
|
|
-
|
|
- // we're only concerned with relative URLs
|
|
- if (ImportSupport.isAbsoluteUrl(systemId))
|
|
- return null;
|
|
-
|
|
- // for relative URLs, load and wrap the resource.
|
|
- // don't bother checking for 'null' since we specifically want
|
|
- // the parser to fail if the resource doesn't exist
|
|
- InputStream s;
|
|
- if (systemId.startsWith("/")) {
|
|
- s = ctx.getServletContext().getResourceAsStream(systemId);
|
|
- if (s == null)
|
|
- throw new FileNotFoundException(
|
|
- Resources.getMessage("UNABLE_TO_RESOLVE_ENTITY",
|
|
- systemId));
|
|
- } else {
|
|
- String pagePath =
|
|
- ((HttpServletRequest) ctx.getRequest()).getServletPath();
|
|
- String basePath =
|
|
- pagePath.substring(0, pagePath.lastIndexOf("/"));
|
|
- s = ctx.getServletContext().getResourceAsStream(
|
|
- basePath + "/" + systemId);
|
|
- if (s == null)
|
|
- throw new FileNotFoundException(
|
|
- Resources.getMessage("UNABLE_TO_RESOLVE_ENTITY",
|
|
- systemId));
|
|
- }
|
|
- return new InputSource(s);
|
|
- }
|
|
+ public void setPageContext(PageContext pageContext) {
|
|
+ super.setPageContext(pageContext);
|
|
+ entityResolver = pageContext == null ? null: new XmlUtil.JstlEntityResolver(pageContext);
|
|
}
|
|
|
|
//*********************************************************************
|
|
Index: jakarta-taglibs-standard-1.1.2-src/standard/src/org/apache/taglibs/standard/tag/common/xml/TransformSupport.java
|
|
===================================================================
|
|
--- jakarta-taglibs-standard-1.1.2-src.orig/standard/src/org/apache/taglibs/standard/tag/common/xml/TransformSupport.java
|
|
+++ jakarta-taglibs-standard-1.1.2-src/standard/src/org/apache/taglibs/standard/tag/common/xml/TransformSupport.java
|
|
@@ -16,43 +16,29 @@
|
|
|
|
package org.apache.taglibs.standard.tag.common.xml;
|
|
|
|
-import java.io.IOException;
|
|
-import java.io.InputStream;
|
|
import java.io.Reader;
|
|
import java.io.StringReader;
|
|
-import java.io.Writer;
|
|
import java.util.List;
|
|
|
|
-import javax.servlet.http.HttpServletRequest;
|
|
import javax.servlet.jsp.JspException;
|
|
import javax.servlet.jsp.JspTagException;
|
|
import javax.servlet.jsp.PageContext;
|
|
import javax.servlet.jsp.tagext.BodyTagSupport;
|
|
-import javax.xml.parsers.DocumentBuilder;
|
|
-import javax.xml.parsers.DocumentBuilderFactory;
|
|
-import javax.xml.parsers.ParserConfigurationException;
|
|
import javax.xml.transform.Result;
|
|
import javax.xml.transform.Source;
|
|
import javax.xml.transform.Transformer;
|
|
import javax.xml.transform.TransformerConfigurationException;
|
|
import javax.xml.transform.TransformerException;
|
|
-import javax.xml.transform.TransformerFactory;
|
|
-import javax.xml.transform.URIResolver;
|
|
import javax.xml.transform.dom.DOMResult;
|
|
import javax.xml.transform.dom.DOMSource;
|
|
-import javax.xml.transform.sax.SAXSource;
|
|
import javax.xml.transform.stream.StreamResult;
|
|
-import javax.xml.transform.stream.StreamSource;
|
|
|
|
import org.apache.taglibs.standard.resources.Resources;
|
|
-import org.apache.taglibs.standard.tag.common.core.ImportSupport;
|
|
import org.apache.taglibs.standard.tag.common.core.Util;
|
|
+import org.apache.taglibs.standard.util.UnclosableWriter;
|
|
import org.w3c.dom.Document;
|
|
import org.w3c.dom.Node;
|
|
-import org.xml.sax.InputSource;
|
|
import org.xml.sax.SAXException;
|
|
-import org.xml.sax.XMLReader;
|
|
-import org.xml.sax.helpers.XMLReaderFactory;
|
|
|
|
/**
|
|
* <p>Support for tag handlers for <transform>, the XML transformation
|
|
@@ -66,6 +52,7 @@ public abstract class TransformSupport e
|
|
// Protected state
|
|
|
|
protected Object xml; // attribute
|
|
+ protected boolean xmlSpecified; // true if xml attribute was specified
|
|
protected String xmlSystemId; // attribute
|
|
protected Object xslt; // attribute
|
|
protected String xsltSystemId; // attribute
|
|
@@ -77,25 +64,22 @@ public abstract class TransformSupport e
|
|
private String var; // 'var' attribute
|
|
private int scope; // processed 'scope' attr
|
|
private Transformer t; // actual Transformer
|
|
- private TransformerFactory tf; // reusable factory
|
|
- private DocumentBuilder db; // reusable factory
|
|
- private DocumentBuilderFactory dbf; // reusable factory
|
|
-
|
|
+ private XmlUtil.JstlEntityResolver entityResolver;
|
|
+ private XmlUtil.JstlUriResolver uriResolver;
|
|
|
|
//*********************************************************************
|
|
// Constructor and initialization
|
|
|
|
public TransformSupport() {
|
|
- super();
|
|
init();
|
|
}
|
|
|
|
private void init() {
|
|
xml = xslt = null;
|
|
+ xmlSpecified = false;
|
|
xmlSystemId = xsltSystemId = null;
|
|
var = null;
|
|
result = null;
|
|
- tf = null;
|
|
scope = PageContext.PAGE_SCOPE;
|
|
}
|
|
|
|
@@ -104,107 +88,70 @@ public abstract class TransformSupport e
|
|
// Tag logic
|
|
|
|
public int doStartTag() throws JspException {
|
|
- /*
|
|
- * We can set up our Transformer here, so we do so, and we let
|
|
- * it receive parameters directly from subtags (instead of
|
|
- * caching them.
|
|
- */
|
|
- try {
|
|
-
|
|
- //************************************
|
|
- // Initialize
|
|
-
|
|
- // set up our DocumentBuilderFactory if necessary
|
|
- if (dbf == null) {
|
|
- dbf = DocumentBuilderFactory.newInstance();
|
|
- dbf.setNamespaceAware(true);
|
|
- dbf.setValidating(false);
|
|
- }
|
|
- if (db == null)
|
|
- db = dbf.newDocumentBuilder();
|
|
-
|
|
- // set up the TransformerFactory if necessary
|
|
- if (tf == null)
|
|
- tf = TransformerFactory.newInstance();
|
|
-
|
|
- //************************************
|
|
- // Produce transformer
|
|
-
|
|
- Source s;
|
|
- if (xslt != null) {
|
|
- if (!(xslt instanceof String) && !(xslt instanceof Reader)
|
|
- && !(xslt instanceof javax.xml.transform.Source))
|
|
- throw new JspTagException(
|
|
- Resources.getMessage("TRANSFORM_XSLT_UNRECOGNIZED"));
|
|
- s = getSource(xslt, xsltSystemId);
|
|
- } else {
|
|
- throw new JspTagException(
|
|
- Resources.getMessage("TRANSFORM_NO_TRANSFORMER"));
|
|
- }
|
|
- tf.setURIResolver(new JstlUriResolver(pageContext));
|
|
- t = tf.newTransformer(s);
|
|
-
|
|
- return EVAL_BODY_BUFFERED;
|
|
-
|
|
- } catch (SAXException ex) {
|
|
- throw new JspException(ex);
|
|
- } catch (ParserConfigurationException ex) {
|
|
- throw new JspException(ex);
|
|
- } catch (IOException ex) {
|
|
- throw new JspException(ex);
|
|
- } catch (TransformerConfigurationException ex) {
|
|
- throw new JspException(ex);
|
|
- }
|
|
+ // set up transformer in the start tag so that nested <param> tags can set parameters directly
|
|
+ if (xslt == null) {
|
|
+ throw new JspTagException(Resources.getMessage("TRANSFORM_XSLT_IS_NULL"));
|
|
+ }
|
|
+
|
|
+ Source source;
|
|
+ try {
|
|
+ if (xslt instanceof Source) {
|
|
+ source = (Source) xslt;
|
|
+ } else if (xslt instanceof String) {
|
|
+ String s = (String) xslt;
|
|
+ s = s.trim();
|
|
+ if (s.length() == 0) {
|
|
+ throw new JspTagException(Resources.getMessage("TRANSFORM_XSLT_IS_EMPTY"));
|
|
+ }
|
|
+ source = XmlUtil.newSAXSource(new StringReader(s), xsltSystemId, entityResolver);
|
|
+ } else if (xslt instanceof Reader) {
|
|
+ source = XmlUtil.newSAXSource((Reader) xslt, xsltSystemId, entityResolver);
|
|
+ } else {
|
|
+ throw new JspTagException(Resources.getMessage("TRANSFORM_XSLT_UNSUPPORTED_TYPE", xslt.getClass()));
|
|
+ }
|
|
+ } catch (SAXException e) {
|
|
+ throw new JspException(e);
|
|
+ }
|
|
+
|
|
+ try {
|
|
+ t = XmlUtil.newTransformer(source);
|
|
+ t.setURIResolver(uriResolver);
|
|
+ } catch (TransformerConfigurationException e) {
|
|
+ throw new JspTagException(e);
|
|
+ } catch (RuntimeException e) {
|
|
+ throw e;
|
|
+ }
|
|
+ return EVAL_BODY_BUFFERED;
|
|
}
|
|
|
|
- // parse 'xml' or body, transform via our Transformer,
|
|
- // and store as 'var' or through 'result'
|
|
public int doEndTag() throws JspException {
|
|
- try {
|
|
|
|
- //************************************
|
|
- // Determine source XML
|
|
+ try {
|
|
+ Source source = xmlSpecified ? getSourceFromXmlAttribute() : getSourceFromBodyContent();
|
|
|
|
- // if we haven't gotten a source, use the body (which may be empty)
|
|
- Object xml = this.xml;
|
|
- if (xml == null) // still equal
|
|
- if (bodyContent != null && bodyContent.getString() != null)
|
|
- xml = bodyContent.getString().trim();
|
|
- else
|
|
- xml = "";
|
|
-
|
|
- // let the Source be with you
|
|
- Source source = getSource(xml, xmlSystemId);
|
|
-
|
|
- //************************************
|
|
- // Conduct the transformation
|
|
-
|
|
- // we can assume at most one of 'var' or 'result' is specified
|
|
- if (result != null)
|
|
- // we can write directly to the Result
|
|
- t.transform(source, result);
|
|
- else if (var != null) {
|
|
- // we need a Document
|
|
- Document d = db.newDocument();
|
|
- Result doc = new DOMResult(d);
|
|
- t.transform(source, doc);
|
|
- pageContext.setAttribute(var, d, scope);
|
|
- } else {
|
|
- Result page =
|
|
- new StreamResult(new SafeWriter(pageContext.getOut()));
|
|
- t.transform(source, page);
|
|
- }
|
|
-
|
|
- return EVAL_PAGE;
|
|
- } catch (SAXException ex) {
|
|
- throw new JspException(ex);
|
|
- } catch (ParserConfigurationException ex) {
|
|
- throw new JspException(ex);
|
|
- } catch (IOException ex) {
|
|
- throw new JspException(ex);
|
|
- } catch (TransformerException ex) {
|
|
- throw new JspException(ex);
|
|
- }
|
|
+ // Conduct the transformation
|
|
+ if (var != null) {
|
|
+ // Save the result to var.
|
|
+ Document d = XmlUtil.newEmptyDocument();
|
|
+ Result doc = new DOMResult(d);
|
|
+ t.transform(source, doc);
|
|
+ pageContext.setAttribute(var, d, scope);
|
|
+ } else {
|
|
+ // Write to out if result is not specified.
|
|
+ Result out = result;
|
|
+ if (out == null) {
|
|
+ out = new StreamResult(new UnclosableWriter(pageContext.getOut()));
|
|
+ }
|
|
+ t.transform(source, out);
|
|
+ }
|
|
+ return EVAL_PAGE;
|
|
+ } catch (TransformerException ex) {
|
|
+ throw new JspException(ex);
|
|
+ } catch (SAXException e) {
|
|
+ throw new JspException(e);
|
|
+ } finally {
|
|
+ t = null;
|
|
+ }
|
|
}
|
|
|
|
// Releases any resources we may have (or inherit)
|
|
@@ -212,6 +159,11 @@ public abstract class TransformSupport e
|
|
init();
|
|
}
|
|
|
|
+ public void setPageContext(PageContext pageContext) {
|
|
+ super.setPageContext(pageContext);
|
|
+ uriResolver = pageContext == null ? null : new XmlUtil.JstlUriResolver(pageContext);
|
|
+ entityResolver = pageContext == null ? null : new XmlUtil.JstlEntityResolver(pageContext);
|
|
+ }
|
|
|
|
//*********************************************************************
|
|
// Public methods for subtags
|
|
@@ -226,64 +178,67 @@ public abstract class TransformSupport e
|
|
// Utility methods
|
|
|
|
/**
|
|
- * Wraps systemId with a "jstl:" prefix to prevent the parser from
|
|
- * thinking that the URI is truly relative and resolving it against
|
|
- * the current directory in the filesystem.
|
|
+ * Return the Source for a document specified in the "doc" or "xml" attribute.
|
|
+ *
|
|
+ * @return the document Source
|
|
+ * @throws JspTagException if there is a problem with the attribute
|
|
*/
|
|
- private static String wrapSystemId(String systemId) {
|
|
- if (systemId == null)
|
|
- return "jstl:";
|
|
- else if (ImportSupport.isAbsoluteUrl(systemId))
|
|
- return systemId;
|
|
- else
|
|
- return ("jstl:" + systemId);
|
|
+ Source getSourceFromXmlAttribute() throws JspTagException, SAXException {
|
|
+ Object xml = this.xml;
|
|
+ if (xml == null) {
|
|
+ throw new JspTagException(Resources.getMessage("TRANSFORM_XML_IS_NULL"));
|
|
+ }
|
|
+
|
|
+ // other JSTL XML tags may produce a list
|
|
+ if (xml instanceof List) {
|
|
+ List<?> list = (List<?>) xml;
|
|
+ if (list.size() != 1) {
|
|
+ throw new JspTagException(Resources.getMessage("TRANSFORM_XML_LIST_SIZE"));
|
|
+ }
|
|
+ xml = list.get(0);
|
|
+ }
|
|
+
|
|
+ if (xml instanceof Source) {
|
|
+ return (Source) xml;
|
|
+ }
|
|
+ if (xml instanceof String) {
|
|
+ String s = (String) xml;
|
|
+ s = s.trim();
|
|
+ if (s.length() == 0) {
|
|
+ throw new JspTagException(Resources.getMessage("TRANSFORM_XML_IS_EMPTY"));
|
|
+ }
|
|
+ return XmlUtil.newSAXSource(new StringReader(s), xmlSystemId, entityResolver);
|
|
+ }
|
|
+ if (xml instanceof Reader) {
|
|
+ return XmlUtil.newSAXSource((Reader) xml, xmlSystemId, entityResolver);
|
|
+ }
|
|
+ if (xml instanceof Node) {
|
|
+ return new DOMSource((Node) xml, xmlSystemId);
|
|
+ }
|
|
+ throw new JspTagException(Resources.getMessage("TRANSFORM_XML_UNSUPPORTED_TYPE", xml.getClass()));
|
|
}
|
|
|
|
/**
|
|
- * Retrieves a Source from the given Object, whether it be a String,
|
|
- * Reader, Node, or other supported types (even a Source already).
|
|
- * If 'url' is true, then we must be passed a String and will interpret
|
|
- * it as a URL. A null input always results in a null output.
|
|
+ * Return the Source for a document specified as body content.
|
|
+ *
|
|
+ * @return the document Source
|
|
+ * @throws JspTagException if there is a problem with the body content
|
|
*/
|
|
- private Source getSource(Object o, String systemId)
|
|
- throws SAXException, ParserConfigurationException, IOException {
|
|
- if (o == null)
|
|
- return null;
|
|
- else if (o instanceof Source) {
|
|
- return (Source) o;
|
|
- } else if (o instanceof String) {
|
|
- // if we've got a string, chain to Reader below
|
|
- return getSource(new StringReader((String) o), systemId);
|
|
- } else if (o instanceof Reader) {
|
|
- // explicitly go through SAX to maintain control
|
|
- // over how relative external entities resolve
|
|
- XMLReader xr = XMLReaderFactory.createXMLReader();
|
|
- xr.setEntityResolver(
|
|
- new ParseSupport.JstlEntityResolver(pageContext));
|
|
- InputSource s = new InputSource((Reader) o);
|
|
- s.setSystemId(wrapSystemId(systemId));
|
|
- Source result = new SAXSource(xr, s);
|
|
- result.setSystemId(wrapSystemId(systemId));
|
|
- return result;
|
|
- } else if (o instanceof Node) {
|
|
- return new DOMSource((Node) o);
|
|
- } else if (o instanceof List) {
|
|
- // support 1-item List because our XPath processor outputs them
|
|
- List l = (List) o;
|
|
- if (l.size() == 1) {
|
|
- return getSource(l.get(0), systemId); // unwrap List
|
|
- } else {
|
|
- throw new IllegalArgumentException(
|
|
- Resources.getMessage("TRANSFORM_SOURCE_INVALID_LIST"));
|
|
- }
|
|
- } else {
|
|
- throw new IllegalArgumentException(
|
|
- Resources.getMessage("TRANSFORM_SOURCE_UNRECOGNIZED")
|
|
- + o.getClass());
|
|
- }
|
|
+ Source getSourceFromBodyContent() throws JspTagException, SAXException {
|
|
+ if (bodyContent == null) {
|
|
+ throw new JspTagException(Resources.getMessage("TRANSFORM_BODY_IS_NULL"));
|
|
+ }
|
|
+ String s = bodyContent.getString();
|
|
+ if (s == null) {
|
|
+ throw new JspTagException(Resources.getMessage("TRANSFORM_BODY_CONTENT_IS_NULL"));
|
|
+ }
|
|
+ s = s.trim();
|
|
+ if (s.length() == 0) {
|
|
+ throw new JspTagException(Resources.getMessage("TRANSFORM_BODY_IS_EMPTY"));
|
|
+ }
|
|
+ return XmlUtil.newSAXSource(new StringReader(s), xmlSystemId, entityResolver);
|
|
}
|
|
|
|
-
|
|
//*********************************************************************
|
|
// Tag attributes
|
|
|
|
@@ -294,88 +249,4 @@ public abstract class TransformSupport e
|
|
public void setScope(String scope) {
|
|
this.scope = Util.getScope(scope);
|
|
}
|
|
-
|
|
-
|
|
- //*********************************************************************
|
|
- // Private utility classes
|
|
-
|
|
- /**
|
|
- * A Writer based on a wrapped Writer but ignoring requests to
|
|
- * close() and flush() it. (Someone must have wrapped the
|
|
- * toilet in my office similarly...)
|
|
- */
|
|
- private static class SafeWriter extends Writer {
|
|
- private Writer w;
|
|
- public SafeWriter(Writer w) { this.w = w; }
|
|
- public void close() { }
|
|
- public void flush() { }
|
|
- public void write(char[] cbuf, int off, int len) throws IOException {
|
|
- w.write(cbuf, off, len);
|
|
- }
|
|
- }
|
|
-
|
|
- //*********************************************************************
|
|
- // JSTL-specific URIResolver class
|
|
-
|
|
- /** Lets us resolve relative external entities. */
|
|
- private static class JstlUriResolver implements URIResolver {
|
|
- private final PageContext ctx;
|
|
- public JstlUriResolver(PageContext ctx) {
|
|
- this.ctx = ctx;
|
|
- }
|
|
- public Source resolve(String href, String base)
|
|
- throws TransformerException {
|
|
-
|
|
- // pass if we don't have a systemId
|
|
- if (href == null)
|
|
- return null;
|
|
-
|
|
- // remove "jstl" marker from 'base'
|
|
- // NOTE: how 'base' is determined varies among different Xalan
|
|
- // xsltc implementations
|
|
- int index;
|
|
- if (base != null && (index = base.indexOf("jstl:")) != -1) {
|
|
- base = base.substring(index + 5);
|
|
- }
|
|
-
|
|
- // we're only concerned with relative URLs
|
|
- if (ImportSupport.isAbsoluteUrl(href)
|
|
- || (base != null && ImportSupport.isAbsoluteUrl(base)))
|
|
- return null;
|
|
-
|
|
- // base is relative; remove everything after trailing '/'
|
|
- if (base == null || base.lastIndexOf("/") == -1)
|
|
- base = "";
|
|
- else
|
|
- base = base.substring(0, base.lastIndexOf("/") + 1);
|
|
-
|
|
- // concatenate to produce the real URL we're interested in
|
|
- String target = base + href;
|
|
-
|
|
- // for relative URLs, load and wrap the resource.
|
|
- // don't bother checking for 'null' since we specifically want
|
|
- // the parser to fail if the resource doesn't exist
|
|
- InputStream s;
|
|
- if (target.startsWith("/")) {
|
|
- s = ctx.getServletContext().getResourceAsStream(target);
|
|
- if (s == null)
|
|
- throw new TransformerException(
|
|
- Resources.getMessage("UNABLE_TO_RESOLVE_ENTITY",
|
|
- href));
|
|
- } else {
|
|
- String pagePath =
|
|
- ((HttpServletRequest) ctx.getRequest()).getServletPath();
|
|
- String basePath =
|
|
- pagePath.substring(0, pagePath.lastIndexOf("/"));
|
|
- s = ctx.getServletContext().getResourceAsStream(
|
|
- basePath + "/" + target);
|
|
- if (s == null)
|
|
- throw new TransformerException(
|
|
- Resources.getMessage("UNABLE_TO_RESOLVE_ENTITY",
|
|
- href));
|
|
- }
|
|
- return new StreamSource(s);
|
|
- }
|
|
- }
|
|
-
|
|
}
|
|
Index: jakarta-taglibs-standard-1.1.2-src/standard/src/org/apache/taglibs/standard/tag/common/xml/XalanUtil.java
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ jakarta-taglibs-standard-1.1.2-src/standard/src/org/apache/taglibs/standard/tag/common/xml/XalanUtil.java
|
|
@@ -0,0 +1,90 @@
|
|
+/*
|
|
+ * Licensed to the Apache Software Foundation (ASF) under one or more
|
|
+ * contributor license agreements. See the NOTICE file distributed with
|
|
+ * this work for additional information regarding copyright ownership.
|
|
+ * The ASF licenses this file to You under the Apache License, Version 2.0
|
|
+ * (the "License"); you may not use this file except in compliance with
|
|
+ * the License. You may obtain a copy of the License at
|
|
+ *
|
|
+ * http://www.apache.org/licenses/LICENSE-2.0
|
|
+ *
|
|
+ * Unless required by applicable law or agreed to in writing, software
|
|
+ * distributed under the License is distributed on an "AS IS" BASIS,
|
|
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
+ * See the License for the specific language governing permissions and
|
|
+ * limitations under the License.
|
|
+ */
|
|
+package org.apache.taglibs.standard.tag.common.xml;
|
|
+
|
|
+import javax.servlet.jsp.PageContext;
|
|
+import javax.servlet.jsp.tagext.Tag;
|
|
+import javax.servlet.jsp.tagext.TagSupport;
|
|
+import javax.xml.transform.TransformerException;
|
|
+
|
|
+import org.apache.xpath.VariableStack;
|
|
+import org.apache.xpath.XPathContext;
|
|
+import org.apache.xpath.objects.XBoolean;
|
|
+import org.apache.xpath.objects.XNodeSet;
|
|
+import org.apache.xpath.objects.XNumber;
|
|
+import org.apache.xpath.objects.XObject;
|
|
+import org.apache.xpath.objects.XString;
|
|
+import org.w3c.dom.NodeList;
|
|
+
|
|
+/**
|
|
+ */
|
|
+public class XalanUtil {
|
|
+ /**
|
|
+ * Return the XPathContext to be used for evaluating expressions.
|
|
+ *
|
|
+ * If the child is nested withing a forEach tag its iteration context is used.
|
|
+ * Otherwise, a new context is created based on an empty Document.
|
|
+ *
|
|
+ * @param child the tag whose context should be returned
|
|
+ * @param pageContext the current page context
|
|
+ * @return the XPath evaluation context
|
|
+ */
|
|
+ public static XPathContext getContext(Tag child, PageContext pageContext) {
|
|
+ // if within a forEach tag, use its context
|
|
+ ForEachTag forEachTag = (ForEachTag) TagSupport.findAncestorWithClass(child, ForEachTag.class);
|
|
+ if (forEachTag != null) {
|
|
+ throw new UnsupportedOperationException("getContext: not implemented method in org.apache.taglibs.standard.tag.common.xml.ForEachTag class!");
|
|
+ //return forEachTag.getContext();
|
|
+ }
|
|
+
|
|
+ // otherwise, create a new context referring to an empty document
|
|
+ XPathContext context = new XPathContext(false);
|
|
+ VariableStack variableStack = new JSTLVariableStack(pageContext);
|
|
+ context.setVarStack(variableStack);
|
|
+ int dtm = context.getDTMHandleFromNode(XmlUtil.newEmptyDocument());
|
|
+ context.pushCurrentNodeAndExpression(dtm, dtm);
|
|
+ return context;
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Return the Java value corresponding to an XPath result.
|
|
+ *
|
|
+ * @param xo the XPath type
|
|
+ * @return the corresponding Java value per the JSTL mapping rules
|
|
+ * @throws TransformerException if there was a problem converting the type
|
|
+ */
|
|
+ static Object coerceToJava(XObject xo) throws TransformerException {
|
|
+ if (xo instanceof XBoolean) {
|
|
+ return xo.bool();
|
|
+ } else if (xo instanceof XNumber) {
|
|
+ return xo.num();
|
|
+ } else if (xo instanceof XString) {
|
|
+ return xo.str();
|
|
+ } else if (xo instanceof XNodeSet) {
|
|
+ NodeList nodes = xo.nodelist();
|
|
+ // if there is only one node in the nodeset return it rather than the list
|
|
+ if (nodes.getLength() == 1) {
|
|
+ return nodes.item(0);
|
|
+ } else {
|
|
+ return nodes;
|
|
+ }
|
|
+ } else {
|
|
+ // unexpected result type
|
|
+ throw new AssertionError();
|
|
+ }
|
|
+ }
|
|
+}
|
|
Index: jakarta-taglibs-standard-1.1.2-src/standard/src/org/apache/taglibs/standard/tag/common/xml/XmlUtil.java
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ jakarta-taglibs-standard-1.1.2-src/standard/src/org/apache/taglibs/standard/tag/common/xml/XmlUtil.java
|
|
@@ -0,0 +1,279 @@
|
|
+/*
|
|
+ * Licensed to the Apache Software Foundation (ASF) under one or more
|
|
+ * contributor license agreements. See the NOTICE file distributed with
|
|
+ * this work for additional information regarding copyright ownership.
|
|
+ * The ASF licenses this file to You under the Apache License, Version 2.0
|
|
+ * (the "License"); you may not use this file except in compliance with
|
|
+ * the License. You may obtain a copy of the License at
|
|
+ *
|
|
+ * http://www.apache.org/licenses/LICENSE-2.0
|
|
+ *
|
|
+ * Unless required by applicable law or agreed to in writing, software
|
|
+ * distributed under the License is distributed on an "AS IS" BASIS,
|
|
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
+ * See the License for the specific language governing permissions and
|
|
+ * limitations under the License.
|
|
+ */
|
|
+package org.apache.taglibs.standard.tag.common.xml;
|
|
+
|
|
+import java.io.FileNotFoundException;
|
|
+import java.io.InputStream;
|
|
+import java.io.Reader;
|
|
+
|
|
+import javax.servlet.http.HttpServletRequest;
|
|
+import javax.servlet.jsp.PageContext;
|
|
+import javax.xml.XMLConstants;
|
|
+import javax.xml.parsers.DocumentBuilder;
|
|
+import javax.xml.parsers.DocumentBuilderFactory;
|
|
+import javax.xml.parsers.ParserConfigurationException;
|
|
+import javax.xml.transform.Source;
|
|
+import javax.xml.transform.Transformer;
|
|
+import javax.xml.transform.TransformerConfigurationException;
|
|
+import javax.xml.transform.TransformerException;
|
|
+import javax.xml.transform.TransformerFactory;
|
|
+import javax.xml.transform.URIResolver;
|
|
+import javax.xml.transform.sax.SAXSource;
|
|
+import javax.xml.transform.sax.SAXTransformerFactory;
|
|
+import javax.xml.transform.sax.TransformerHandler;
|
|
+import javax.xml.transform.stream.StreamSource;
|
|
+
|
|
+import org.apache.taglibs.standard.resources.Resources;
|
|
+import org.apache.taglibs.standard.util.UrlUtil;
|
|
+import org.w3c.dom.Document;
|
|
+import org.xml.sax.EntityResolver;
|
|
+import org.xml.sax.InputSource;
|
|
+import org.xml.sax.SAXException;
|
|
+import org.xml.sax.XMLReader;
|
|
+import org.xml.sax.helpers.XMLReaderFactory;
|
|
+
|
|
+/**
|
|
+ * Utilities for working with JAXP and SAX.
|
|
+ */
|
|
+public class XmlUtil {
|
|
+ private static final DocumentBuilderFactory dbf;
|
|
+ private static final SAXTransformerFactory stf;
|
|
+
|
|
+ static {
|
|
+ // from Java5 on DocumentBuilderFactory is thread safe and hence can be cached
|
|
+ dbf = DocumentBuilderFactory.newInstance();
|
|
+ dbf.setNamespaceAware(true);
|
|
+ dbf.setValidating(false);
|
|
+ try {
|
|
+ dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
|
|
+ } catch (ParserConfigurationException e) {
|
|
+ throw new AssertionError("Parser does not support secure processing");
|
|
+ }
|
|
+
|
|
+ TransformerFactory tf = TransformerFactory.newInstance();
|
|
+ if (!(tf instanceof SAXTransformerFactory)) {
|
|
+ throw new AssertionError("TransformerFactory does not support SAX");
|
|
+ }
|
|
+ try {
|
|
+ tf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
|
|
+ } catch (TransformerConfigurationException e) {
|
|
+ throw new AssertionError("TransformerFactory does not support secure processing");
|
|
+ }
|
|
+ stf = (SAXTransformerFactory) tf;
|
|
+ }
|
|
+
|
|
+
|
|
+ /**
|
|
+ * Create a new empty document.
|
|
+ *
|
|
+ * This method always allocates a new document as its root node might be
|
|
+ * exposed to other tags and potentially be mutated.
|
|
+ *
|
|
+ * @return a new empty document
|
|
+ */
|
|
+ static Document newEmptyDocument() {
|
|
+ return newDocumentBuilder().newDocument();
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Create a new DocumentBuilder configured for namespaces but not validating.
|
|
+ *
|
|
+ * @return a new, configured DocumentBuilder
|
|
+ */
|
|
+ static DocumentBuilder newDocumentBuilder() {
|
|
+ try {
|
|
+ return dbf.newDocumentBuilder();
|
|
+ } catch (ParserConfigurationException e) {
|
|
+ throw new AssertionError();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Create a new TransformerHandler.
|
|
+ * @return a new TransformerHandler
|
|
+ */
|
|
+ static TransformerHandler newTransformerHandler() throws TransformerConfigurationException {
|
|
+ return stf.newTransformerHandler();
|
|
+ }
|
|
+
|
|
+ static Transformer newTransformer(Source source) throws TransformerConfigurationException {
|
|
+ Transformer transformer = stf.newTransformer(source);
|
|
+ if (transformer == null) {
|
|
+ throw new TransformerConfigurationException("newTransformer returned null");
|
|
+ }
|
|
+ return transformer;
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Create an InputSource from a Reader.
|
|
+ *
|
|
+ * The systemId will be wrapped for use with JSTL's EntityResolver and UriResolver.
|
|
+ *
|
|
+ * @param reader the source of the XML
|
|
+ * @param systemId the system id
|
|
+ * @return a configured InputSource
|
|
+ */
|
|
+ static InputSource newInputSource(Reader reader, String systemId) {
|
|
+ InputSource source = new InputSource(reader);
|
|
+ source.setSystemId(wrapSystemId(systemId));
|
|
+ return source;
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Create an XMLReader that resolves entities using JSTL semantics.
|
|
+ * @param entityResolver for resolving using JSTL semamtics
|
|
+ * @return a new XMLReader
|
|
+ * @throws SAXException if there was a problem creating the reader
|
|
+ */
|
|
+ static XMLReader newXMLReader(JstlEntityResolver entityResolver) throws SAXException {
|
|
+ XMLReader xmlReader = XMLReaderFactory.createXMLReader();
|
|
+ xmlReader.setEntityResolver(entityResolver);
|
|
+ return xmlReader;
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Create a SAXSource from a Reader. Any entities will be resolved using JSTL semantics.
|
|
+ *
|
|
+ * @param reader the source of the XML
|
|
+ * @param systemId the system id
|
|
+ * @param entityResolver for resolving using JSTL semamtics
|
|
+ * @return a new SAXSource
|
|
+ * @throws SAXException if there was a problem creating the source
|
|
+ */
|
|
+ static SAXSource newSAXSource(Reader reader, String systemId, JstlEntityResolver entityResolver) throws SAXException {
|
|
+ SAXSource source = new SAXSource(newXMLReader(entityResolver), new InputSource(reader));
|
|
+ source.setSystemId(wrapSystemId(systemId));
|
|
+ return source;
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Wraps systemId with a "jstl:" prefix to prevent the parser from
|
|
+ * thinking that the URI is truly relative and resolving it against
|
|
+ * the current directory in the filesystem.
|
|
+ */
|
|
+ private static String wrapSystemId(String systemId) {
|
|
+ if (systemId == null) {
|
|
+ return "jstl:";
|
|
+ } else if (UrlUtil.isAbsoluteUrl(systemId)) {
|
|
+ return systemId;
|
|
+ } else {
|
|
+ return ("jstl:" + systemId);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * JSTL-specific implementation of EntityResolver.
|
|
+ */
|
|
+ static class JstlEntityResolver implements EntityResolver {
|
|
+ private final PageContext ctx;
|
|
+
|
|
+ public JstlEntityResolver(PageContext ctx) {
|
|
+ this.ctx = ctx;
|
|
+ }
|
|
+
|
|
+ public InputSource resolveEntity(String publicId, String systemId) throws FileNotFoundException {
|
|
+
|
|
+ // pass if we don't have a systemId
|
|
+ if (systemId == null) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ // strip leading "jstl:" off URL if applicable
|
|
+ if (systemId.startsWith("jstl:")) {
|
|
+ systemId = systemId.substring(5);
|
|
+ }
|
|
+
|
|
+ // we're only concerned with relative URLs
|
|
+ if (UrlUtil.isAbsoluteUrl(systemId)) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ // for relative URLs, load and wrap the resource.
|
|
+ // don't bother checking for 'null' since we specifically want
|
|
+ // the parser to fail if the resource doesn't exist
|
|
+ String path = systemId;
|
|
+ if (!path.startsWith("/")) {
|
|
+ String pagePath = ((HttpServletRequest) ctx.getRequest()).getServletPath();
|
|
+ String basePath = pagePath.substring(0, pagePath.lastIndexOf("/"));
|
|
+ path = basePath + "/" + systemId;
|
|
+ }
|
|
+
|
|
+ InputStream s = ctx.getServletContext().getResourceAsStream(path);
|
|
+ if (s == null) {
|
|
+ throw new FileNotFoundException(Resources.getMessage("UNABLE_TO_RESOLVE_ENTITY", systemId));
|
|
+ }
|
|
+ return new InputSource(s);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * JSTL-specific implementation of URIResolver.
|
|
+ */
|
|
+ static class JstlUriResolver implements URIResolver {
|
|
+ private final PageContext ctx;
|
|
+
|
|
+ public JstlUriResolver(PageContext ctx) {
|
|
+ this.ctx = ctx;
|
|
+ }
|
|
+
|
|
+ public Source resolve(String href, String base) throws TransformerException {
|
|
+
|
|
+ // pass if we don't have a systemId
|
|
+ if (href == null) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ // remove "jstl" marker from 'base'
|
|
+ // NOTE: how 'base' is determined varies among different Xalan
|
|
+ // xsltc implementations
|
|
+ int index;
|
|
+ if (base != null && (index = base.indexOf("jstl:")) != -1) {
|
|
+ base = base.substring(index + 5);
|
|
+ }
|
|
+
|
|
+ // we're only concerned with relative URLs
|
|
+ if (UrlUtil.isAbsoluteUrl(href)
|
|
+ || (base != null && UrlUtil.isAbsoluteUrl(base))) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ // base is relative; remove everything after trailing '/'
|
|
+ if (base == null || base.lastIndexOf("/") == -1) {
|
|
+ base = "";
|
|
+ } else {
|
|
+ base = base.substring(0, base.lastIndexOf("/") + 1);
|
|
+ }
|
|
+
|
|
+ // concatenate to produce the real URL we're interested in
|
|
+ String target = base + href;
|
|
+
|
|
+ // for relative URLs, load and wrap the resource.
|
|
+ // don't bother checking for 'null' since we specifically want
|
|
+ // the parser to fail if the resource doesn't exist
|
|
+ if (!target.startsWith("/")) {
|
|
+ String pagePath = ((HttpServletRequest) ctx.getRequest()).getServletPath();
|
|
+ String basePath = pagePath.substring(0, pagePath.lastIndexOf("/"));
|
|
+ target = basePath + "/" + target;
|
|
+ }
|
|
+ InputStream s = ctx.getServletContext().getResourceAsStream(target);
|
|
+ if (s == null) {
|
|
+ throw new TransformerException(Resources.getMessage("UNABLE_TO_RESOLVE_ENTITY", href));
|
|
+ }
|
|
+ return new StreamSource(s);
|
|
+ }
|
|
+ }
|
|
+}
|
|
Index: jakarta-taglibs-standard-1.1.2-src/standard/src/org/apache/taglibs/standard/tlv/JstlBaseTLV.java
|
|
===================================================================
|
|
--- jakarta-taglibs-standard-1.1.2-src.orig/standard/src/org/apache/taglibs/standard/tlv/JstlBaseTLV.java
|
|
+++ jakarta-taglibs-standard-1.1.2-src/standard/src/org/apache/taglibs/standard/tlv/JstlBaseTLV.java
|
|
@@ -17,6 +17,7 @@
|
|
package org.apache.taglibs.standard.tlv;
|
|
|
|
import java.io.IOException;
|
|
+import java.io.InputStream;
|
|
import java.util.HashMap;
|
|
import java.util.HashSet;
|
|
import java.util.Map;
|
|
@@ -30,15 +31,15 @@ import javax.servlet.jsp.tagext.PageData
|
|
import javax.servlet.jsp.tagext.TagData;
|
|
import javax.servlet.jsp.tagext.TagLibraryValidator;
|
|
import javax.servlet.jsp.tagext.ValidationMessage;
|
|
-import javax.xml.parsers.ParserConfigurationException;
|
|
-import javax.xml.parsers.SAXParser;
|
|
-import javax.xml.parsers.SAXParserFactory;
|
|
|
|
import org.apache.taglibs.standard.lang.support.ExpressionEvaluator;
|
|
import org.apache.taglibs.standard.lang.support.ExpressionEvaluatorManager;
|
|
import org.apache.taglibs.standard.resources.Resources;
|
|
+import org.apache.taglibs.standard.util.XmlUtil;
|
|
import org.xml.sax.Attributes;
|
|
+import org.xml.sax.InputSource;
|
|
import org.xml.sax.SAXException;
|
|
+import org.xml.sax.XMLReader;
|
|
import org.xml.sax.helpers.DefaultHandler;
|
|
|
|
/**
|
|
@@ -149,11 +150,19 @@ public abstract class JstlBaseTLV extend
|
|
DefaultHandler h = getHandler();
|
|
|
|
// parse the page
|
|
- SAXParserFactory f = SAXParserFactory.newInstance();
|
|
- f.setValidating(false);
|
|
- f.setNamespaceAware(true);
|
|
- SAXParser p = f.newSAXParser();
|
|
- p.parse(page.getInputStream(), h);
|
|
+ XMLReader xmlReader = XmlUtil.newXMLReader(null);
|
|
+ xmlReader.setContentHandler(h);
|
|
+ InputStream inputStream = page.getInputStream();
|
|
+ try {
|
|
+ xmlReader.parse(new InputSource(inputStream));
|
|
+ } finally {
|
|
+ try {
|
|
+ inputStream.close();
|
|
+ } catch (IOException e) {
|
|
+ // Suppressed.
|
|
+ }
|
|
+ }
|
|
+
|
|
|
|
if (messageVector.size() == 0)
|
|
return null;
|
|
@@ -162,8 +171,6 @@ public abstract class JstlBaseTLV extend
|
|
|
|
} catch (SAXException ex) {
|
|
return vmFromString(ex.toString());
|
|
- } catch (ParserConfigurationException ex) {
|
|
- return vmFromString(ex.toString());
|
|
} catch (IOException ex) {
|
|
return vmFromString(ex.toString());
|
|
}
|
|
Index: jakarta-taglibs-standard-1.1.2-src/standard/src/org/apache/taglibs/standard/util/UnclosableWriter.java
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ jakarta-taglibs-standard-1.1.2-src/standard/src/org/apache/taglibs/standard/util/UnclosableWriter.java
|
|
@@ -0,0 +1,44 @@
|
|
+/*
|
|
+ * Licensed to the Apache Software Foundation (ASF) under one or more
|
|
+ * contributor license agreements. See the NOTICE file distributed with
|
|
+ * this work for additional information regarding copyright ownership.
|
|
+ * The ASF licenses this file to You under the Apache License, Version 2.0
|
|
+ * (the "License"); you may not use this file except in compliance with
|
|
+ * the License. You may obtain a copy of the License at
|
|
+ *
|
|
+ * http://www.apache.org/licenses/LICENSE-2.0
|
|
+ *
|
|
+ * Unless required by applicable law or agreed to in writing, software
|
|
+ * distributed under the License is distributed on an "AS IS" BASIS,
|
|
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
+ * See the License for the specific language governing permissions and
|
|
+ * limitations under the License.
|
|
+ */
|
|
+package org.apache.taglibs.standard.util;
|
|
+
|
|
+import java.io.IOException;
|
|
+import java.io.Writer;
|
|
+
|
|
+/**
|
|
+ * A Writer based on a wrapped Writer but ignoring requests to
|
|
+ * close() and flush() it. (Someone must have wrapped the
|
|
+ * toilet in my office similarly...)
|
|
+ */
|
|
+public class UnclosableWriter extends Writer {
|
|
+ // TODO: shouldn't we be delegating all methods?
|
|
+ private Writer w;
|
|
+
|
|
+ public UnclosableWriter(Writer w) {
|
|
+ this.w = w;
|
|
+ }
|
|
+
|
|
+ public void close() {
|
|
+ }
|
|
+
|
|
+ public void flush() {
|
|
+ }
|
|
+
|
|
+ public void write(char[] cbuf, int off, int len) throws IOException {
|
|
+ w.write(cbuf, off, len);
|
|
+ }
|
|
+}
|
|
Index: jakarta-taglibs-standard-1.1.2-src/standard/src/org/apache/taglibs/standard/util/UrlUtil.java
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ jakarta-taglibs-standard-1.1.2-src/standard/src/org/apache/taglibs/standard/util/UrlUtil.java
|
|
@@ -0,0 +1,80 @@
|
|
+/*
|
|
+ * Licensed to the Apache Software Foundation (ASF) under one or more
|
|
+ * contributor license agreements. See the NOTICE file distributed with
|
|
+ * this work for additional information regarding copyright ownership.
|
|
+ * The ASF licenses this file to You under the Apache License, Version 2.0
|
|
+ * (the "License"); you may not use this file except in compliance with
|
|
+ * the License. You may obtain a copy of the License at
|
|
+ *
|
|
+ * http://www.apache.org/licenses/LICENSE-2.0
|
|
+ *
|
|
+ * Unless required by applicable law or agreed to in writing, software
|
|
+ * distributed under the License is distributed on an "AS IS" BASIS,
|
|
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
+ * See the License for the specific language governing permissions and
|
|
+ * limitations under the License.
|
|
+ */
|
|
+package org.apache.taglibs.standard.util;
|
|
+
|
|
+import java.util.BitSet;
|
|
+
|
|
+/**
|
|
+ * Utilities for working with URLs.
|
|
+ */
|
|
+public class UrlUtil {
|
|
+ /**
|
|
+ * <p>Valid characters in a scheme.</p>
|
|
+ * <p>RFC 1738 says the following:</p>
|
|
+ * <blockquote>
|
|
+ * Scheme names consist of a sequence of characters. The lower
|
|
+ * case letters "a"--"z", digits, and the characters plus ("+"),
|
|
+ * period ("."), and hyphen ("-") are allowed. For resiliency,
|
|
+ * programs interpreting URLs should treat upper case letters as
|
|
+ * equivalent to lower case in scheme names (e.g., allow "HTTP" as
|
|
+ * well as "http").
|
|
+ * </blockquote>
|
|
+ * <p>We treat as absolute any URL that begins with such a scheme name,
|
|
+ * followed by a colon.</p>
|
|
+ */
|
|
+/*
|
|
+ private static final String VALID_SCHEME_CHARS =
|
|
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+.-";
|
|
+*/
|
|
+ private static final BitSet VALID_SCHEME_CHARS;
|
|
+ static {
|
|
+ VALID_SCHEME_CHARS = new BitSet(128);
|
|
+ VALID_SCHEME_CHARS.set('A', 'Z' + 1);
|
|
+ VALID_SCHEME_CHARS.set('a', 'z' + 1);
|
|
+ VALID_SCHEME_CHARS.set('0', '9' + 1);
|
|
+ VALID_SCHEME_CHARS.set('+');
|
|
+ VALID_SCHEME_CHARS.set('.');
|
|
+ VALID_SCHEME_CHARS.set('-');
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Determine if a URL is absolute by JSTL's definition.
|
|
+ */
|
|
+ public static boolean isAbsoluteUrl(String url) {
|
|
+ // a null URL is not absolute, by our definition
|
|
+ if (url == null) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ // do a fast, simple check first
|
|
+ int colonPos = url.indexOf(":");
|
|
+ if (colonPos == -1) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ // if we DO have a colon, make sure that every character
|
|
+ // leading up to it is a valid scheme character
|
|
+ for (int i = 0; i < colonPos; i++) {
|
|
+ if (!VALID_SCHEME_CHARS.get(url.charAt(i))) {
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // if so, we've got an absolute url
|
|
+ return true;
|
|
+ }
|
|
+}
|
|
Index: jakarta-taglibs-standard-1.1.2-src/standard/src/org/apache/taglibs/standard/util/XmlUtil.java
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ jakarta-taglibs-standard-1.1.2-src/standard/src/org/apache/taglibs/standard/util/XmlUtil.java
|
|
@@ -0,0 +1,345 @@
|
|
+/*
|
|
+ * Licensed to the Apache Software Foundation (ASF) under one or more
|
|
+ * contributor license agreements. See the NOTICE file distributed with
|
|
+ * this work for additional information regarding copyright ownership.
|
|
+ * The ASF licenses this file to You under the Apache License, Version 2.0
|
|
+ * (the "License"); you may not use this file except in compliance with
|
|
+ * the License. You may obtain a copy of the License at
|
|
+ *
|
|
+ * http://www.apache.org/licenses/LICENSE-2.0
|
|
+ *
|
|
+ * Unless required by applicable law or agreed to in writing, software
|
|
+ * distributed under the License is distributed on an "AS IS" BASIS,
|
|
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
+ * See the License for the specific language governing permissions and
|
|
+ * limitations under the License.
|
|
+ */
|
|
+package org.apache.taglibs.standard.util;
|
|
+
|
|
+import java.io.FileNotFoundException;
|
|
+import java.io.InputStream;
|
|
+import java.io.Reader;
|
|
+import java.security.AccessController;
|
|
+import java.security.PrivilegedActionException;
|
|
+import java.security.PrivilegedExceptionAction;
|
|
+import java.util.concurrent.Callable;
|
|
+
|
|
+import javax.servlet.http.HttpServletRequest;
|
|
+import javax.servlet.jsp.PageContext;
|
|
+import javax.xml.XMLConstants;
|
|
+import javax.xml.parsers.DocumentBuilder;
|
|
+import javax.xml.parsers.DocumentBuilderFactory;
|
|
+import javax.xml.parsers.ParserConfigurationException;
|
|
+import javax.xml.transform.Source;
|
|
+import javax.xml.transform.Transformer;
|
|
+import javax.xml.transform.TransformerConfigurationException;
|
|
+import javax.xml.transform.TransformerException;
|
|
+import javax.xml.transform.TransformerFactory;
|
|
+import javax.xml.transform.URIResolver;
|
|
+import javax.xml.transform.sax.SAXSource;
|
|
+import javax.xml.transform.sax.SAXTransformerFactory;
|
|
+import javax.xml.transform.sax.TransformerHandler;
|
|
+import javax.xml.transform.stream.StreamSource;
|
|
+
|
|
+import org.apache.taglibs.standard.resources.Resources;
|
|
+import org.w3c.dom.Document;
|
|
+import org.xml.sax.EntityResolver;
|
|
+import org.xml.sax.InputSource;
|
|
+import org.xml.sax.SAXException;
|
|
+import org.xml.sax.XMLReader;
|
|
+import org.xml.sax.helpers.XMLReaderFactory;
|
|
+
|
|
+/**
|
|
+ * Utilities for working with JAXP and SAX.
|
|
+ */
|
|
+public class XmlUtil {
|
|
+ /* Cache factory classes when this class is initialized (since Java1.5 factories are required
|
|
+ * to be thread safe).
|
|
+ *
|
|
+ * As JavaEE 5 requires JSTL to be provided by the container we use our ClassLoader to locate
|
|
+ * the implementations rather than the application's. As we don't know the actual implementation
|
|
+ * class in use we can't use the newInstance() variant that allows the ClassLoader to be
|
|
+ * specified so we use the no-arg form and coerce the TCCL (which may be restricted by the
|
|
+ * AccessController).
|
|
+ */
|
|
+ private static final DocumentBuilderFactory PARSER_FACTORY;
|
|
+ private static final SAXTransformerFactory TRANSFORMER_FACTORY;
|
|
+ static {
|
|
+ try {
|
|
+ PARSER_FACTORY = runWithOurClassLoader(new Callable<DocumentBuilderFactory>() {
|
|
+ public DocumentBuilderFactory call() throws ParserConfigurationException {
|
|
+ return DocumentBuilderFactory.newInstance();
|
|
+ }
|
|
+ }, ParserConfigurationException.class);
|
|
+ PARSER_FACTORY.setNamespaceAware(true);
|
|
+ PARSER_FACTORY.setValidating(false);
|
|
+ PARSER_FACTORY.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
|
|
+ } catch (ParserConfigurationException e) {
|
|
+ throw new ExceptionInInitializerError(e);
|
|
+ }
|
|
+ try {
|
|
+ TRANSFORMER_FACTORY = runWithOurClassLoader(new Callable<SAXTransformerFactory>() {
|
|
+ public SAXTransformerFactory call() throws TransformerConfigurationException {
|
|
+ TransformerFactory tf = TransformerFactory.newInstance();
|
|
+ if (!(tf instanceof SAXTransformerFactory)) {
|
|
+ throw new TransformerConfigurationException("TransformerFactory does not support SAX");
|
|
+ }
|
|
+ return (SAXTransformerFactory) tf;
|
|
+ }
|
|
+ }, TransformerConfigurationException.class);
|
|
+ TRANSFORMER_FACTORY.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
|
|
+ } catch (TransformerConfigurationException e) {
|
|
+ throw new ExceptionInInitializerError(e);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Create a new empty document.
|
|
+ *
|
|
+ * @return a new empty document
|
|
+ */
|
|
+ public static Document newEmptyDocument() {
|
|
+ return newDocumentBuilder().newDocument();
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Create a new DocumentBuilder configured for namespaces but not validating.
|
|
+ *
|
|
+ * @return a new, configured DocumentBuilder
|
|
+ */
|
|
+ public static DocumentBuilder newDocumentBuilder() {
|
|
+ try {
|
|
+ return PARSER_FACTORY.newDocumentBuilder();
|
|
+ } catch (ParserConfigurationException e) {
|
|
+ throw (Error) new AssertionError().initCause(e);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Create a new TransformerHandler.
|
|
+ * @return a new TransformerHandler
|
|
+ */
|
|
+ public static TransformerHandler newTransformerHandler() throws TransformerConfigurationException {
|
|
+ return TRANSFORMER_FACTORY.newTransformerHandler();
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Create a new Transformer from an XSLT.
|
|
+ * @param source the source of the XSLT.
|
|
+ * @return a new Transformer
|
|
+ * @throws TransformerConfigurationException if there was a problem creating the Transformer from the XSLT
|
|
+ */
|
|
+ public static Transformer newTransformer(Source source) throws TransformerConfigurationException {
|
|
+ Transformer transformer = TRANSFORMER_FACTORY.newTransformer(source);
|
|
+ // Although newTansformer() is not allowed to return null, Xalan does.
|
|
+ // Trap that here by throwing the expected TransformerConfigurationException.
|
|
+ if (transformer == null) {
|
|
+ throw new TransformerConfigurationException("newTransformer returned null. XSLT may be invalid.");
|
|
+ }
|
|
+ return transformer;
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Create an InputSource from a Reader.
|
|
+ *
|
|
+ * The systemId will be wrapped for use with JSTL's EntityResolver and UriResolver.
|
|
+ *
|
|
+ * @param reader the source of the XML
|
|
+ * @param systemId the system id
|
|
+ * @return a configured InputSource
|
|
+ */
|
|
+ public static InputSource newInputSource(Reader reader, String systemId) {
|
|
+ InputSource source = new InputSource(reader);
|
|
+ source.setSystemId(wrapSystemId(systemId));
|
|
+ return source;
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Create an XMLReader that resolves entities using JSTL semantics.
|
|
+ * @param entityResolver for resolving using JSTL semamtics
|
|
+ * @return a new XMLReader
|
|
+ * @throws SAXException if there was a problem creating the reader
|
|
+ */
|
|
+ public static XMLReader newXMLReader(JstlEntityResolver entityResolver) throws SAXException {
|
|
+ XMLReader xmlReader = runWithOurClassLoader(new Callable<XMLReader>() {
|
|
+ public XMLReader call() throws SAXException {
|
|
+ return XMLReaderFactory.createXMLReader();
|
|
+ }
|
|
+ }, SAXException.class);
|
|
+ xmlReader.setEntityResolver(entityResolver);
|
|
+ xmlReader.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
|
|
+ return xmlReader;
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Create a SAXSource from a Reader. Any entities will be resolved using JSTL semantics.
|
|
+ *
|
|
+ * @param reader the source of the XML
|
|
+ * @param systemId the system id
|
|
+ * @param entityResolver for resolving using JSTL semamtics
|
|
+ * @return a new SAXSource
|
|
+ * @throws SAXException if there was a problem creating the source
|
|
+ */
|
|
+ public static SAXSource newSAXSource(Reader reader, String systemId, JstlEntityResolver entityResolver) throws SAXException {
|
|
+ SAXSource source = new SAXSource(newXMLReader(entityResolver), new InputSource(reader));
|
|
+ source.setSystemId(wrapSystemId(systemId));
|
|
+ return source;
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Wraps systemId with a "jstl:" prefix to prevent the parser from
|
|
+ * thinking that the URI is truly relative and resolving it against
|
|
+ * the current directory in the filesystem.
|
|
+ */
|
|
+ private static String wrapSystemId(String systemId) {
|
|
+ if (systemId == null) {
|
|
+ return "jstl:";
|
|
+ } else if (UrlUtil.isAbsoluteUrl(systemId)) {
|
|
+ return systemId;
|
|
+ } else {
|
|
+ return ("jstl:" + systemId);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * JSTL-specific implementation of EntityResolver.
|
|
+ */
|
|
+ public static class JstlEntityResolver implements EntityResolver {
|
|
+ private final PageContext ctx;
|
|
+
|
|
+ public JstlEntityResolver(PageContext ctx) {
|
|
+ this.ctx = ctx;
|
|
+ }
|
|
+
|
|
+ public InputSource resolveEntity(String publicId, String systemId) throws FileNotFoundException {
|
|
+
|
|
+ // pass if we don't have a systemId
|
|
+ if (systemId == null) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ // strip leading "jstl:" off URL if applicable
|
|
+ if (systemId.startsWith("jstl:")) {
|
|
+ systemId = systemId.substring(5);
|
|
+ }
|
|
+
|
|
+ // we're only concerned with relative URLs
|
|
+ if (UrlUtil.isAbsoluteUrl(systemId)) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ // for relative URLs, load and wrap the resource.
|
|
+ // don't bother checking for 'null' since we specifically want
|
|
+ // the parser to fail if the resource doesn't exist
|
|
+ String path = systemId;
|
|
+ if (!path.startsWith("/")) {
|
|
+ String pagePath = ((HttpServletRequest) ctx.getRequest()).getServletPath();
|
|
+ String basePath = pagePath.substring(0, pagePath.lastIndexOf("/"));
|
|
+ path = basePath + "/" + systemId;
|
|
+ }
|
|
+
|
|
+ InputStream s = ctx.getServletContext().getResourceAsStream(path);
|
|
+ if (s == null) {
|
|
+ throw new FileNotFoundException(Resources.getMessage("UNABLE_TO_RESOLVE_ENTITY", systemId));
|
|
+ }
|
|
+ return new InputSource(s);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * JSTL-specific implementation of URIResolver.
|
|
+ */
|
|
+ public static class JstlUriResolver implements URIResolver {
|
|
+ private final PageContext ctx;
|
|
+
|
|
+ public JstlUriResolver(PageContext ctx) {
|
|
+ this.ctx = ctx;
|
|
+ }
|
|
+
|
|
+ public Source resolve(String href, String base) throws TransformerException {
|
|
+
|
|
+ // pass if we don't have a systemId
|
|
+ if (href == null) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ // remove "jstl" marker from 'base'
|
|
+ // NOTE: how 'base' is determined varies among different Xalan
|
|
+ // xsltc implementations
|
|
+ int index;
|
|
+ if (base != null && (index = base.indexOf("jstl:")) != -1) {
|
|
+ base = base.substring(index + 5);
|
|
+ }
|
|
+
|
|
+ // we're only concerned with relative URLs
|
|
+ if (UrlUtil.isAbsoluteUrl(href)
|
|
+ || (base != null && UrlUtil.isAbsoluteUrl(base))) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ // base is relative; remove everything after trailing '/'
|
|
+ if (base == null || base.lastIndexOf("/") == -1) {
|
|
+ base = "";
|
|
+ } else {
|
|
+ base = base.substring(0, base.lastIndexOf("/") + 1);
|
|
+ }
|
|
+
|
|
+ // concatenate to produce the real URL we're interested in
|
|
+ String target = base + href;
|
|
+
|
|
+ // for relative URLs, load and wrap the resource.
|
|
+ // don't bother checking for 'null' since we specifically want
|
|
+ // the parser to fail if the resource doesn't exist
|
|
+ if (!target.startsWith("/")) {
|
|
+ String pagePath = ((HttpServletRequest) ctx.getRequest()).getServletPath();
|
|
+ String basePath = pagePath.substring(0, pagePath.lastIndexOf("/"));
|
|
+ target = basePath + "/" + target;
|
|
+ }
|
|
+ InputStream s = ctx.getServletContext().getResourceAsStream(target);
|
|
+ if (s == null) {
|
|
+ throw new TransformerException(Resources.getMessage("UNABLE_TO_RESOLVE_ENTITY", href));
|
|
+ }
|
|
+ return new StreamSource(s);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Performs an action using this Class's ClassLoader as the Thread context ClassLoader.
|
|
+ *
|
|
+ * @param action the action to perform
|
|
+ * @param allowed an Exception that might be thrown by the action
|
|
+ * @param <T> the type of the result
|
|
+ * @param <E> the type of the allowed Exception
|
|
+ * @return the result of the action
|
|
+ * @throws E if the action threw the allowed Exception
|
|
+ */
|
|
+ private static <T, E extends Exception> T runWithOurClassLoader(final Callable<T> action, Class<E> allowed) throws E {
|
|
+ PrivilegedExceptionAction<T> actionWithClassloader = new PrivilegedExceptionAction<T>() {
|
|
+ public T run() throws Exception {
|
|
+ ClassLoader original = Thread.currentThread().getContextClassLoader();
|
|
+ ClassLoader ours = XmlUtil.class.getClassLoader();
|
|
+ // Don't override the TCCL if it is not needed.
|
|
+ if (original == ours) {
|
|
+ return action.call();
|
|
+ } else {
|
|
+ try {
|
|
+ Thread.currentThread().setContextClassLoader(ours);
|
|
+ return action.call();
|
|
+ } finally {
|
|
+ Thread.currentThread().setContextClassLoader(original);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ };
|
|
+ try {
|
|
+ return AccessController.doPrivileged(actionWithClassloader);
|
|
+ } catch (PrivilegedActionException e) {
|
|
+ Throwable cause = e.getCause();
|
|
+ if (allowed.isInstance(cause)) {
|
|
+ throw allowed.cast(cause);
|
|
+ } else {
|
|
+ throw (Error) new AssertionError().initCause(cause);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|