You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2021/01/22 11:50:20 UTC
[tomcat] branch master updated: New StringInterpreter to allow
optimised String -> Type conversions
This is an automated email from the ASF dual-hosted git repository.
markt pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/master by this push:
new 03a3edf New StringInterpreter to allow optimised String -> Type conversions
03a3edf is described below
commit 03a3edf5572e8336321d49bb3db3f4d663b7f935
Author: Mark Thomas <ma...@apache.org>
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 | 91 ++---------
.../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(+), 77 deletions(-)
diff --git a/java/org/apache/jasper/compiler/Generator.java b/java/org/apache/jasper/compiler/Generator.java
index 21593d8..efd5485 100644
--- a/java/org/apache/jasper/compiler/Generator.java
+++ b/java/org/apache/jasper/compiler/Generator.java
@@ -113,6 +113,8 @@ class Generator {
private final ELInterpreter elInterpreter;
+ private final StringInterpreter stringInterpreter;
+
/**
* @param s
* the input string
@@ -3009,7 +3011,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()) {
@@ -3112,7 +3114,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;
@@ -3258,81 +3260,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.
@@ -3593,6 +3520,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..b035752
--- /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 jakarta.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 cf79400..ecdc5d2 100644
--- a/java/org/apache/jasper/resources/LocalStrings.properties
+++ b/java/org/apache/jasper/resources/LocalStrings.properties
@@ -198,6 +198,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 9553dad..22f4853 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: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org