| <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 < 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> |