This is an automated email from the ASF dual-hosted git repository.
markt pushed a commit to branch 8.5.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/8.5.x by this push:
new 01c08ff New StringInterpreter to allow optimised String -> Type
conversions
01c08ff is described below
commit 01c08fff3054dc20fe181dd72742bbdb97d3603d
Author: Mark Thomas <[email protected]>
AuthorDate: Fri Jan 22 11:22:09 2021 +0000
New StringInterpreter to allow optimised String -> Type conversions
This is the additional hook required to provide the optimisations
proposed in https://bz.apache.org/bugzilla/show_bug.cgi?id=64872
---
java/org/apache/jasper/compiler/Generator.java | 85 ++--------
.../apache/jasper/compiler/StringInterpreter.java | 49 ++++++
.../jasper/compiler/StringInterpreterFactory.java | 178 +++++++++++++++++++++
.../jasper/resources/LocalStrings.properties | 1 +
webapps/docs/changelog.xml | 6 +
5 files changed, 248 insertions(+), 71 deletions(-)
diff --git a/java/org/apache/jasper/compiler/Generator.java
b/java/org/apache/jasper/compiler/Generator.java
index b0797a1..ef445da 100644
--- a/java/org/apache/jasper/compiler/Generator.java
+++ b/java/org/apache/jasper/compiler/Generator.java
@@ -129,6 +129,8 @@ class Generator {
private final ELInterpreter elInterpreter;
+ private final StringInterpreter stringInterpreter;
+
/**
* @param s
* the input string
@@ -3014,7 +3016,7 @@ class Generator {
} else if (attr.isNamedAttribute()) {
if (!n.checkIfAttributeIsJspFragment(attr.getName())
&& !attr.isDynamic()) {
- attrValue = convertString(c[0], attrValue, localName,
+ attrValue = stringInterpreter.convertString(c[0],
attrValue, localName,
handlerInfo.getPropertyEditorClass(localName),
true);
}
} else if (attr.isELInterpreterInput()) {
@@ -3117,7 +3119,7 @@ class Generator {
this.isTagFile, attrValue, c[0], mapName);
}
} else {
- attrValue = convertString(c[0], attrValue, localName,
+ attrValue = stringInterpreter.convertString(c[0], attrValue,
localName,
handlerInfo.getPropertyEditorClass(localName), false);
}
return attrValue;
@@ -3263,75 +3265,6 @@ class Generator {
}
/*
- * @param c The target class to which to coerce the given string @param
- * s The string value @param attrName The name of the attribute whose
- * value is being supplied @param propEditorClass The property editor
- * for the given attribute @param isNamedAttribute true if the given
- * attribute is a named attribute (that is, specified using the
- * jsp:attribute standard action), and false otherwise
- */
- private String convertString(Class<?> c, String s, String attrName,
- Class<?> propEditorClass, boolean isNamedAttribute) {
-
- String quoted = s;
- if (!isNamedAttribute) {
- quoted = quote(s);
- }
-
- if (propEditorClass != null) {
- String className = c.getCanonicalName();
- return "("
- + className
- +
")org.apache.jasper.runtime.JspRuntimeLibrary.getValueFromBeanInfoPropertyEditor("
- + className + ".class, \"" + attrName + "\", " + quoted
- + ", " + propEditorClass.getCanonicalName() +
".class)";
- } else if (c == String.class) {
- return quoted;
- } else if (c == boolean.class) {
- return JspUtil.coerceToPrimitiveBoolean(s, isNamedAttribute);
- } else if (c == Boolean.class) {
- return JspUtil.coerceToBoolean(s, isNamedAttribute);
- } else if (c == byte.class) {
- return JspUtil.coerceToPrimitiveByte(s, isNamedAttribute);
- } else if (c == Byte.class) {
- return JspUtil.coerceToByte(s, isNamedAttribute);
- } else if (c == char.class) {
- return JspUtil.coerceToChar(s, isNamedAttribute);
- } else if (c == Character.class) {
- return JspUtil.coerceToCharacter(s, isNamedAttribute);
- } else if (c == double.class) {
- return JspUtil.coerceToPrimitiveDouble(s, isNamedAttribute);
- } else if (c == Double.class) {
- return JspUtil.coerceToDouble(s, isNamedAttribute);
- } else if (c == float.class) {
- return JspUtil.coerceToPrimitiveFloat(s, isNamedAttribute);
- } else if (c == Float.class) {
- return JspUtil.coerceToFloat(s, isNamedAttribute);
- } else if (c == int.class) {
- return JspUtil.coerceToInt(s, isNamedAttribute);
- } else if (c == Integer.class) {
- return JspUtil.coerceToInteger(s, isNamedAttribute);
- } else if (c == short.class) {
- return JspUtil.coerceToPrimitiveShort(s, isNamedAttribute);
- } else if (c == Short.class) {
- return JspUtil.coerceToShort(s, isNamedAttribute);
- } else if (c == long.class) {
- return JspUtil.coerceToPrimitiveLong(s, isNamedAttribute);
- } else if (c == Long.class) {
- return JspUtil.coerceToLong(s, isNamedAttribute);
- } else if (c == Object.class) {
- return quoted;
- } else {
- String className = c.getCanonicalName();
- return "("
- + className
- +
")org.apache.jasper.runtime.JspRuntimeLibrary.getValueFromPropertyEditorManager("
- + className + ".class, \"" + attrName + "\", " + quoted
- + ")";
- }
- }
-
- /*
* Converts the scope string representation, whose possible values are
* "page", "request", "session", and "application", to the
corresponding
* scope constant.
@@ -3597,6 +3530,16 @@ class Generator {
}
this.elInterpreter = elInterpreter;
+ StringInterpreter stringInterpreter = null;
+ try {
+ stringInterpreter = StringInterpreterFactory.getStringInterpreter(
+ compiler.getCompilationContext().getServletContext());
+ } catch (Exception e) {
+ err.jspError("jsp.error.string_interpreter_class.instantiation",
+ e.getMessage());
+ }
+ this.stringInterpreter = stringInterpreter;
+
/*
* Temporary hack. If a JSP page uses the "extends" attribute of the
* page directive, the _jspInit() method of the generated servlet class
diff --git a/java/org/apache/jasper/compiler/StringInterpreter.java
b/java/org/apache/jasper/compiler/StringInterpreter.java
new file mode 100644
index 0000000..038e262
--- /dev/null
+++ b/java/org/apache/jasper/compiler/StringInterpreter.java
@@ -0,0 +1,49 @@
+/*
+ * 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;
+
+/**
+ * Defines the interface for the String interpreter. This allows users to
+ * provide custom String interpreter implementations that can optimise
+ * String processing for an application by performing code generation for
+ * a sub-set of Strings.
+ */
+public interface StringInterpreter {
+
+ /**
+ * Generates the source code that represents the conversion of the string
+ * value to the appropriate type.
+ *
+ * @param c
+ * The target class to which to coerce the given string
+ * @param s
+ * The string value
+ * @param attrName
+ * The name of the attribute whose value is being supplied
+ * @param propEditorClass
+ * The property editor for the given attribute
+ * @param isNamedAttribute
+ * true if the given attribute is a named attribute (that
+ * is, specified using the jsp:attribute standard action),
+ * and false otherwise
+ *
+ * @return the string representing the code that will be inserted into the
+ * source code for the Servlet generated for the JSP.
+ */
+ String convertString(Class<?> c, String s, String attrName,
+ Class<?> propEditorClass, boolean isNamedAttribute);
+}
diff --git a/java/org/apache/jasper/compiler/StringInterpreterFactory.java
b/java/org/apache/jasper/compiler/StringInterpreterFactory.java
new file mode 100644
index 0000000..1e5bcc5
--- /dev/null
+++ b/java/org/apache/jasper/compiler/StringInterpreterFactory.java
@@ -0,0 +1,178 @@
+/*
+ * 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 javax.servlet.ServletContext;
+
+/**
+ * Provides {@link StringInterpreter} instances for JSP compilation.
+ *
+ * The search order is as follows:
+ * <ol>
+ * <li>StringInterpreter instance or implementation class name provided as a
+ * ServletContext attribute</li>
+ * <li>Implementation class named in a ServletContext initialisation parameter
+ * </li>
+ * <li>Default implementation</li>
+ * </ol>
+ */
+public class StringInterpreterFactory {
+
+ public static final String STRING_INTERPRETER_CLASS_NAME =
StringInterpreter.class.getName();
+
+ private static final StringInterpreter DEFAULT_INSTANCE = new
DefaultStringInterpreter();
+
+
+ /**
+ * Obtain the correct String Interpreter for the given web application.
+ * @param context The Servlet context
+ * @return the String interpreter
+ * @throws Exception If an error occurs creating the interpreter
+ */
+ public static StringInterpreter getStringInterpreter(ServletContext
context)
+ throws Exception {
+
+ StringInterpreter result = null;
+
+ // Search for an implementation
+ // 1. ServletContext attribute (set by application or cached by a
+ // previous call to this method).
+ Object attribute = context.getAttribute(STRING_INTERPRETER_CLASS_NAME);
+ if (attribute instanceof StringInterpreter) {
+ return (StringInterpreter) attribute;
+ } else if (attribute instanceof String) {
+ result = createInstance(context, (String) attribute);
+ }
+
+ // 2. ServletContext init parameter
+ if (result == null) {
+ String className =
context.getInitParameter(STRING_INTERPRETER_CLASS_NAME);
+ if (className != null) {
+ result = createInstance(context, className);
+ }
+ }
+
+ // 3. Default
+ if (result == null) {
+ result = DEFAULT_INSTANCE;
+ }
+
+ // Cache the result for next time
+ context.setAttribute(STRING_INTERPRETER_CLASS_NAME, result);
+ return result;
+ }
+
+
+ private static StringInterpreter createInstance(ServletContext context,
+ String className) throws Exception {
+ return (StringInterpreter) context.getClassLoader().loadClass(
+ className).getConstructor().newInstance();
+ }
+
+
+ private StringInterpreterFactory() {
+ // Utility class. Hide default constructor.
+ }
+
+
+ public static class DefaultStringInterpreter implements StringInterpreter {
+
+ @Override
+ public String convertString(Class<?> c, String s, String attrName,
+ Class<?> propEditorClass, boolean isNamedAttribute) {
+
+ String quoted = s;
+ if (!isNamedAttribute) {
+ quoted = Generator.quote(s);
+ }
+
+ if (propEditorClass != null) {
+ String className = c.getCanonicalName();
+ return "("
+ + className
+ +
")org.apache.jasper.runtime.JspRuntimeLibrary.getValueFromBeanInfoPropertyEditor("
+ + className + ".class, \"" + attrName + "\", " + quoted
+ + ", " + propEditorClass.getCanonicalName() +
".class)";
+ } else if (c == String.class) {
+ return quoted;
+ } else if (c == boolean.class) {
+ return JspUtil.coerceToPrimitiveBoolean(s, isNamedAttribute);
+ } else if (c == Boolean.class) {
+ return JspUtil.coerceToBoolean(s, isNamedAttribute);
+ } else if (c == byte.class) {
+ return JspUtil.coerceToPrimitiveByte(s, isNamedAttribute);
+ } else if (c == Byte.class) {
+ return JspUtil.coerceToByte(s, isNamedAttribute);
+ } else if (c == char.class) {
+ return JspUtil.coerceToChar(s, isNamedAttribute);
+ } else if (c == Character.class) {
+ return JspUtil.coerceToCharacter(s, isNamedAttribute);
+ } else if (c == double.class) {
+ return JspUtil.coerceToPrimitiveDouble(s, isNamedAttribute);
+ } else if (c == Double.class) {
+ return JspUtil.coerceToDouble(s, isNamedAttribute);
+ } else if (c == float.class) {
+ return JspUtil.coerceToPrimitiveFloat(s, isNamedAttribute);
+ } else if (c == Float.class) {
+ return JspUtil.coerceToFloat(s, isNamedAttribute);
+ } else if (c == int.class) {
+ return JspUtil.coerceToInt(s, isNamedAttribute);
+ } else if (c == Integer.class) {
+ return JspUtil.coerceToInteger(s, isNamedAttribute);
+ } else if (c == short.class) {
+ return JspUtil.coerceToPrimitiveShort(s, isNamedAttribute);
+ } else if (c == Short.class) {
+ return JspUtil.coerceToShort(s, isNamedAttribute);
+ } else if (c == long.class) {
+ return JspUtil.coerceToPrimitiveLong(s, isNamedAttribute);
+ } else if (c == Long.class) {
+ return JspUtil.coerceToLong(s, isNamedAttribute);
+ } else if (c == Object.class) {
+ return quoted;
+ }
+
+ String result = coerceToOtherType(c, s, isNamedAttribute);
+
+ if (result != null) {
+ return result;
+ }
+
+ String className = c.getCanonicalName();
+ return "("
+ + className
+ +
")org.apache.jasper.runtime.JspRuntimeLibrary.getValueFromPropertyEditorManager("
+ + className + ".class, \"" + attrName + "\", " + quoted
+ + ")";
+ }
+
+
+ /**
+ * Intended to be used by sub-classes that don't need/want to
+ * re-implement the logic in
+ * {@link #convertString(Class, String, String, Class, boolean)}.
+ *
+ * @param c unused
+ * @param s unused
+ * @param isNamedAttribute unused
+ *
+ * @return Always {@code null}
+ */
+ protected String coerceToOtherType(Class<?> c, String s, boolean
isNamedAttribute) {
+ return null;
+ }
+ }
+}
diff --git a/java/org/apache/jasper/resources/LocalStrings.properties
b/java/org/apache/jasper/resources/LocalStrings.properties
index 34a057c..c95dad5 100644
--- a/java/org/apache/jasper/resources/LocalStrings.properties
+++ b/java/org/apache/jasper/resources/LocalStrings.properties
@@ -177,6 +177,7 @@ jsp.error.simpletag.badbodycontent=The TLD for the class
[{0}] specifies an inva
jsp.error.single.line.number=An error occurred at line: [{0}] in the jsp file:
[{1}]
jsp.error.stream.close.failed=Failed to close stream
jsp.error.stream.closed=Stream closed
+jsp.error.string_interpreter_class.instantiation=Failed to load or instantiate
StringInterpreter class [{0}]
jsp.error.tag.conflict.attr=Tag directive: illegal to have multiple
occurrences of the attribute [{0}] with different values (old: [{1}], new:
[{2}])
jsp.error.tag.conflict.deferredsyntaxallowedasliteral=Tag directive: illegal
to have multiple occurrences of ''deferredSyntaxAllowedAsLiteral'' with
different values (old: [{0}], new: [{1}])
jsp.error.tag.conflict.iselignored=Tag directive: illegal to have multiple
occurrences of ''isELIgnored'' with different values (old: [{0}], new: [{1}])
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 6099920..a06b0c1 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -183,6 +183,12 @@
</subsection>
<subsection name="Jasper">
<changelog>
+ <add>
+ Add a new <code>StringInterpreter</code> interface that allows
+ applications to provide customised string attribute value to type
+ conversion within JSPs. This allows applications to provide a
conversion
+ implementation that is optimised for the application. (markt)
+ </add>
<fix>
<bug>64965</bug>: <code>JspContextWrapper.findAttribute</code> should
ignore expired sessions rather than throw an
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]