This document is meant as a development/integration guide for anyone wanting to use the new OGNL 2.7 features for doing byte code runtime enhancements on OGNL statements. This is not meant for general user reference as it covers what are mostly internal API development concerns.
Basic Usage
By default there isn't much you have to do to use the new compilation abilities in OGNL. Following is an example of compiling a simple property expression and invoking it.
SimpleObject root = new SimpleObject(); OgnlContext context = (OgnlContext) Ognl.createDefaultContext(null); Node node = (Node) Ognl.compileExpression(context, root, "user.name"); String userName = node.getAccessor().get(context, root);
You'll notice that this example references the new ognl.enhance.ExpressionAccessor
class. This is the interface used to create the enhanced expression versions of any given expression via javassist and should be used to set/get expression values from the compiled versions of the code. Although the old Ognl.getValue(node, context, root)
method of getting/setting values will correctly detect a compiled expression and use the accessor directly as well, it's not going to be as fast as you doing it directly.
ognl.enhance.OgnlExpressionCompiler
The core class involved in doing the management of these expression compilations by default is ognl.enhance.ExpressionCompiler
, which implements ognl.enhance.OgnlExpressionCompiler
. Although you can in theory use this default implementation it is not recommended for more robust integration points - such as being incorporated within a web framework. The majority of examples here are going to be based around the strategy that Tapestry has used to integrate these new features.
Tapestry OGNL Integration
There are only small handful of classes/services involved in the Tapestry implementation of these features, so hopefully using them as a reference will help anyone trying to get started with this:
- org.apache.tapestry.services.impl.HiveMindExpressionCompiler The Tapestry implementation of
ognl.enhance.OgnlExpressionCompiler
- which is a subclass of theognl.enhance.ExpressionCompiler
default implementation. - org.apache.tapestry.services.impl.ExpressionEvaluatorImpl Main service point involved in compiling/evaluating OGNL expressions. This is the core service that the rest of Tapestry uses when dealing with OGNL expressions.
- org.apache.tapestry.services.impl.ExpressionCacheImpl Service responsible for caching OGNL statements where appropriate.
- org.apache.tapestry.binding.ExpressionBinding Wrapper class which represents a single OGNL binding expression within Tapestry templates/annotations/html/etc. Anything formally specified in an html attribute for components in Tapestry is represented by a specific type of
IBinding
,ExpressionBinding
represents the type of bindings for OGNL expressions.
*org.apache.tapestry.bean.BeanProviderPropertyAccessor One of the customPropertyAccessor
classes Tapestry registers with OGNL. This will be a good reference for the new source code generation methods you will need to implement for yourPropertyAccessor
classes if you want to compile expressions.
ExpressionEvaluator
If you look at the ExpressionEvaluator
source you'll see a block of initialization where the HiveMindExpressionCompiler
and OgnlContext
pools are setup:
OgnlRuntime.setCompiler(new HiveMindExpressionCompiler(_classFactory)); _contextPool = new GenericObjectPool(new PoolableOgnlContextFactory(_ognlResolver, _typeConverter)); _contextPool.setMaxActive(-1); _contextPool.setMaxIdle(-1); _contextPool.setMinEvictableIdleTimeMillis(POOL_MIN_IDLE_TIME); _contextPool.setTimeBetweenEvictionRunsMillis(POOL_SLEEP_TIME);
Some things like null handlers/property accessor configuration has been left out but you should have enough there to get a good idea of what is going on. Because creating new OgnlContext objects for every expression evaluation can be needlessly expensive Tapestry uses the Apache commons-pool library to manage pooling of these instances. It is recommended that you do the same where you can. You will also notice in other portions of the source some new method calls made on OgnlRuntime
:
OgnlRuntime.clearCache(); Introspector.flushCaches();
The OgnlRuntime class stores static Map
-like instances of reflection meta cache information for all objects evaluated in OGNL expressions. The new clearCache
method clears these caches out as the memory footprint can get quite large after a while. How often/when to call this will largely depend on how your framework works - just keep in mind that calling it too often will have a big impact on runtime performance of your app if you are doing normal application development sort of things with it.