This is an automated email from the ASF dual-hosted git repository. markt pushed a commit to branch 9.0.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/9.0.x by this push: new de149b4 New StringInterpreter to allow optimised String -> Type conversions de149b4 is described below commit de149b490976caec050e22aeb9f53a993b1f68c5 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 7f8f7d6..7be9edb 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 @@ -3022,7 +3024,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()) { @@ -3125,7 +3127,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; @@ -3271,81 +3273,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. @@ -3606,6 +3533,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 587e185..e716b47 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 aa1dd31..2484402 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-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org