blob: b59c7d29287e3e5c43d91b585e61ad25638a7d95 [file] [log] [blame]
<html>
<head><title>SPEL Implementation</title></head>
<body>
<h1>SPEL Implementation</h1>
<h4>Nathan Abramson (arista@atg.com)</h4>
<h2>Overview</h2>
<p>This distribution contains an implementation of the <a
href="spel.html">Simplest Possible Expression Language (SPEL) for the
JSTL</a>. In addition to the implementation, the distribution
contains regression tests for the parser and evaluator, and a simple
performance test.
<p>The distribution contains the following directories and files:
<ul>
<li><b>doc/</b> - documentation
<li><b>src/</b> - all source files
<li><b>lib/classes.jar</b> - compiled classes
</ul>
<h2>Implementation Notes</h2>
<ul>
<li>The class files are found in the package
<b>org.apache.taglibs.standard.lang.spel</b>.
<li>The class implementing ExpressionEvaluator is
<b>org.apache.taglibs.standard.lang.spel.Evaluator</b>
<li>The implementation assumes that the ExpressionEvaluator interface
is found at <b>javax.servlet.jsp.jstl.core.ExpressionEvaluator</b>, with the
following definition (taken from Shawn's implementation):
<ul><pre>
public String validate(String attributeName,
String expression);
public Object evaluate(String attributeName,
String expression,
Class expectedType,
Tag tag,
PageContext pageContext)
throws JspException;
</pre></ul>
<p>If this is not the package or definition of the ExpressionEvaluator
interface, the appropriate changes should be made to the <tt>
src/org/apache/taglibs/jsptl/lang/spel/Evaluator.java</tt>
source file. In any case, the <tt>ExpressionEvaluator</tt>
implementation should be removed from the distribution, and the
distribution should be compiled against the "real" implementation of
that class.
<li>The parser was generated using <a
href="http://www.webgain.com/products/metamata/java_doc.html">JavaCC</a>.
The parser definition is found in
<tt>src/org/apache/taglibs/jsptl/lang/spel/SpelParser.jj</tt>,
and all files generated by JavaCC are placed into the
org.apache.taglibs.standard.lang.spel.parser package. Note that JavaCC is not
required to compile the SPEL implementation - it is only needed if the
grammar in the SpelParser.jj file changes.
<li>JavaCC 1.0 was used to generate the parser. If desired, the most
recent version of JavaCC can be used to regenerate the parser, but the
<a href="#regressionTests">regression tests</a> should then be run to
make sure the behavior hasn't changed.
<li>All localizable error messages are stored in the
<tt>src/org/apache/taglibs/jsptl/lang/spel/Resources.properties</tt>
file. This file must be copied into the final distribution with the
generated class files.
</ul>
<a name="regressionTests"><h2>Regression Tests</h2></a>
<p>There are two sets of automated regression tests - the first tests
the parser, and the second tests the full evaluator. In both cases,
the tester takes an input file containing expressions to be parsed or
evaluated, processes each expression, and writes the results or errors
to an output file. The output file can then be compared against an
expected output file to see if any regression has occurred.
<p>In both cases, input lines that are blank or start with "#" are
copied to the output file without interpretation. This allows
comments to appear in the output file, which can aid in debugging.
<h4>Parser tests</h4>
<p>The parser test input file is found at
<tt>src/org/apache/taglibs/jsptl/lang/spel/test/parserTests.txt</tt/>.
Each line represents an expression to be parsed - the expression is
parsed, and printed back out in the evaluator's "canonical form". For
example, in the canonical form, all Strings are enclosed by double
quotes, all names are prefixed by a scope operator ":", etc.
<p>The expected outputs are found at <tt>src/org/apache/taglibs/jsptl/lang/spel/test/parserTestsExpectedOutput.txt</tt>.
<p>The parser tests can be run like this:
<ul><pre>
java org.apache.taglibs.standard.lang.spel.test.ParserTest {inputFile} {outputFile} [{expectedOutputFile}]
</pre></ul>
The {expectedOutputFile} is optional - if supplied, the test will
compare the ouput file with the expected output file and print whether
the test passed or failed.
<h4>Evaluation tests</h4>
<p>For the evaluation test, a "dummy" PageContext was created
containing some beans with properties in various scopes. That
PageContext is created in the "createTestContext" method of
<tt>src/org/apache/taglibs/jsptl/lang/spel/test/EvaluationTest.java</tt>.
<p>The evaluation test input file is found at
<tt>src/org/apache/taglibs/jsptl/lang/spel/test/evaluationTests.txt</tt>.
Each line represents an expression to be parsed, followed by a line
specifying the expected type - the expression is parsed, and the
resulting value is printed along with its class.
<p>The expected outputs are found at
<tt>src/org/apache/taglibs/jsptl/lang/spel/test/evaluationTestsExpectedOutput.txt</tt>.
<p>The evaluation tests can be run like this:
<ul><pre>
java org.apache.taglibs.standard.lang.spel.test.EvaluationTest {inputFile} {outputFile} [{expectedOutputFile}]
</pre></ul>
The {expectedOutputFile} is optional - if supplied, the test will
compare the ouput file with the expected output file and print whether
the test passed or failed.
<a name="optimizations"><h2>Optimizations</h2></a>
<p>Because expression evaluation could conceivably make up a large
portion of a JSP's rendering time, a great deal of emphasis was placed
on performance. The following performance optimizations were used:
<ul>
<p><li>Parsed expressions are cached, so that each expression is
parsed only once. To avoid synchronization bottlenecks, the cache is
only synchronized around determining if an expression is in the cache,
and putting a parsed expression into the cache. This means that two
threads could both end up parsing and caching the same uncached
expression at the same time, but the result of this is harmless. This
caching happens in the Evaluator class.
<p><li>The Evaluator class is also responsible for parsing literals
(expressions not starting with "$"). The results of these parsings
are also cached, but in this case the key is {expression,
expectedType}. Rather than creating an actual key class (which would
require object creations for each lookup), the tuple key is simulated
by having two levels of caches: expected type -> expression -> parsed
expression.
<p><li>The mapping from {class,propertyName} to PropertyDescriptor is
cached by the BeanInfoManager. The PropertyDescriptors are used by
the "{bean}.{property}" construct.
<p><li>In cases where a primitive type needs to be wrapped in an
object (such as the final casting from expression result to expected
type), the PrimitiveObjects class is used to do the wrapping. This
class precalculates the wrappings for bytes and chars with values from
0-255, and shorts, ints, and longs with values from -1000 to 1000.
Wrapped values in those ranges will not require any object creations
to represent their primitive values.
</ul>
<a name="performanceTests"><h2>Performance Tests</h2></a>
<p>The distribution includes a single performance test which evaluates
an expression many times. The expression is:
<ul><pre>
session:bean1a.bean1.int1 &lt; 24
</pre></ul>
<p>To test for synchronization bottlenecks, the test spawns several
threads which simultaneously evaluate the expression many times.
<p>The test then runs again, but evaluates the expression "by hand"
using direct API calls - i.e.,
pageContext.getAttribute(...).getBean1().getInt1 () < 24. So the
performance test essentially determines the cost of using an
expression instead of an rtexprvalue.
<p>The test is run like this:
<ul><pre>
org.apache.taglibs.standard.lang.spel.test.PerformanceTest {#threads} {#iterations/thread}
</pre></ul>
<p>The initial results are encouraging - running 20 threads over
100000 iterations/thread, a ThinkPad 600E could do ~50000
iterations/second using the evaluator, and ~110000 iterations/second
using the API calls. So using the API calls in this example is only
twice as fast as using the evaluator, which means that the expression
language introduces a significant, but not severe performance penalty.
<h4><i>$Change: 181170 $$DateTime: 2001/06/26 08:09:32 $$Author$</i></h4>
</html>