You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@struts.apache.org by mu...@apache.org on 2009/09/28 02:43:39 UTC
svn commit: r819435 [7/23] - in
/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper:
./ compiler/ compiler/tagplugin/ el/ runtime/ security/ servlet/
tagplugins/ tagplugins/jstl/ tagplugins/jstl/core/ util/ xmlparser/
Added: struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/JspDocumentParser.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/JspDocumentParser.java?rev=819435&view=auto
==============================================================================
--- struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/JspDocumentParser.java (added)
+++ struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/JspDocumentParser.java Mon Sep 28 00:43:34 2009
@@ -0,0 +1,1453 @@
+/*
+ * 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.jasper.compiler;
+
+import java.io.CharArrayWriter;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.jar.JarFile;
+
+import javax.servlet.jsp.tagext.TagFileInfo;
+import javax.servlet.jsp.tagext.TagInfo;
+import javax.servlet.jsp.tagext.TagLibraryInfo;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.apache.jasper.JasperException;
+import org.apache.jasper.JspCompilationContext;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.ext.LexicalHandler;
+import org.xml.sax.helpers.AttributesImpl;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * Class implementing a parser for a JSP document, that is, a JSP page in XML
+ * syntax.
+ *
+ * @author Jan Luehe
+ * @author Kin-man Chung
+ */
+
+class JspDocumentParser
+ extends DefaultHandler
+ implements LexicalHandler, TagConstants {
+
+ private static final String JSP_VERSION = "version";
+ private static final String LEXICAL_HANDLER_PROPERTY =
+ "http://xml.org/sax/properties/lexical-handler";
+ private static final String JSP_URI = "http://java.sun.com/JSP/Page";
+
+ private static final EnableDTDValidationException ENABLE_DTD_VALIDATION_EXCEPTION =
+ new EnableDTDValidationException(
+ "jsp.error.enable_dtd_validation",
+ null);
+
+ private ParserController parserController;
+ private JspCompilationContext ctxt;
+ private PageInfo pageInfo;
+ private String path;
+ private StringBuffer charBuffer;
+
+ // Node representing the XML element currently being parsed
+ private Node current;
+
+ /*
+ * Outermost (in the nesting hierarchy) node whose body is declared to be
+ * scriptless. If a node's body is declared to be scriptless, all its
+ * nested nodes must be scriptless, too.
+ */
+ private Node scriptlessBodyNode;
+
+ private Locator locator;
+
+ //Mark representing the start of the current element. Note
+ //that locator.getLineNumber() and locator.getColumnNumber()
+ //return the line and column numbers for the character
+ //immediately _following_ the current element. The underlying
+ //XMl parser eats white space that is not part of character
+ //data, so for Nodes that are not created from character data,
+ //this is the best we can do. But when we parse character data,
+ //we get an accurate starting location by starting with startMark
+ //as set by the previous element, and updating it as we advance
+ //through the characters.
+ private Mark startMark;
+
+ // Flag indicating whether we are inside DTD declarations
+ private boolean inDTD;
+
+ private boolean isValidating;
+
+ private ErrorDispatcher err;
+ private boolean isTagFile;
+ private boolean directivesOnly;
+ private boolean isTop;
+
+ // Nesting level of Tag dependent bodies
+ private int tagDependentNesting = 0;
+ // Flag set to delay incrmenting tagDependentNesting until jsp:body
+ // is first encountered
+ private boolean tagDependentPending = false;
+
+ /*
+ * Constructor
+ */
+ public JspDocumentParser(
+ ParserController pc,
+ String path,
+ boolean isTagFile,
+ boolean directivesOnly) {
+ this.parserController = pc;
+ this.ctxt = pc.getJspCompilationContext();
+ this.pageInfo = pc.getCompiler().getPageInfo();
+ this.err = pc.getCompiler().getErrorDispatcher();
+ this.path = path;
+ this.isTagFile = isTagFile;
+ this.directivesOnly = directivesOnly;
+ this.isTop = true;
+ }
+
+ /*
+ * Parses a JSP document by responding to SAX events.
+ *
+ * @throws JasperException
+ */
+ public static Node.Nodes parse(
+ ParserController pc,
+ String path,
+ JarFile jarFile,
+ Node parent,
+ boolean isTagFile,
+ boolean directivesOnly,
+ String pageEnc,
+ String jspConfigPageEnc,
+ boolean isEncodingSpecifiedInProlog,
+ boolean isBomPresent)
+ throws JasperException {
+
+ JspDocumentParser jspDocParser =
+ new JspDocumentParser(pc, path, isTagFile, directivesOnly);
+ Node.Nodes pageNodes = null;
+
+ try {
+
+ // Create dummy root and initialize it with given page encodings
+ Node.Root dummyRoot = new Node.Root(null, parent, true);
+ dummyRoot.setPageEncoding(pageEnc);
+ dummyRoot.setJspConfigPageEncoding(jspConfigPageEnc);
+ dummyRoot.setIsEncodingSpecifiedInProlog(
+ isEncodingSpecifiedInProlog);
+ dummyRoot.setIsBomPresent(isBomPresent);
+ jspDocParser.current = dummyRoot;
+ if (parent == null) {
+ jspDocParser.addInclude(
+ dummyRoot,
+ jspDocParser.pageInfo.getIncludePrelude());
+ } else {
+ jspDocParser.isTop = false;
+ }
+
+ // Parse the input
+ SAXParser saxParser = getSAXParser(false, jspDocParser);
+ InputStream inStream = null;
+ try {
+ inStream = JspUtil.getInputStream(path, jarFile,
+ jspDocParser.ctxt,
+ jspDocParser.err);
+ saxParser.parse(new InputSource(inStream), jspDocParser);
+ } catch (EnableDTDValidationException e) {
+ saxParser = getSAXParser(true, jspDocParser);
+ jspDocParser.isValidating = true;
+ if (inStream != null) {
+ try {
+ inStream.close();
+ } catch (Exception any) {
+ }
+ }
+ inStream = JspUtil.getInputStream(path, jarFile,
+ jspDocParser.ctxt,
+ jspDocParser.err);
+ saxParser.parse(new InputSource(inStream), jspDocParser);
+ } finally {
+ if (inStream != null) {
+ try {
+ inStream.close();
+ } catch (Exception any) {
+ }
+ }
+ }
+
+ if (parent == null) {
+ jspDocParser.addInclude(
+ dummyRoot,
+ jspDocParser.pageInfo.getIncludeCoda());
+ }
+
+ // Create Node.Nodes from dummy root
+ pageNodes = new Node.Nodes(dummyRoot);
+
+ } catch (IOException ioe) {
+ jspDocParser.err.jspError("jsp.error.data.file.read", path, ioe);
+ } catch (SAXParseException e) {
+ jspDocParser.err.jspError
+ (new Mark(jspDocParser.ctxt, path, e.getLineNumber(),
+ e.getColumnNumber()),
+ e.getMessage());
+ } catch (Exception e) {
+ jspDocParser.err.jspError(e);
+ }
+
+ return pageNodes;
+ }
+
+ /*
+ * Processes the given list of included files.
+ *
+ * This is used to implement the include-prelude and include-coda
+ * subelements of the jsp-config element in web.xml
+ */
+ private void addInclude(Node parent, List files) throws SAXException {
+ if (files != null) {
+ Iterator iter = files.iterator();
+ while (iter.hasNext()) {
+ String file = (String)iter.next();
+ AttributesImpl attrs = new AttributesImpl();
+ attrs.addAttribute("", "file", "file", "CDATA", file);
+
+ // Create a dummy Include directive node
+ Node includeDir =
+ new Node.IncludeDirective(attrs, null, // XXX
+ parent);
+ processIncludeDirective(file, includeDir);
+ }
+ }
+ }
+
+ /*
+ * Receives notification of the start of an element.
+ *
+ * This method assigns the given tag attributes to one of 3 buckets:
+ *
+ * - "xmlns" attributes that represent (standard or custom) tag libraries.
+ * - "xmlns" attributes that do not represent tag libraries.
+ * - all remaining attributes.
+ *
+ * For each "xmlns" attribute that represents a custom tag library, the
+ * corresponding TagLibraryInfo object is added to the set of custom
+ * tag libraries.
+ */
+ public void startElement(
+ String uri,
+ String localName,
+ String qName,
+ Attributes attrs)
+ throws SAXException {
+
+ AttributesImpl taglibAttrs = null;
+ AttributesImpl nonTaglibAttrs = null;
+ AttributesImpl nonTaglibXmlnsAttrs = null;
+
+ processChars();
+
+ checkPrefixes(uri, qName, attrs);
+
+ if (directivesOnly &&
+ !(JSP_URI.equals(uri) && localName.startsWith(DIRECTIVE_ACTION))) {
+ return;
+ }
+
+ String currentPrefix = getPrefix(current.getQName());
+
+ // jsp:text must not have any subelements
+ if (JSP_URI.equals(uri) && TEXT_ACTION.equals(current.getLocalName())
+ && "jsp".equals(currentPrefix)) {
+ throw new SAXParseException(
+ Localizer.getMessage("jsp.error.text.has_subelement"),
+ locator);
+ }
+
+ startMark = new Mark(ctxt, path, locator.getLineNumber(),
+ locator.getColumnNumber());
+
+ if (attrs != null) {
+ /*
+ * Notice that due to a bug in the underlying SAX parser, the
+ * attributes must be enumerated in descending order.
+ */
+ boolean isTaglib = false;
+ for (int i = attrs.getLength() - 1; i >= 0; i--) {
+ isTaglib = false;
+ String attrQName = attrs.getQName(i);
+ if (!attrQName.startsWith("xmlns")) {
+ if (nonTaglibAttrs == null) {
+ nonTaglibAttrs = new AttributesImpl();
+ }
+ nonTaglibAttrs.addAttribute(
+ attrs.getURI(i),
+ attrs.getLocalName(i),
+ attrs.getQName(i),
+ attrs.getType(i),
+ attrs.getValue(i));
+ } else {
+ if (attrQName.startsWith("xmlns:jsp")) {
+ isTaglib = true;
+ } else {
+ String attrUri = attrs.getValue(i);
+ // TaglibInfo for this uri already established in
+ // startPrefixMapping
+ isTaglib = pageInfo.hasTaglib(attrUri);
+ }
+ if (isTaglib) {
+ if (taglibAttrs == null) {
+ taglibAttrs = new AttributesImpl();
+ }
+ taglibAttrs.addAttribute(
+ attrs.getURI(i),
+ attrs.getLocalName(i),
+ attrs.getQName(i),
+ attrs.getType(i),
+ attrs.getValue(i));
+ } else {
+ if (nonTaglibXmlnsAttrs == null) {
+ nonTaglibXmlnsAttrs = new AttributesImpl();
+ }
+ nonTaglibXmlnsAttrs.addAttribute(
+ attrs.getURI(i),
+ attrs.getLocalName(i),
+ attrs.getQName(i),
+ attrs.getType(i),
+ attrs.getValue(i));
+ }
+ }
+ }
+ }
+
+ Node node = null;
+
+ if (tagDependentPending && JSP_URI.equals(uri) &&
+ localName.equals(BODY_ACTION)) {
+ tagDependentPending = false;
+ tagDependentNesting++;
+ current =
+ parseStandardAction(
+ qName,
+ localName,
+ nonTaglibAttrs,
+ nonTaglibXmlnsAttrs,
+ taglibAttrs,
+ startMark,
+ current);
+ return;
+ }
+
+ if (tagDependentPending && JSP_URI.equals(uri) &&
+ localName.equals(ATTRIBUTE_ACTION)) {
+ current =
+ parseStandardAction(
+ qName,
+ localName,
+ nonTaglibAttrs,
+ nonTaglibXmlnsAttrs,
+ taglibAttrs,
+ startMark,
+ current);
+ return;
+ }
+
+ if (tagDependentPending) {
+ tagDependentPending = false;
+ tagDependentNesting++;
+ }
+
+ if (tagDependentNesting > 0) {
+ node =
+ new Node.UninterpretedTag(
+ qName,
+ localName,
+ nonTaglibAttrs,
+ nonTaglibXmlnsAttrs,
+ taglibAttrs,
+ startMark,
+ current);
+ } else if (JSP_URI.equals(uri)) {
+ node =
+ parseStandardAction(
+ qName,
+ localName,
+ nonTaglibAttrs,
+ nonTaglibXmlnsAttrs,
+ taglibAttrs,
+ startMark,
+ current);
+ } else {
+ node =
+ parseCustomAction(
+ qName,
+ localName,
+ uri,
+ nonTaglibAttrs,
+ nonTaglibXmlnsAttrs,
+ taglibAttrs,
+ startMark,
+ current);
+ if (node == null) {
+ node =
+ new Node.UninterpretedTag(
+ qName,
+ localName,
+ nonTaglibAttrs,
+ nonTaglibXmlnsAttrs,
+ taglibAttrs,
+ startMark,
+ current);
+ } else {
+ // custom action
+ String bodyType = getBodyType((Node.CustomTag) node);
+
+ if (scriptlessBodyNode == null
+ && bodyType.equalsIgnoreCase(TagInfo.BODY_CONTENT_SCRIPTLESS)) {
+ scriptlessBodyNode = node;
+ }
+ else if (TagInfo.BODY_CONTENT_TAG_DEPENDENT.equalsIgnoreCase(bodyType)) {
+ tagDependentPending = true;
+ }
+ }
+ }
+
+ current = node;
+ }
+
+ /*
+ * Receives notification of character data inside an element.
+ *
+ * The SAX does not call this method with all of the template text, but may
+ * invoke this method with chunks of it. This is a problem when we try
+ * to determine if the text contains only whitespaces, or when we are
+ * looking for an EL expression string. Therefore it is necessary to
+ * buffer and concatenate the chunks and process the concatenated text
+ * later (at beginTag and endTag)
+ *
+ * @param buf The characters
+ * @param offset The start position in the character array
+ * @param len The number of characters to use from the character array
+ *
+ * @throws SAXException
+ */
+ public void characters(char[] buf, int offset, int len) {
+
+ if (charBuffer == null) {
+ charBuffer = new StringBuffer();
+ }
+ charBuffer.append(buf, offset, len);
+ }
+
+ private void processChars() throws SAXException {
+
+ if (charBuffer == null || directivesOnly) {
+ return;
+ }
+
+ /*
+ * JSP.6.1.1: All textual nodes that have only white space are to be
+ * dropped from the document, except for nodes in a jsp:text element,
+ * and any leading and trailing white-space-only textual nodes in a
+ * jsp:attribute whose 'trim' attribute is set to FALSE, which are to
+ * be kept verbatim.
+ * JSP.6.2.3 defines white space characters.
+ */
+ boolean isAllSpace = true;
+ if (!(current instanceof Node.JspText)
+ && !(current instanceof Node.NamedAttribute)) {
+ for (int i = 0; i < charBuffer.length(); i++) {
+ if (!(charBuffer.charAt(i) == ' '
+ || charBuffer.charAt(i) == '\n'
+ || charBuffer.charAt(i) == '\r'
+ || charBuffer.charAt(i) == '\t')) {
+ isAllSpace = false;
+ break;
+ }
+ }
+ }
+
+ if (!isAllSpace && tagDependentPending) {
+ tagDependentPending = false;
+ tagDependentNesting++;
+ }
+
+ if (tagDependentNesting > 0) {
+ if (charBuffer.length() > 0) {
+ new Node.TemplateText(charBuffer.toString(), startMark, current);
+ }
+ startMark = new Mark(ctxt, path, locator.getLineNumber(),
+ locator.getColumnNumber());
+ charBuffer = null;
+ return;
+ }
+
+ if ((current instanceof Node.JspText)
+ || (current instanceof Node.NamedAttribute)
+ || !isAllSpace) {
+
+ int line = startMark.getLineNumber();
+ int column = startMark.getColumnNumber();
+
+ CharArrayWriter ttext = new CharArrayWriter();
+ int lastCh = 0, elType = 0;
+ for (int i = 0; i < charBuffer.length(); i++) {
+
+ int ch = charBuffer.charAt(i);
+ if (ch == '\n') {
+ column = 1;
+ line++;
+ } else {
+ column++;
+ }
+ if ((lastCh == '$' || lastCh == '#') && ch == '{') {
+ elType = lastCh;
+ if (ttext.size() > 0) {
+ new Node.TemplateText(
+ ttext.toString(),
+ startMark,
+ current);
+ ttext = new CharArrayWriter();
+ //We subtract two from the column number to
+ //account for the '[$,#]{' that we've already parsed
+ startMark = new Mark(ctxt, path, line, column - 2);
+ }
+ // following "${" || "#{" to first unquoted "}"
+ i++;
+ boolean singleQ = false;
+ boolean doubleQ = false;
+ lastCh = 0;
+ for (;; i++) {
+ if (i >= charBuffer.length()) {
+ throw new SAXParseException(
+ Localizer.getMessage(
+ "jsp.error.unterminated",
+ (char) elType + "{"),
+ locator);
+
+ }
+ ch = charBuffer.charAt(i);
+ if (ch == '\n') {
+ column = 1;
+ line++;
+ } else {
+ column++;
+ }
+ if (lastCh == '\\' && (singleQ || doubleQ)) {
+ ttext.write(ch);
+ lastCh = 0;
+ continue;
+ }
+ if (ch == '}') {
+ new Node.ELExpression((char) elType,
+ ttext.toString(),
+ startMark,
+ current);
+ ttext = new CharArrayWriter();
+ startMark = new Mark(ctxt, path, line, column);
+ break;
+ }
+ if (ch == '"')
+ doubleQ = !doubleQ;
+ else if (ch == '\'')
+ singleQ = !singleQ;
+
+ ttext.write(ch);
+ lastCh = ch;
+ }
+ } else if (lastCh == '\\' && (ch == '$' || ch == '#')) {
+ if (pageInfo.isELIgnored()) {
+ ttext.write('\\');
+ }
+ ttext.write(ch);
+ ch = 0; // Not start of EL anymore
+ } else {
+ if (lastCh == '$' || lastCh == '#' || lastCh == '\\') {
+ ttext.write(lastCh);
+ }
+ if (ch != '$' && ch != '#' && ch != '\\') {
+ ttext.write(ch);
+ }
+ }
+ lastCh = ch;
+ }
+ if (lastCh == '$' || lastCh == '#' || lastCh == '\\') {
+ ttext.write(lastCh);
+ }
+ if (ttext.size() > 0) {
+ new Node.TemplateText(ttext.toString(), startMark, current);
+ }
+ }
+ startMark = new Mark(ctxt, path, locator.getLineNumber(),
+ locator.getColumnNumber());
+
+ charBuffer = null;
+ }
+
+ /*
+ * Receives notification of the end of an element.
+ */
+ public void endElement(String uri, String localName, String qName)
+ throws SAXException {
+
+ processChars();
+
+ if (directivesOnly &&
+ !(JSP_URI.equals(uri) && localName.startsWith(DIRECTIVE_ACTION))) {
+ return;
+ }
+
+ if (current instanceof Node.NamedAttribute) {
+ boolean isTrim = ((Node.NamedAttribute)current).isTrim();
+ Node.Nodes subElems = ((Node.NamedAttribute)current).getBody();
+ for (int i = 0; subElems != null && i < subElems.size(); i++) {
+ Node subElem = subElems.getNode(i);
+ if (!(subElem instanceof Node.TemplateText)) {
+ continue;
+ }
+ // Ignore any whitespace (including spaces, carriage returns,
+ // line feeds, and tabs, that appear at the beginning and at
+ // the end of the body of the <jsp:attribute> action, if the
+ // action's 'trim' attribute is set to TRUE (default).
+ // In addition, any textual nodes in the <jsp:attribute> that
+ // have only white space are dropped from the document, with
+ // the exception of leading and trailing white-space-only
+ // textual nodes in a <jsp:attribute> whose 'trim' attribute
+ // is set to FALSE, which must be kept verbatim.
+ if (i == 0) {
+ if (isTrim) {
+ ((Node.TemplateText)subElem).ltrim();
+ }
+ } else if (i == subElems.size() - 1) {
+ if (isTrim) {
+ ((Node.TemplateText)subElem).rtrim();
+ }
+ } else {
+ if (((Node.TemplateText)subElem).isAllSpace()) {
+ subElems.remove(subElem);
+ }
+ }
+ }
+ } else if (current instanceof Node.ScriptingElement) {
+ checkScriptingBody((Node.ScriptingElement)current);
+ }
+
+ if ( isTagDependent(current)) {
+ tagDependentNesting--;
+ }
+
+ if (scriptlessBodyNode != null
+ && current.equals(scriptlessBodyNode)) {
+ scriptlessBodyNode = null;
+ }
+
+ if (current.getParent() != null) {
+ current = current.getParent();
+ }
+ }
+
+ /*
+ * Receives the document locator.
+ *
+ * @param locator the document locator
+ */
+ public void setDocumentLocator(Locator locator) {
+ this.locator = locator;
+ }
+
+ /*
+ * See org.xml.sax.ext.LexicalHandler.
+ */
+ public void comment(char[] buf, int offset, int len) throws SAXException {
+
+ processChars(); // Flush char buffer and remove white spaces
+
+ // ignore comments in the DTD
+ if (!inDTD) {
+ startMark =
+ new Mark(
+ ctxt,
+ path,
+ locator.getLineNumber(),
+ locator.getColumnNumber());
+ new Node.Comment(new String(buf, offset, len), startMark, current);
+ }
+ }
+
+ /*
+ * See org.xml.sax.ext.LexicalHandler.
+ */
+ public void startCDATA() throws SAXException {
+
+ processChars(); // Flush char buffer and remove white spaces
+ startMark = new Mark(ctxt, path, locator.getLineNumber(),
+ locator.getColumnNumber());
+ }
+
+ /*
+ * See org.xml.sax.ext.LexicalHandler.
+ */
+ public void endCDATA() throws SAXException {
+ processChars(); // Flush char buffer and remove white spaces
+ }
+
+ /*
+ * See org.xml.sax.ext.LexicalHandler.
+ */
+ public void startEntity(String name) throws SAXException {
+ // do nothing
+ }
+
+ /*
+ * See org.xml.sax.ext.LexicalHandler.
+ */
+ public void endEntity(String name) throws SAXException {
+ // do nothing
+ }
+
+ /*
+ * See org.xml.sax.ext.LexicalHandler.
+ */
+ public void startDTD(String name, String publicId, String systemId)
+ throws SAXException {
+ if (!isValidating) {
+ fatalError(ENABLE_DTD_VALIDATION_EXCEPTION);
+ }
+
+ inDTD = true;
+ }
+
+ /*
+ * See org.xml.sax.ext.LexicalHandler.
+ */
+ public void endDTD() throws SAXException {
+ inDTD = false;
+ }
+
+ /*
+ * Receives notification of a non-recoverable error.
+ */
+ public void fatalError(SAXParseException e) throws SAXException {
+ throw e;
+ }
+
+ /*
+ * Receives notification of a recoverable error.
+ */
+ public void error(SAXParseException e) throws SAXException {
+ throw e;
+ }
+
+ /*
+ * Receives notification of the start of a Namespace mapping.
+ */
+ public void startPrefixMapping(String prefix, String uri)
+ throws SAXException {
+ TagLibraryInfo taglibInfo;
+
+ if (directivesOnly && !(JSP_URI.equals(uri))) {
+ return;
+ }
+
+ try {
+ taglibInfo = getTaglibInfo(prefix, uri);
+ } catch (JasperException je) {
+ throw new SAXParseException(
+ Localizer.getMessage("jsp.error.could.not.add.taglibraries"),
+ locator,
+ je);
+ }
+
+ if (taglibInfo != null) {
+ if (pageInfo.getTaglib(uri) == null) {
+ pageInfo.addTaglib(uri, taglibInfo);
+ }
+ pageInfo.pushPrefixMapping(prefix, uri);
+ } else {
+ pageInfo.pushPrefixMapping(prefix, null);
+ }
+ }
+
+ /*
+ * Receives notification of the end of a Namespace mapping.
+ */
+ public void endPrefixMapping(String prefix) throws SAXException {
+
+ if (directivesOnly) {
+ String uri = pageInfo.getURI(prefix);
+ if (!JSP_URI.equals(uri)) {
+ return;
+ }
+ }
+
+ pageInfo.popPrefixMapping(prefix);
+ }
+
+ //*********************************************************************
+ // Private utility methods
+
+ private Node parseStandardAction(
+ String qName,
+ String localName,
+ Attributes nonTaglibAttrs,
+ Attributes nonTaglibXmlnsAttrs,
+ Attributes taglibAttrs,
+ Mark start,
+ Node parent)
+ throws SAXException {
+
+ Node node = null;
+
+ if (localName.equals(ROOT_ACTION)) {
+ if (!(current instanceof Node.Root)) {
+ throw new SAXParseException(
+ Localizer.getMessage("jsp.error.nested_jsproot"),
+ locator);
+ }
+ node =
+ new Node.JspRoot(
+ qName,
+ nonTaglibAttrs,
+ nonTaglibXmlnsAttrs,
+ taglibAttrs,
+ start,
+ current);
+ if (isTop) {
+ pageInfo.setHasJspRoot(true);
+ }
+ } else if (localName.equals(PAGE_DIRECTIVE_ACTION)) {
+ if (isTagFile) {
+ throw new SAXParseException(
+ Localizer.getMessage(
+ "jsp.error.action.istagfile",
+ localName),
+ locator);
+ }
+ node =
+ new Node.PageDirective(
+ qName,
+ nonTaglibAttrs,
+ nonTaglibXmlnsAttrs,
+ taglibAttrs,
+ start,
+ current);
+ String imports = nonTaglibAttrs.getValue("import");
+ // There can only be one 'import' attribute per page directive
+ if (imports != null) {
+ ((Node.PageDirective)node).addImport(imports);
+ }
+ } else if (localName.equals(INCLUDE_DIRECTIVE_ACTION)) {
+ node =
+ new Node.IncludeDirective(
+ qName,
+ nonTaglibAttrs,
+ nonTaglibXmlnsAttrs,
+ taglibAttrs,
+ start,
+ current);
+ processIncludeDirective(nonTaglibAttrs.getValue("file"), node);
+ } else if (localName.equals(DECLARATION_ACTION)) {
+ if (scriptlessBodyNode != null) {
+ // We're nested inside a node whose body is
+ // declared to be scriptless
+ throw new SAXParseException(
+ Localizer.getMessage(
+ "jsp.error.no.scriptlets",
+ localName),
+ locator);
+ }
+ node =
+ new Node.Declaration(
+ qName,
+ nonTaglibXmlnsAttrs,
+ taglibAttrs,
+ start,
+ current);
+ } else if (localName.equals(SCRIPTLET_ACTION)) {
+ if (scriptlessBodyNode != null) {
+ // We're nested inside a node whose body is
+ // declared to be scriptless
+ throw new SAXParseException(
+ Localizer.getMessage(
+ "jsp.error.no.scriptlets",
+ localName),
+ locator);
+ }
+ node =
+ new Node.Scriptlet(
+ qName,
+ nonTaglibXmlnsAttrs,
+ taglibAttrs,
+ start,
+ current);
+ } else if (localName.equals(EXPRESSION_ACTION)) {
+ if (scriptlessBodyNode != null) {
+ // We're nested inside a node whose body is
+ // declared to be scriptless
+ throw new SAXParseException(
+ Localizer.getMessage(
+ "jsp.error.no.scriptlets",
+ localName),
+ locator);
+ }
+ node =
+ new Node.Expression(
+ qName,
+ nonTaglibXmlnsAttrs,
+ taglibAttrs,
+ start,
+ current);
+ } else if (localName.equals(USE_BEAN_ACTION)) {
+ node =
+ new Node.UseBean(
+ qName,
+ nonTaglibAttrs,
+ nonTaglibXmlnsAttrs,
+ taglibAttrs,
+ start,
+ current);
+ } else if (localName.equals(SET_PROPERTY_ACTION)) {
+ node =
+ new Node.SetProperty(
+ qName,
+ nonTaglibAttrs,
+ nonTaglibXmlnsAttrs,
+ taglibAttrs,
+ start,
+ current);
+ } else if (localName.equals(GET_PROPERTY_ACTION)) {
+ node =
+ new Node.GetProperty(
+ qName,
+ nonTaglibAttrs,
+ nonTaglibXmlnsAttrs,
+ taglibAttrs,
+ start,
+ current);
+ } else if (localName.equals(INCLUDE_ACTION)) {
+ node =
+ new Node.IncludeAction(
+ qName,
+ nonTaglibAttrs,
+ nonTaglibXmlnsAttrs,
+ taglibAttrs,
+ start,
+ current);
+ } else if (localName.equals(FORWARD_ACTION)) {
+ node =
+ new Node.ForwardAction(
+ qName,
+ nonTaglibAttrs,
+ nonTaglibXmlnsAttrs,
+ taglibAttrs,
+ start,
+ current);
+ } else if (localName.equals(PARAM_ACTION)) {
+ node =
+ new Node.ParamAction(
+ qName,
+ nonTaglibAttrs,
+ nonTaglibXmlnsAttrs,
+ taglibAttrs,
+ start,
+ current);
+ } else if (localName.equals(PARAMS_ACTION)) {
+ node =
+ new Node.ParamsAction(
+ qName,
+ nonTaglibXmlnsAttrs,
+ taglibAttrs,
+ start,
+ current);
+ } else if (localName.equals(PLUGIN_ACTION)) {
+ node =
+ new Node.PlugIn(
+ qName,
+ nonTaglibAttrs,
+ nonTaglibXmlnsAttrs,
+ taglibAttrs,
+ start,
+ current);
+ } else if (localName.equals(TEXT_ACTION)) {
+ node =
+ new Node.JspText(
+ qName,
+ nonTaglibXmlnsAttrs,
+ taglibAttrs,
+ start,
+ current);
+ } else if (localName.equals(BODY_ACTION)) {
+ node =
+ new Node.JspBody(
+ qName,
+ nonTaglibXmlnsAttrs,
+ taglibAttrs,
+ start,
+ current);
+ } else if (localName.equals(ATTRIBUTE_ACTION)) {
+ node =
+ new Node.NamedAttribute(
+ qName,
+ nonTaglibAttrs,
+ nonTaglibXmlnsAttrs,
+ taglibAttrs,
+ start,
+ current);
+ } else if (localName.equals(OUTPUT_ACTION)) {
+ node =
+ new Node.JspOutput(
+ qName,
+ nonTaglibAttrs,
+ nonTaglibXmlnsAttrs,
+ taglibAttrs,
+ start,
+ current);
+ } else if (localName.equals(TAG_DIRECTIVE_ACTION)) {
+ if (!isTagFile) {
+ throw new SAXParseException(
+ Localizer.getMessage(
+ "jsp.error.action.isnottagfile",
+ localName),
+ locator);
+ }
+ node =
+ new Node.TagDirective(
+ qName,
+ nonTaglibAttrs,
+ nonTaglibXmlnsAttrs,
+ taglibAttrs,
+ start,
+ current);
+ String imports = nonTaglibAttrs.getValue("import");
+ // There can only be one 'import' attribute per tag directive
+ if (imports != null) {
+ ((Node.TagDirective)node).addImport(imports);
+ }
+ } else if (localName.equals(ATTRIBUTE_DIRECTIVE_ACTION)) {
+ if (!isTagFile) {
+ throw new SAXParseException(
+ Localizer.getMessage(
+ "jsp.error.action.isnottagfile",
+ localName),
+ locator);
+ }
+ node =
+ new Node.AttributeDirective(
+ qName,
+ nonTaglibAttrs,
+ nonTaglibXmlnsAttrs,
+ taglibAttrs,
+ start,
+ current);
+ } else if (localName.equals(VARIABLE_DIRECTIVE_ACTION)) {
+ if (!isTagFile) {
+ throw new SAXParseException(
+ Localizer.getMessage(
+ "jsp.error.action.isnottagfile",
+ localName),
+ locator);
+ }
+ node =
+ new Node.VariableDirective(
+ qName,
+ nonTaglibAttrs,
+ nonTaglibXmlnsAttrs,
+ taglibAttrs,
+ start,
+ current);
+ } else if (localName.equals(INVOKE_ACTION)) {
+ if (!isTagFile) {
+ throw new SAXParseException(
+ Localizer.getMessage(
+ "jsp.error.action.isnottagfile",
+ localName),
+ locator);
+ }
+ node =
+ new Node.InvokeAction(
+ qName,
+ nonTaglibAttrs,
+ nonTaglibXmlnsAttrs,
+ taglibAttrs,
+ start,
+ current);
+ } else if (localName.equals(DOBODY_ACTION)) {
+ if (!isTagFile) {
+ throw new SAXParseException(
+ Localizer.getMessage(
+ "jsp.error.action.isnottagfile",
+ localName),
+ locator);
+ }
+ node =
+ new Node.DoBodyAction(
+ qName,
+ nonTaglibAttrs,
+ nonTaglibXmlnsAttrs,
+ taglibAttrs,
+ start,
+ current);
+ } else if (localName.equals(ELEMENT_ACTION)) {
+ node =
+ new Node.JspElement(
+ qName,
+ nonTaglibAttrs,
+ nonTaglibXmlnsAttrs,
+ taglibAttrs,
+ start,
+ current);
+ } else if (localName.equals(FALLBACK_ACTION)) {
+ node =
+ new Node.FallBackAction(
+ qName,
+ nonTaglibXmlnsAttrs,
+ taglibAttrs,
+ start,
+ current);
+ } else {
+ throw new SAXParseException(
+ Localizer.getMessage(
+ "jsp.error.xml.badStandardAction",
+ localName),
+ locator);
+ }
+
+ return node;
+ }
+
+ /*
+ * Checks if the XML element with the given tag name is a custom action,
+ * and returns the corresponding Node object.
+ */
+ private Node parseCustomAction(
+ String qName,
+ String localName,
+ String uri,
+ Attributes nonTaglibAttrs,
+ Attributes nonTaglibXmlnsAttrs,
+ Attributes taglibAttrs,
+ Mark start,
+ Node parent)
+ throws SAXException {
+
+ // Check if this is a user-defined (custom) tag
+ TagLibraryInfo tagLibInfo = pageInfo.getTaglib(uri);
+ if (tagLibInfo == null) {
+ return null;
+ }
+
+ TagInfo tagInfo = tagLibInfo.getTag(localName);
+ TagFileInfo tagFileInfo = tagLibInfo.getTagFile(localName);
+ if (tagInfo == null && tagFileInfo == null) {
+ throw new SAXException(
+ Localizer.getMessage("jsp.error.xml.bad_tag", localName, uri));
+ }
+ Class tagHandlerClass = null;
+ if (tagInfo != null) {
+ String handlerClassName = tagInfo.getTagClassName();
+ try {
+ tagHandlerClass =
+ ctxt.getClassLoader().loadClass(handlerClassName);
+ } catch (Exception e) {
+ throw new SAXException(
+ Localizer.getMessage("jsp.error.loadclass.taghandler",
+ handlerClassName,
+ qName),
+ e);
+ }
+ }
+
+ String prefix = getPrefix(qName);
+
+ Node.CustomTag ret = null;
+ if (tagInfo != null) {
+ ret =
+ new Node.CustomTag(
+ qName,
+ prefix,
+ localName,
+ uri,
+ nonTaglibAttrs,
+ nonTaglibXmlnsAttrs,
+ taglibAttrs,
+ start,
+ parent,
+ tagInfo,
+ tagHandlerClass);
+ } else {
+ ret =
+ new Node.CustomTag(
+ qName,
+ prefix,
+ localName,
+ uri,
+ nonTaglibAttrs,
+ nonTaglibXmlnsAttrs,
+ taglibAttrs,
+ start,
+ parent,
+ tagFileInfo);
+ }
+
+ return ret;
+ }
+
+ /*
+ * Creates the tag library associated with the given uri namespace, and
+ * returns it.
+ *
+ * @param prefix The prefix of the xmlns attribute
+ * @param uri The uri namespace (value of the xmlns attribute)
+ *
+ * @return The tag library associated with the given uri namespace
+ */
+ private TagLibraryInfo getTaglibInfo(String prefix, String uri)
+ throws JasperException {
+
+ TagLibraryInfo result = null;
+
+ if (uri.startsWith(URN_JSPTAGDIR)) {
+ // uri (of the form "urn:jsptagdir:path") references tag file dir
+ String tagdir = uri.substring(URN_JSPTAGDIR.length());
+ result =
+ new ImplicitTagLibraryInfo(
+ ctxt,
+ parserController,
+ pageInfo,
+ prefix,
+ tagdir,
+ err);
+ } else {
+ // uri references TLD file
+ boolean isPlainUri = false;
+ if (uri.startsWith(URN_JSPTLD)) {
+ // uri is of the form "urn:jsptld:path"
+ uri = uri.substring(URN_JSPTLD.length());
+ } else {
+ isPlainUri = true;
+ }
+
+ String[] location = ctxt.getTldLocation(uri);
+ if (location != null || !isPlainUri) {
+ if (ctxt.getOptions().isCaching()) {
+ result = (TagLibraryInfoImpl) ctxt.getOptions().getCache().get(uri);
+ }
+ if (result == null) {
+ /*
+ * If the uri value is a plain uri, a translation error must
+ * not be generated if the uri is not found in the taglib map.
+ * Instead, any actions in the namespace defined by the uri
+ * value must be treated as uninterpreted.
+ */
+ result =
+ new TagLibraryInfoImpl(
+ ctxt,
+ parserController,
+ pageInfo,
+ prefix,
+ uri,
+ location,
+ err);
+ if (ctxt.getOptions().isCaching()) {
+ ctxt.getOptions().getCache().put(uri, result);
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /*
+ * Ensures that the given body only contains nodes that are instances of
+ * TemplateText.
+ *
+ * This check is performed only for the body of a scripting (that is:
+ * declaration, scriptlet, or expression) element, after the end tag of a
+ * scripting element has been reached.
+ */
+ private void checkScriptingBody(Node.ScriptingElement scriptingElem)
+ throws SAXException {
+ Node.Nodes body = scriptingElem.getBody();
+ if (body != null) {
+ int size = body.size();
+ for (int i = 0; i < size; i++) {
+ Node n = body.getNode(i);
+ if (!(n instanceof Node.TemplateText)) {
+ String elemType = SCRIPTLET_ACTION;
+ if (scriptingElem instanceof Node.Declaration)
+ elemType = DECLARATION_ACTION;
+ if (scriptingElem instanceof Node.Expression)
+ elemType = EXPRESSION_ACTION;
+ String msg =
+ Localizer.getMessage(
+ "jsp.error.parse.xml.scripting.invalid.body",
+ elemType);
+ throw new SAXException(msg);
+ }
+ }
+ }
+ }
+
+ /*
+ * Parses the given file included via an include directive.
+ *
+ * @param fname The path to the included resource, as specified by the
+ * 'file' attribute of the include directive
+ * @param parent The Node representing the include directive
+ */
+ private void processIncludeDirective(String fname, Node parent)
+ throws SAXException {
+
+ if (fname == null) {
+ return;
+ }
+
+ try {
+ parserController.parse(fname, parent, null);
+ } catch (FileNotFoundException fnfe) {
+ throw new SAXParseException(
+ Localizer.getMessage("jsp.error.file.not.found", fname),
+ locator,
+ fnfe);
+ } catch (Exception e) {
+ throw new SAXException(e);
+ }
+ }
+
+ /*
+ * Checks an element's given URI, qname, and attributes to see if any
+ * of them hijack the 'jsp' prefix, that is, bind it to a namespace other
+ * than http://java.sun.com/JSP/Page.
+ *
+ * @param uri The element's URI
+ * @param qName The element's qname
+ * @param attrs The element's attributes
+ */
+ private void checkPrefixes(String uri, String qName, Attributes attrs) {
+
+ checkPrefix(uri, qName);
+
+ int len = attrs.getLength();
+ for (int i = 0; i < len; i++) {
+ checkPrefix(attrs.getURI(i), attrs.getQName(i));
+ }
+ }
+
+ /*
+ * Checks the given URI and qname to see if they hijack the 'jsp' prefix,
+ * which would be the case if qName contained the 'jsp' prefix and
+ * uri was different from http://java.sun.com/JSP/Page.
+ *
+ * @param uri The URI to check
+ * @param qName The qname to check
+ */
+ private void checkPrefix(String uri, String qName) {
+
+ String prefix = getPrefix(qName);
+ if (prefix.length() > 0) {
+ pageInfo.addPrefix(prefix);
+ if ("jsp".equals(prefix) && !JSP_URI.equals(uri)) {
+ pageInfo.setIsJspPrefixHijacked(true);
+ }
+ }
+ }
+
+ private String getPrefix(String qName) {
+ int index = qName.indexOf(':');
+ if (index != -1) {
+ return qName.substring(0, index);
+ }
+ return "";
+ }
+
+ /*
+ * Gets SAXParser.
+ *
+ * @param validating Indicates whether the requested SAXParser should
+ * be validating
+ * @param jspDocParser The JSP document parser
+ *
+ * @return The SAXParser
+ */
+ private static SAXParser getSAXParser(
+ boolean validating,
+ JspDocumentParser jspDocParser)
+ throws Exception {
+
+ SAXParserFactory factory = SAXParserFactory.newInstance();
+ factory.setNamespaceAware(true);
+
+ // Preserve xmlns attributes
+ factory.setFeature(
+ "http://xml.org/sax/features/namespace-prefixes",
+ true);
+ factory.setValidating(validating);
+ //factory.setFeature(
+ // "http://xml.org/sax/features/validation",
+ // validating);
+
+ // Configure the parser
+ SAXParser saxParser = factory.newSAXParser();
+ XMLReader xmlReader = saxParser.getXMLReader();
+ xmlReader.setProperty(LEXICAL_HANDLER_PROPERTY, jspDocParser);
+ xmlReader.setErrorHandler(jspDocParser);
+
+ return saxParser;
+ }
+
+ /*
+ * Exception indicating that a DOCTYPE declaration is present, but
+ * validation is turned off.
+ */
+ private static class EnableDTDValidationException
+ extends SAXParseException {
+
+ EnableDTDValidationException(String message, Locator loc) {
+ super(message, loc);
+ }
+ }
+
+ private static String getBodyType(Node.CustomTag custom) {
+
+ if (custom.getTagInfo() != null) {
+ return custom.getTagInfo().getBodyContent();
+ }
+
+ return custom.getTagFileInfo().getTagInfo().getBodyContent();
+ }
+
+ private boolean isTagDependent(Node n) {
+
+ if (n instanceof Node.CustomTag) {
+ String bodyType = getBodyType((Node.CustomTag) n);
+ return
+ TagInfo.BODY_CONTENT_TAG_DEPENDENT.equalsIgnoreCase(bodyType);
+ }
+ return false;
+ }
+}
Added: struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/JspReader.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/JspReader.java?rev=819435&view=auto
==============================================================================
--- struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/JspReader.java (added)
+++ struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/JspReader.java Mon Sep 28 00:43:34 2009
@@ -0,0 +1,657 @@
+/*
+ * 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.jasper.compiler;
+
+import java.io.CharArrayWriter;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.List;
+import java.util.Vector;
+import java.util.jar.JarFile;
+import java.net.URL;
+import java.net.MalformedURLException;
+
+import org.apache.jasper.JasperException;
+import org.apache.jasper.JspCompilationContext;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+
+/**
+ * JspReader is an input buffer for the JSP parser. It should allow
+ * unlimited lookahead and pushback. It also has a bunch of parsing
+ * utility methods for understanding htmlesque thingies.
+ *
+ * @author Anil K. Vijendran
+ * @author Anselm Baird-Smith
+ * @author Harish Prabandham
+ * @author Rajiv Mordani
+ * @author Mandar Raje
+ * @author Danno Ferrin
+ * @author Kin-man Chung
+ * @author Shawn Bayern
+ * @author Mark Roth
+ */
+
+class JspReader {
+
+ /**
+ * Logger.
+ */
+ private Log log = LogFactory.getLog(JspReader.class);
+
+ /**
+ * The current spot in the file.
+ */
+ private Mark current;
+
+ /**
+ * What is this?
+ */
+ private String master;
+
+ /**
+ * The list of source files.
+ */
+ private List sourceFiles;
+
+ /**
+ * The current file ID (-1 indicates an error or no file).
+ */
+ private int currFileId;
+
+ /**
+ * Seems redundant.
+ */
+ private int size;
+
+ /**
+ * The compilation context.
+ */
+ private JspCompilationContext context;
+
+ /**
+ * The Jasper error dispatcher.
+ */
+ private ErrorDispatcher err;
+
+ /**
+ * Set to true when using the JspReader on a single file where we read up
+ * to the end and reset to the beginning many times.
+ * (as in ParserController.figureOutJspDocument()).
+ */
+ private boolean singleFile;
+
+ /**
+ * Constructor.
+ *
+ * @param ctxt The compilation context
+ * @param fname The file name
+ * @param encoding The file encoding
+ * @param jarFile ?
+ * @param err The error dispatcher
+ * @throws JasperException If a Jasper-internal error occurs
+ * @throws FileNotFoundException If the JSP file is not found (or is unreadable)
+ * @throws IOException If an IO-level error occurs, e.g. reading the file
+ */
+ public JspReader(JspCompilationContext ctxt,
+ String fname,
+ String encoding,
+ JarFile jarFile,
+ ErrorDispatcher err)
+ throws JasperException, FileNotFoundException, IOException {
+
+ this(ctxt, fname, encoding,
+ JspUtil.getReader(fname, encoding, jarFile, ctxt, err),
+ err);
+ }
+
+ /**
+ * Constructor: same as above constructor but with initialized reader
+ * to the file given.
+ */
+ public JspReader(JspCompilationContext ctxt,
+ String fname,
+ String encoding,
+ InputStreamReader reader,
+ ErrorDispatcher err)
+ throws JasperException, FileNotFoundException {
+
+ this.context = ctxt;
+ this.err = err;
+ sourceFiles = new Vector();
+ currFileId = 0;
+ size = 0;
+ singleFile = false;
+ pushFile(fname, encoding, reader);
+ }
+
+ /**
+ * @return JSP compilation context with which this JspReader is
+ * associated
+ */
+ JspCompilationContext getJspCompilationContext() {
+ return context;
+ }
+
+ /**
+ * Returns the file at the given position in the list.
+ *
+ * @param fileid The file position in the list
+ * @return The file at that position, if found, null otherwise
+ */
+ String getFile(final int fileid) {
+ return (String) sourceFiles.get(fileid);
+ }
+
+ /**
+ * Checks if the current file has more input.
+ *
+ * @return True if more reading is possible
+ * @throws JasperException if an error occurs
+ */
+ boolean hasMoreInput() throws JasperException {
+ if (current.cursor >= current.stream.length) {
+ if (singleFile) return false;
+ while (popFile()) {
+ if (current.cursor < current.stream.length) return true;
+ }
+ return false;
+ }
+ return true;
+ }
+
+ int nextChar() throws JasperException {
+ if (!hasMoreInput())
+ return -1;
+
+ int ch = current.stream[current.cursor];
+
+ current.cursor++;
+
+ if (ch == '\n') {
+ current.line++;
+ current.col = 0;
+ } else {
+ current.col++;
+ }
+ return ch;
+ }
+
+ /**
+ * Back up the current cursor by one char, assumes current.cursor > 0,
+ * and that the char to be pushed back is not '\n'.
+ */
+ void pushChar() {
+ current.cursor--;
+ current.col--;
+ }
+
+ String getText(Mark start, Mark stop) throws JasperException {
+ Mark oldstart = mark();
+ reset(start);
+ CharArrayWriter caw = new CharArrayWriter();
+ while (!stop.equals(mark()))
+ caw.write(nextChar());
+ caw.close();
+ reset(oldstart);
+ return caw.toString();
+ }
+
+ int peekChar() throws JasperException {
+ if (!hasMoreInput())
+ return -1;
+ return current.stream[current.cursor];
+ }
+
+ Mark mark() {
+ return new Mark(current);
+ }
+
+ void reset(Mark mark) {
+ current = new Mark(mark);
+ }
+
+ boolean matchesIgnoreCase(String string) throws JasperException {
+ Mark mark = mark();
+ int ch = 0;
+ int i = 0;
+ do {
+ ch = nextChar();
+ if (Character.toLowerCase((char) ch) != string.charAt(i++)) {
+ reset(mark);
+ return false;
+ }
+ } while (i < string.length());
+ reset(mark);
+ return true;
+ }
+
+ /**
+ * search the stream for a match to a string
+ * @param string The string to match
+ * @return <strong>true</strong> is one is found, the current position
+ * in stream is positioned after the search string, <strong>
+ * false</strong> otherwise, position in stream unchanged.
+ */
+ boolean matches(String string) throws JasperException {
+ Mark mark = mark();
+ int ch = 0;
+ int i = 0;
+ do {
+ ch = nextChar();
+ if (((char) ch) != string.charAt(i++)) {
+ reset(mark);
+ return false;
+ }
+ } while (i < string.length());
+ return true;
+ }
+
+ boolean matchesETag(String tagName) throws JasperException {
+ Mark mark = mark();
+
+ if (!matches("</" + tagName))
+ return false;
+ skipSpaces();
+ if (nextChar() == '>')
+ return true;
+
+ reset(mark);
+ return false;
+ }
+
+ boolean matchesETagWithoutLessThan(String tagName)
+ throws JasperException
+ {
+ Mark mark = mark();
+
+ if (!matches("/" + tagName))
+ return false;
+ skipSpaces();
+ if (nextChar() == '>')
+ return true;
+
+ reset(mark);
+ return false;
+ }
+
+
+ /**
+ * Looks ahead to see if there are optional spaces followed by
+ * the given String. If so, true is returned and those spaces and
+ * characters are skipped. If not, false is returned and the
+ * position is restored to where we were before.
+ */
+ boolean matchesOptionalSpacesFollowedBy( String s )
+ throws JasperException
+ {
+ Mark mark = mark();
+
+ skipSpaces();
+ boolean result = matches( s );
+ if( !result ) {
+ reset( mark );
+ }
+
+ return result;
+ }
+
+ int skipSpaces() throws JasperException {
+ int i = 0;
+ while (hasMoreInput() && isSpace()) {
+ i++;
+ nextChar();
+ }
+ return i;
+ }
+
+ /**
+ * Skip until the given string is matched in the stream.
+ * When returned, the context is positioned past the end of the match.
+ *
+ * @param s The String to match.
+ * @return A non-null <code>Mark</code> instance (positioned immediately
+ * before the search string) if found, <strong>null</strong>
+ * otherwise.
+ */
+ Mark skipUntil(String limit) throws JasperException {
+ Mark ret = null;
+ int limlen = limit.length();
+ int ch;
+
+ skip:
+ for (ret = mark(), ch = nextChar() ; ch != -1 ;
+ ret = mark(), ch = nextChar()) {
+ if (ch == limit.charAt(0)) {
+ Mark restart = mark();
+ for (int i = 1 ; i < limlen ; i++) {
+ if (peekChar() == limit.charAt(i))
+ nextChar();
+ else {
+ reset(restart);
+ continue skip;
+ }
+ }
+ return ret;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Skip until the given string is matched in the stream, but ignoring
+ * chars initially escaped by a '\'.
+ * When returned, the context is positioned past the end of the match.
+ *
+ * @param s The String to match.
+ * @return A non-null <code>Mark</code> instance (positioned immediately
+ * before the search string) if found, <strong>null</strong>
+ * otherwise.
+ */
+ Mark skipUntilIgnoreEsc(String limit) throws JasperException {
+ Mark ret = null;
+ int limlen = limit.length();
+ int ch;
+ int prev = 'x'; // Doesn't matter
+
+ skip:
+ for (ret = mark(), ch = nextChar() ; ch != -1 ;
+ ret = mark(), prev = ch, ch = nextChar()) {
+ if (ch == '\\' && prev == '\\') {
+ ch = 0; // Double \ is not an escape char anymore
+ }
+ else if (ch == limit.charAt(0) && prev != '\\') {
+ for (int i = 1 ; i < limlen ; i++) {
+ if (peekChar() == limit.charAt(i))
+ nextChar();
+ else
+ continue skip;
+ }
+ return ret;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Skip until the given end tag is matched in the stream.
+ * When returned, the context is positioned past the end of the tag.
+ *
+ * @param tag The name of the tag whose ETag (</tag>) to match.
+ * @return A non-null <code>Mark</code> instance (positioned immediately
+ * before the ETag) if found, <strong>null</strong> otherwise.
+ */
+ Mark skipUntilETag(String tag) throws JasperException {
+ Mark ret = skipUntil("</" + tag);
+ if (ret != null) {
+ skipSpaces();
+ if (nextChar() != '>')
+ ret = null;
+ }
+ return ret;
+ }
+
+ final boolean isSpace() throws JasperException {
+ // Note: If this logic changes, also update Node.TemplateText.rtrim()
+ return peekChar() <= ' ';
+ }
+
+ /**
+ * Parse a space delimited token.
+ * If quoted the token will consume all characters up to a matching quote,
+ * otherwise, it consumes up to the first delimiter character.
+ *
+ * @param quoted If <strong>true</strong> accept quoted strings.
+ */
+ String parseToken(boolean quoted) throws JasperException {
+ StringBuffer stringBuffer = new StringBuffer();
+ skipSpaces();
+ stringBuffer.setLength(0);
+
+ if (!hasMoreInput()) {
+ return "";
+ }
+
+ int ch = peekChar();
+
+ if (quoted) {
+ if (ch == '"' || ch == '\'') {
+
+ char endQuote = ch == '"' ? '"' : '\'';
+ // Consume the open quote:
+ ch = nextChar();
+ for (ch = nextChar(); ch != -1 && ch != endQuote;
+ ch = nextChar()) {
+ if (ch == '\\')
+ ch = nextChar();
+ stringBuffer.append((char) ch);
+ }
+ // Check end of quote, skip closing quote:
+ if (ch == -1) {
+ err.jspError(mark(), "jsp.error.quotes.unterminated");
+ }
+ } else {
+ err.jspError(mark(), "jsp.error.attr.quoted");
+ }
+ } else {
+ if (!isDelimiter()) {
+ // Read value until delimiter is found:
+ do {
+ ch = nextChar();
+ // Take care of the quoting here.
+ if (ch == '\\') {
+ if (peekChar() == '"' || peekChar() == '\'' ||
+ peekChar() == '>' || peekChar() == '%')
+ ch = nextChar();
+ }
+ stringBuffer.append((char) ch);
+ } while (!isDelimiter());
+ }
+ }
+
+ return stringBuffer.toString();
+ }
+
+ void setSingleFile(boolean val) {
+ singleFile = val;
+ }
+
+
+ /**
+ * Gets the URL for the given path name.
+ *
+ * @param path Path name
+ *
+ * @return URL for the given path name.
+ *
+ * @exception MalformedURLException if the path name is not given in
+ * the correct form
+ */
+ URL getResource(String path) throws MalformedURLException {
+ return context.getResource(path);
+ }
+
+
+ /**
+ * Parse utils - Is current character a token delimiter ?
+ * Delimiters are currently defined to be =, >, <, ", and ' or any
+ * any space character as defined by <code>isSpace</code>.
+ *
+ * @return A boolean.
+ */
+ private boolean isDelimiter() throws JasperException {
+ if (! isSpace()) {
+ int ch = peekChar();
+ // Look for a single-char work delimiter:
+ if (ch == '=' || ch == '>' || ch == '"' || ch == '\''
+ || ch == '/') {
+ return true;
+ }
+ // Look for an end-of-comment or end-of-tag:
+ if (ch == '-') {
+ Mark mark = mark();
+ if (((ch = nextChar()) == '>')
+ || ((ch == '-') && (nextChar() == '>'))) {
+ reset(mark);
+ return true;
+ } else {
+ reset(mark);
+ return false;
+ }
+ }
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * Register a new source file.
+ * This method is used to implement file inclusion. Each included file
+ * gets a unique identifier (which is the index in the array of source
+ * files).
+ *
+ * @return The index of the now registered file.
+ */
+ private int registerSourceFile(final String file) {
+ if (sourceFiles.contains(file)) {
+ return -1;
+ }
+
+ sourceFiles.add(file);
+ this.size++;
+
+ return sourceFiles.size() - 1;
+ }
+
+
+ /**
+ * Unregister the source file.
+ * This method is used to implement file inclusion. Each included file
+ * gets a uniq identifier (which is the index in the array of source
+ * files).
+ *
+ * @return The index of the now registered file.
+ */
+ private int unregisterSourceFile(final String file) {
+ if (!sourceFiles.contains(file)) {
+ return -1;
+ }
+
+ sourceFiles.remove(file);
+ this.size--;
+ return sourceFiles.size() - 1;
+ }
+
+ /**
+ * Push a file (and its associated Stream) on the file stack. THe
+ * current position in the current file is remembered.
+ */
+ private void pushFile(String file, String encoding,
+ InputStreamReader reader)
+ throws JasperException, FileNotFoundException {
+
+ // Register the file
+ String longName = file;
+
+ int fileid = registerSourceFile(longName);
+
+ if (fileid == -1) {
+ // Bugzilla 37407: http://issues.apache.org/bugzilla/show_bug.cgi?id=37407
+ if(reader != null) {
+ try {
+ reader.close();
+ } catch (Exception any) {
+ if(log.isDebugEnabled()) {
+ log.debug("Exception closing reader: ", any);
+ }
+ }
+ }
+
+ err.jspError("jsp.error.file.already.registered", file);
+ }
+
+ currFileId = fileid;
+
+ try {
+ CharArrayWriter caw = new CharArrayWriter();
+ char buf[] = new char[1024];
+ for (int i = 0 ; (i = reader.read(buf)) != -1 ;)
+ caw.write(buf, 0, i);
+ caw.close();
+ if (current == null) {
+ current = new Mark(this, caw.toCharArray(), fileid,
+ getFile(fileid), master, encoding);
+ } else {
+ current.pushStream(caw.toCharArray(), fileid, getFile(fileid),
+ longName, encoding);
+ }
+ } catch (Throwable ex) {
+ log.error("Exception parsing file ", ex);
+ // Pop state being constructed:
+ popFile();
+ err.jspError("jsp.error.file.cannot.read", file);
+ } finally {
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (Exception any) {
+ if(log.isDebugEnabled()) {
+ log.debug("Exception closing reader: ", any);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Pop a file from the file stack. The field "current" is retored
+ * to the value to point to the previous files, if any, and is set
+ * to null otherwise.
+ * @return true is there is a previous file on the stack.
+ * false otherwise.
+ */
+ private boolean popFile() throws JasperException {
+
+ // Is stack created ? (will happen if the Jsp file we're looking at is
+ // missing.
+ if (current == null || currFileId < 0) {
+ return false;
+ }
+
+ // Restore parser state:
+ String fName = getFile(currFileId);
+ currFileId = unregisterSourceFile(fName);
+ if (currFileId < -1) {
+ err.jspError("jsp.error.file.not.registered", fName);
+ }
+
+ Mark previous = current.popStream();
+ if (previous != null) {
+ master = current.baseDir;
+ current = previous;
+ return true;
+ }
+ // Note that although the current file is undefined here, "current"
+ // is not set to null just for convience, for it maybe used to
+ // set the current (undefined) position.
+ return false;
+ }
+}
+
Added: struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/JspRuntimeContext.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/JspRuntimeContext.java?rev=819435&view=auto
==============================================================================
--- struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/JspRuntimeContext.java (added)
+++ struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/JspRuntimeContext.java Mon Sep 28 00:43:34 2009
@@ -0,0 +1,446 @@
+/*
+ * 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.jasper.compiler;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FilePermission;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.security.CodeSource;
+import java.security.PermissionCollection;
+import java.security.Policy;
+import java.security.cert.Certificate;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.servlet.ServletContext;
+import javax.servlet.jsp.JspFactory;
+
+import org.apache.jasper.Constants;
+import org.apache.jasper.JspCompilationContext;
+import org.apache.jasper.Options;
+import org.apache.jasper.runtime.JspFactoryImpl;
+import org.apache.jasper.security.SecurityClassLoad;
+import org.apache.jasper.servlet.JspServletWrapper;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+
+/**
+ * Class for tracking JSP compile time file dependencies when the
+ * &060;%@include file="..."%&062; directive is used.
+ *
+ * A background thread periodically checks the files a JSP page
+ * is dependent upon. If a dpendent file changes the JSP page
+ * which included it is recompiled.
+ *
+ * Only used if a web application context is a directory.
+ *
+ * @author Glenn L. Nielsen
+ * @version $Revision: 505593 $
+ */
+public final class JspRuntimeContext {
+
+ // Logger
+ private Log log = LogFactory.getLog(JspRuntimeContext.class);
+
+ /*
+ * Counts how many times the webapp's JSPs have been reloaded.
+ */
+ private int jspReloadCount;
+
+ /**
+ * Preload classes required at runtime by a JSP servlet so that
+ * we don't get a defineClassInPackage security exception.
+ */
+ static {
+ JspFactoryImpl factory = new JspFactoryImpl();
+ SecurityClassLoad.securityClassLoad(factory.getClass().getClassLoader());
+ if( System.getSecurityManager() != null ) {
+ String basePackage = "org.apache.jasper.";
+ try {
+ factory.getClass().getClassLoader().loadClass( basePackage +
+ "runtime.JspFactoryImpl$PrivilegedGetPageContext");
+ factory.getClass().getClassLoader().loadClass( basePackage +
+ "runtime.JspFactoryImpl$PrivilegedReleasePageContext");
+ factory.getClass().getClassLoader().loadClass( basePackage +
+ "runtime.JspRuntimeLibrary");
+ factory.getClass().getClassLoader().loadClass( basePackage +
+ "runtime.JspRuntimeLibrary$PrivilegedIntrospectHelper");
+ factory.getClass().getClassLoader().loadClass( basePackage +
+ "runtime.ServletResponseWrapperInclude");
+ factory.getClass().getClassLoader().loadClass( basePackage +
+ "servlet.JspServletWrapper");
+ } catch (ClassNotFoundException ex) {
+ throw new IllegalStateException(ex);
+ }
+ }
+
+ JspFactory.setDefaultFactory(factory);
+ }
+
+ // ----------------------------------------------------------- Constructors
+
+ /**
+ * Create a JspRuntimeContext for a web application context.
+ *
+ * Loads in any previously generated dependencies from file.
+ *
+ * @param context ServletContext for web application
+ */
+ public JspRuntimeContext(ServletContext context, Options options) {
+
+ this.context = context;
+ this.options = options;
+
+ // Get the parent class loader
+ parentClassLoader =
+ (URLClassLoader) Thread.currentThread().getContextClassLoader();
+ if (parentClassLoader == null) {
+ parentClassLoader =
+ (URLClassLoader)this.getClass().getClassLoader();
+ }
+
+ if (log.isDebugEnabled()) {
+ if (parentClassLoader != null) {
+ log.debug(Localizer.getMessage("jsp.message.parent_class_loader_is",
+ parentClassLoader.toString()));
+ } else {
+ log.debug(Localizer.getMessage("jsp.message.parent_class_loader_is",
+ "<none>"));
+ }
+ }
+
+ initClassPath();
+
+ if (context instanceof org.apache.jasper.servlet.JspCServletContext) {
+ return;
+ }
+
+ if (Constants.IS_SECURITY_ENABLED) {
+ initSecurity();
+ }
+
+ // If this web application context is running from a
+ // directory, start the background compilation thread
+ String appBase = context.getRealPath("/");
+ if (!options.getDevelopment()
+ && appBase != null
+ && options.getCheckInterval() > 0) {
+ lastCheck = System.currentTimeMillis();
+ }
+ }
+
+ // ----------------------------------------------------- Instance Variables
+
+ /**
+ * This web applications ServletContext
+ */
+ private ServletContext context;
+ private Options options;
+ private URLClassLoader parentClassLoader;
+ private PermissionCollection permissionCollection;
+ private CodeSource codeSource;
+ private String classpath;
+ private long lastCheck = -1L;
+
+ /**
+ * Maps JSP pages to their JspServletWrapper's
+ */
+ private Map<String, JspServletWrapper> jsps = new ConcurrentHashMap<String, JspServletWrapper>();
+
+
+ // ------------------------------------------------------ Public Methods
+
+ /**
+ * Add a new JspServletWrapper.
+ *
+ * @param jspUri JSP URI
+ * @param jsw Servlet wrapper for JSP
+ */
+ public void addWrapper(String jspUri, JspServletWrapper jsw) {
+ jsps.put(jspUri, jsw);
+ }
+
+ /**
+ * Get an already existing JspServletWrapper.
+ *
+ * @param jspUri JSP URI
+ * @return JspServletWrapper for JSP
+ */
+ public JspServletWrapper getWrapper(String jspUri) {
+ return jsps.get(jspUri);
+ }
+
+ /**
+ * Remove a JspServletWrapper.
+ *
+ * @param jspUri JSP URI of JspServletWrapper to remove
+ */
+ public void removeWrapper(String jspUri) {
+ jsps.remove(jspUri);
+ }
+
+ /**
+ * Returns the number of JSPs for which JspServletWrappers exist, i.e.,
+ * the number of JSPs that have been loaded into the webapp.
+ *
+ * @return The number of JSPs that have been loaded into the webapp
+ */
+ public int getJspCount() {
+ return jsps.size();
+ }
+
+ /**
+ * Get the SecurityManager Policy CodeSource for this web
+ * applicaiton context.
+ *
+ * @return CodeSource for JSP
+ */
+ public CodeSource getCodeSource() {
+ return codeSource;
+ }
+
+ /**
+ * Get the parent URLClassLoader.
+ *
+ * @return URLClassLoader parent
+ */
+ public URLClassLoader getParentClassLoader() {
+ return parentClassLoader;
+ }
+
+ /**
+ * Get the SecurityManager PermissionCollection for this
+ * web application context.
+ *
+ * @return PermissionCollection permissions
+ */
+ public PermissionCollection getPermissionCollection() {
+ return permissionCollection;
+ }
+
+ /**
+ * Process a "destory" event for this web application context.
+ */
+ public void destroy() {
+ Iterator servlets = jsps.values().iterator();
+ while (servlets.hasNext()) {
+ ((JspServletWrapper) servlets.next()).destroy();
+ }
+ }
+
+ /**
+ * Increments the JSP reload counter.
+ */
+ public synchronized void incrementJspReloadCount() {
+ jspReloadCount++;
+ }
+
+ /**
+ * Resets the JSP reload counter.
+ *
+ * @param count Value to which to reset the JSP reload counter
+ */
+ public synchronized void setJspReloadCount(int count) {
+ this.jspReloadCount = count;
+ }
+
+ /**
+ * Gets the current value of the JSP reload counter.
+ *
+ * @return The current value of the JSP reload counter
+ */
+ public int getJspReloadCount() {
+ return jspReloadCount;
+ }
+
+
+ /**
+ * Method used by background thread to check the JSP dependencies
+ * registered with this class for JSP's.
+ */
+ public void checkCompile() {
+
+ if (lastCheck < 0) {
+ // Checking was disabled
+ return;
+ }
+ long now = System.currentTimeMillis();
+ if (now > (lastCheck + (options.getCheckInterval() * 1000L))) {
+ lastCheck = now;
+ } else {
+ return;
+ }
+
+ Object [] wrappers = jsps.values().toArray();
+ for (int i = 0; i < wrappers.length; i++ ) {
+ JspServletWrapper jsw = (JspServletWrapper)wrappers[i];
+ JspCompilationContext ctxt = jsw.getJspEngineContext();
+ // JspServletWrapper also synchronizes on this when
+ // it detects it has to do a reload
+ synchronized(jsw) {
+ try {
+ ctxt.compile();
+ } catch (FileNotFoundException ex) {
+ ctxt.incrementRemoved();
+ } catch (Throwable t) {
+ jsw.getServletContext().log("Background compile failed",
+ t);
+ }
+ }
+ }
+
+ }
+
+ /**
+ * The classpath that is passed off to the Java compiler.
+ */
+ public String getClassPath() {
+ return classpath;
+ }
+
+
+ // -------------------------------------------------------- Private Methods
+
+
+ /**
+ * Method used to initialize classpath for compiles.
+ */
+ private void initClassPath() {
+
+ URL [] urls = parentClassLoader.getURLs();
+ StringBuffer cpath = new StringBuffer();
+ String sep = System.getProperty("path.separator");
+
+ for(int i = 0; i < urls.length; i++) {
+ // Tomcat 4 can use URL's other than file URL's,
+ // a protocol other than file: will generate a
+ // bad file system path, so only add file:
+ // protocol URL's to the classpath.
+
+ if( urls[i].getProtocol().equals("file") ) {
+ cpath.append((String)urls[i].getFile()+sep);
+ }
+ }
+
+ cpath.append(options.getScratchDir() + sep);
+
+ String cp = (String) context.getAttribute(Constants.SERVLET_CLASSPATH);
+ if (cp == null || cp.equals("")) {
+ cp = options.getClassPath();
+ }
+
+ classpath = cpath.toString() + cp;
+
+ if(log.isDebugEnabled()) {
+ log.debug("Compilation classpath initialized: " + getClassPath());
+ }
+ }
+
+ /**
+ * Method used to initialize SecurityManager data.
+ */
+ private void initSecurity() {
+
+ // Setup the PermissionCollection for this web app context
+ // based on the permissions configured for the root of the
+ // web app context directory, then add a file read permission
+ // for that directory.
+ Policy policy = Policy.getPolicy();
+ if( policy != null ) {
+ try {
+ // Get the permissions for the web app context
+ String docBase = context.getRealPath("/");
+ if( docBase == null ) {
+ docBase = options.getScratchDir().toString();
+ }
+ String codeBase = docBase;
+ if (!codeBase.endsWith(File.separator)){
+ codeBase = codeBase + File.separator;
+ }
+ File contextDir = new File(codeBase);
+ URL url = contextDir.getCanonicalFile().toURL();
+ codeSource = new CodeSource(url,(Certificate[])null);
+ permissionCollection = policy.getPermissions(codeSource);
+
+ // Create a file read permission for web app context directory
+ if (!docBase.endsWith(File.separator)){
+ permissionCollection.add
+ (new FilePermission(docBase,"read"));
+ docBase = docBase + File.separator;
+ } else {
+ permissionCollection.add
+ (new FilePermission
+ (docBase.substring(0,docBase.length() - 1),"read"));
+ }
+ docBase = docBase + "-";
+ permissionCollection.add(new FilePermission(docBase,"read"));
+
+ // Create a file read permission for web app tempdir (work)
+ // directory
+ String workDir = options.getScratchDir().toString();
+ if (!workDir.endsWith(File.separator)){
+ permissionCollection.add
+ (new FilePermission(workDir,"read"));
+ workDir = workDir + File.separator;
+ }
+ workDir = workDir + "-";
+ permissionCollection.add(new FilePermission(workDir,"read"));
+
+ // Allow the JSP to access org.apache.jasper.runtime.HttpJspBase
+ permissionCollection.add( new RuntimePermission(
+ "accessClassInPackage.org.apache.jasper.runtime") );
+
+ if (parentClassLoader instanceof URLClassLoader) {
+ URL [] urls = parentClassLoader.getURLs();
+ String jarUrl = null;
+ String jndiUrl = null;
+ for (int i=0; i<urls.length; i++) {
+ if (jndiUrl == null
+ && urls[i].toString().startsWith("jndi:") ) {
+ jndiUrl = urls[i].toString() + "-";
+ }
+ if (jarUrl == null
+ && urls[i].toString().startsWith("jar:jndi:")
+ ) {
+ jarUrl = urls[i].toString();
+ jarUrl = jarUrl.substring(0,jarUrl.length() - 2);
+ jarUrl = jarUrl.substring(0,
+ jarUrl.lastIndexOf('/')) + "/-";
+ }
+ }
+ if (jarUrl != null) {
+ permissionCollection.add(
+ new FilePermission(jarUrl,"read"));
+ permissionCollection.add(
+ new FilePermission(jarUrl.substring(4),"read"));
+ }
+ if (jndiUrl != null)
+ permissionCollection.add(
+ new FilePermission(jndiUrl,"read") );
+ }
+ } catch(Exception e) {
+ context.log("Security Init for context failed",e);
+ }
+ }
+ }
+
+
+}