blob: dbed37cd22da612d4f187e2018085a898b228611 [file] [log] [blame]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta name="Date-Revision-yyyymmdd" content="20140918"/>
<meta http-equiv="Content-Language" content="en"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>OGNL Expression Compilation</title>
<link href="//fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,400italic,600italic,700italic" rel="stylesheet" type="text/css">
<link href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" rel="stylesheet">
<link href="/css/main.css" rel="stylesheet">
<link href="/css/custom.css" rel="stylesheet">
<link href="/highlighter/github-theme.css" rel="stylesheet">
<script src="//code.jquery.com/jquery-1.11.0.min.js"></script>
<script type="text/javascript" src="/bootstrap/js/bootstrap.js"></script>
<script type="text/javascript" src="/js/community.js"></script>
</head>
<body>
<a href="http://github.com/apache/struts" class="github-ribbon">
<img style="position: absolute; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub">
</a>
<header>
<nav>
<div role="navigation" class="navbar navbar-default navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" data-toggle="collapse" data-target="#struts-menu" class="navbar-toggle">
Menu
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a href="/index.html" class="navbar-brand logo"><img src="/img/struts-logo.svg"></a>
</div>
<div id="struts-menu" class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li class="dropdown">
<a data-toggle="dropdown" href="#" class="dropdown-toggle">
Home<b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li><a href="/index.html">Welcome</a></li>
<li><a href="/download.cgi">Download</a></li>
<li><a href="/releases.html">Releases</a></li>
<li><a href="/announce-2021.html">Announcements</a></li>
<li><a href="http://www.apache.org/licenses/">License</a></li>
<li><a href="https://www.apache.org/foundation/thanks.html">Thanks!</a></li>
<li><a href="https://www.apache.org/foundation/sponsorship.html">Sponsorship</a></li>
</ul>
</li>
<li class="dropdown">
<a data-toggle="dropdown" href="#" class="dropdown-toggle">
Support<b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li><a href="/mail.html">User Mailing List</a></li>
<li><a href="https://issues.apache.org/jira/browse/WW">Issue Tracker</a></li>
<li><a href="/security.html">Reporting Security Issues</a></li>
<li class="divider"></li>
<li><a href="https://cwiki.apache.org/confluence/display/WW/Migration+Guide">Version Notes</a></li>
<li><a href="https://cwiki.apache.org/confluence/display/WW/Security+Bulletins">Security Bulletins</a></li>
<li class="divider"></li>
<li><a href="/maven/project-info.html">Maven Project Info</a></li>
<li><a href="/maven/struts2-core/dependencies.html">Struts Core Dependencies</a></li>
<li><a href="/maven/struts2-plugins/modules.html">Plugin Dependencies</a></li>
</ul>
</li>
<li class="dropdown">
<a data-toggle="dropdown" href="#" class="dropdown-toggle">
Documentation<b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li><a href="/birdseye.html">Birds Eye</a></li>
<li><a href="/primer.html">Key Technologies</a></li>
<li><a href="/kickstart.html">Kickstart FAQ</a></li>
<li><a href="https://cwiki.apache.org/confluence/display/WW/Home">Wiki</a></li>
<li class="divider"></li>
<li><a href="/getting-started/">Getting Started</a></li>
<li><a href="/security/">Security Guide</a></li>
<li><a href="/core-developers/">Core Developers Guide</a></li>
<li><a href="/tag-developers/">Tag Developers Guide</a></li>
<li><a href="/maven-archetypes/">Maven Archetypes</a></li>
<li><a href="/plugins/">Plugins</a></li>
<li><a href="/maven/struts2-core/apidocs/index.html">Struts Core API</a></li>
<li><a href="/tag-developers/tag-reference.html">Tag reference</a></li>
<li><a href="https://cwiki.apache.org/confluence/display/WW/FAQs">FAQs</a></li>
<li><a href="http://cwiki.apache.org/S2PLUGINS/home.html">Plugin registry</a></li>
</ul>
</li>
<li class="dropdown">
<a data-toggle="dropdown" href="#" class="dropdown-toggle">
Contributing<b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li><a href="/youatstruts.html">You at Struts</a></li>
<li><a href="/helping.html">How to Help FAQ</a></li>
<li><a href="/dev-mail.html">Development Lists</a></li>
<li><a href="/contributors/">Contributors Guide</a></li>
<li class="divider"></li>
<li><a href="/submitting-patches.html">Submitting patches</a></li>
<li><a href="/builds.html">Source Code and Builds</a></li>
<li><a href="/coding-standards.html">Coding standards</a></li>
<li><a href="https://cwiki.apache.org/confluence/display/WW/Contributors+Guide">Contributors Guide</a></li>
<li class="divider"></li>
<li><a href="/release-guidelines.html">Release Guidelines</a></li>
<li><a href="/bylaws.html">PMC Charter</a></li>
<li><a href="/volunteers.html">Volunteers</a></li>
<li><a href="https://gitbox.apache.org/repos/asf?p=struts.git">Source Repository</a></li>
<li><a href="/updating-website.html">Updating the website</a></li>
</ul>
</li>
<li class="apache"><a href="http://www.apache.org/"><img src="/img/apache.png"></a></li>
</ul>
</div>
</div>
</div>
</nav>
</header>
<article class="container">
<section class="col-md-12">
<a class="edit-on-gh" href="https://github.com/apache/struts-site/edit/master/source/tag-developers/ognl-expression-compilation.md" title="Edit this page on GitHub">Edit on GitHub</a>
<a href="index" title="back to Tag Developers Guide"><< back to Tag Developers Guide</a>
<h1 class="no_toc" id="ognl-expression-compilation">OGNL Expression Compilation</h1>
<ul id="markdown-toc">
<li><a href="#tapestry-ognl-integration" id="markdown-toc-tapestry-ognl-integration">Tapestry OGNL Integration</a></li>
<li><a href="#expressionevaluator" id="markdown-toc-expressionevaluator">ExpressionEvaluator</a></li>
<li><a href="#hivemindexpressioncompiler" id="markdown-toc-hivemindexpressioncompiler">HiveMindExpressionCompiler</a></li>
<li><a href="#expressionbinding" id="markdown-toc-expressionbinding">ExpressionBinding</a></li>
<li><a href="#beanproviderpropertyaccessor--custom-propertyaccessor-implementations" id="markdown-toc-beanproviderpropertyaccessor--custom-propertyaccessor-implementations">BeanProviderPropertyAccessor / Custom PropertyAccessor implementations</a></li>
<li><a href="#compiler-errors" id="markdown-toc-compiler-errors">Compiler Errors</a></li>
<li><a href="#compile-vs-normal-expression-evaluation" id="markdown-toc-compile-vs-normal-expression-evaluation">Compile vs. normal expression evaluation</a></li>
<li><a href="#snapshot-repository" id="markdown-toc-snapshot-repository">Snapshot Repository</a></li>
</ul>
<p>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 <em>not</em> meant for general user reference as it covers what
are mostly internal API development concerns.</p>
<p>## Basic Usage</p>
<p>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.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">SimpleObject</span> <span class="n">root</span> <span class="o">=</span> <span class="k">new</span> <span class="n">SimpleObject</span><span class="o">();</span>
<span class="n">OgnlContext</span> <span class="n">context</span> <span class="o">=</span> <span class="o">(</span><span class="n">OgnlContext</span><span class="o">)</span> <span class="n">Ognl</span><span class="o">.</span><span class="na">createDefaultContext</span><span class="o">(</span><span class="kc">null</span><span class="o">);</span>
<span class="n">Node</span> <span class="n">node</span> <span class="o">=</span> <span class="o">(</span><span class="n">Node</span><span class="o">)</span> <span class="n">Ognl</span><span class="o">.</span><span class="na">compileExpression</span><span class="o">(</span><span class="n">context</span><span class="o">,</span> <span class="n">root</span><span class="o">,</span> <span class="s">"user.name"</span><span class="o">);</span>
<span class="n">String</span> <span class="n">userName</span> <span class="o">=</span> <span class="n">node</span><span class="o">.</span><span class="na">getAccessor</span><span class="o">().</span><span class="na">get</span><span class="o">(</span><span class="n">context</span><span class="o">,</span> <span class="n">root</span><span class="o">);</span>
</code></pre></div></div>
<p>You’ll notice that this example references the new <code class="highlighter-rouge">ognl.enhance.ExpressionAccessor</code> 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 <code class="highlighter-rouge">Ognl.getValue(node, context, root)</code> 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.</p>
<p>## ognl.enhance.OgnlExpressionCompiler</p>
<p>The core class involved in doing the management of these expression compilations by default is <code class="highlighter-rouge">ognl.enhance.ExpressionCompiler</code>,
which implements <code class="highlighter-rouge">ognl.enhance.OgnlExpressionCompiler</code>. 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.</p>
<h2 id="tapestry-ognl-integration">Tapestry OGNL Integration</h2>
<p>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:</p>
<ul>
<li><a href="http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/services/impl/HiveMindExpressionCompiler.java?view=markup">org.apache.tapestry.services.impl.HiveMindExpressionCompiler</a>
The Tapestry implementation of <code class="highlighter-rouge">ognl.enhance.OgnlExpressionCompiler</code> - which is a subclass
of the <code class="highlighter-rouge">ognl.enhance.ExpressionCompiler</code> default implementation.
_ <a href="http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/services/impl/ExpressionEvaluatorImpl.java?view=markup">org.apache.tapestry.services.impl.ExpressionEvaluatorImpl</a>
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.</li>
<li><a href="http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/services/impl/ExpressionCacheImpl.java?view=markup">org.apache.tapestry.services.impl.ExpressionCacheImpl</a>
Service responsible for caching OGNL statements where appropriate.</li>
<li><a href="http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/binding/ExpressionBinding.java?view=markup">org.apache.tapestry.binding.ExpressionBinding</a>
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 <code class="highlighter-rouge">IBinding</code>,
<code class="highlighter-rouge">ExpressionBinding</code> represents the type of bindings for OGNL expressions.</li>
<li><a href="http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/bean/BeanProviderPropertyAccessor.java?view=markup">org.apache.tapestry.bean.BeanProviderPropertyAccessor</a>
One of the custom <code class="highlighter-rouge">PropertyAccessor</code> classes Tapestry registers with OGNL. This will be a good reference for the new
source code generation methods you will need to implement for your <code class="highlighter-rouge">PropertyAccessor</code> classes if you want to compile
expressions.</li>
</ul>
<h2 id="expressionevaluator">ExpressionEvaluator</h2>
<p>If you look at the <code class="highlighter-rouge">ExpressionEvaluator</code> source you’ll see a block of initialization where the <code class="highlighter-rouge">HiveMindExpressionCompiler</code>
and <code class="highlighter-rouge">OgnlContext</code> pools are setup:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">OgnlRuntime</span><span class="o">.</span><span class="na">setCompiler</span><span class="o">(</span><span class="k">new</span> <span class="n">HiveMindExpressionCompiler</span><span class="o">(</span><span class="n">_classFactory</span><span class="o">));</span>
<span class="n">_contextPool</span> <span class="o">=</span> <span class="k">new</span> <span class="n">GenericObjectPool</span><span class="o">(</span><span class="k">new</span> <span class="n">PoolableOgnlContextFactory</span><span class="o">(</span><span class="n">_ognlResolver</span><span class="o">,</span> <span class="n">_typeConverter</span><span class="o">));</span>
<span class="n">_contextPool</span><span class="o">.</span><span class="na">setMaxActive</span><span class="o">(-</span><span class="mi">1</span><span class="o">);</span>
<span class="n">_contextPool</span><span class="o">.</span><span class="na">setMaxIdle</span><span class="o">(-</span><span class="mi">1</span><span class="o">);</span>
<span class="n">_contextPool</span><span class="o">.</span><span class="na">setMinEvictableIdleTimeMillis</span><span class="o">(</span><span class="n">POOL_MIN_IDLE_TIME</span><span class="o">);</span>
<span class="n">_contextPool</span><span class="o">.</span><span class="na">setTimeBetweenEvictionRunsMillis</span><span class="o">(</span><span class="n">POOL_SLEEP_TIME</span><span class="o">);</span>
</code></pre></div></div>
<p>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
<code class="highlighter-rouge">OgnlRuntime</code>:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">OgnlRuntime</span><span class="o">.</span><span class="na">clearCache</span><span class="o">();</span>
<span class="n">Introspector</span><span class="o">.</span><span class="na">flushCaches</span><span class="o">();</span>
</code></pre></div></div>
<p>The OgnlRuntime class stores static <code class="highlighter-rouge">Map</code>-like instances of reflection meta cache information for all objects evaluated
in OGNL expressions. The new <code class="highlighter-rouge">clearCache</code> 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.</p>
<h2 id="hivemindexpressioncompiler">HiveMindExpressionCompiler</h2>
<p>Perhaps the most important class to examine is Tapestrys implementation of <code class="highlighter-rouge">OgnlExpressionCompiler</code>. This class still
extends the default <code class="highlighter-rouge">ExpressionCompiler</code> provided by OGNL - but does a few more things that can’t be made generic enough
to live in the default implementation.</p>
<p>One of these important differences is how Javassist is used to compile the expressions and the ClassLoader/ClassResolver
it uses. Because these expressions are being compiled against what are already Javassist enhanced Tapestry component
class instances this implementation needed to re-use existing hivemind Javassist services so that these enhanced classes
could be correctly resolved while OGNL is evaluating them.</p>
<p>If you don’t have a need to provide this kind of classloading functionality you will probably still need to modify
at least how the javassist <code class="highlighter-rouge">ClassPool</code> is being managed in your own implementations. The internal functionality of that
library is such that the memory consumption of the pool is very large and will get unwieldy especially in development
of web apps. Tapestry has a special state that users are used to which is known as “disable caching” - more or less
meaning that javassist enhancements happen for every request instead of only once.</p>
<p>Another very important piece of logic that this class handles is the generation of “fail safe” getters/setters when
expressions just can’t be compiled because of either internal errors or a specific syntax type used isn’t yet able to support
javassist compilations. This logic can sometimes get tricky in that in many instances OGNL expressions won’t be compilable
because the full expression contains a null reference. The basic idea is that the compiler keeps trying to compile these
kinds of expressions until it either gets a fatal exception thrown or the full expression is able to be resolved.
For example, the following expression would throw a <code class="highlighter-rouge">UnsupportedCompilationException</code> if the “user” object returned
was null - resulting in no direct compilation being done at all:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>"user.firstName"
</code></pre></div></div>
<p>That doesn’t mean that the user object might not be resolvable the next time this expression is invoked though,
so the next time the compiler tries it may succeed in which case the whole expression is enhanced and the new <code class="highlighter-rouge">ExpressionAccessor</code>
instance is attached to the root <code class="highlighter-rouge">Node</code> object by calling <code class="highlighter-rouge">SimpleNode.setAccessor(newInstance)</code>.</p>
<p>The fail safe logic is there for expressions that are likely to never be resolvable for one reason or another. In these
instances a <code class="highlighter-rouge">ExpressionAccessor</code> class instance is still created - with the major difference being that instead of pure
java object expressions being compiled the get/set methods on the instance just call back to the standard OGNL
getValue/setValue methods:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="n">Object</span> <span class="nf">get</span><span class="o">(</span><span class="n">OgnlContext</span> <span class="n">context</span><span class="o">,</span> <span class="n">Object</span> <span class="n">root</span><span class="o">)</span>
<span class="o">{</span>
<span class="k">return</span> <span class="n">_node</span><span class="o">.</span><span class="na">getValue</span><span class="o">(</span><span class="err">$</span><span class="mi">1</span><span class="o">,</span> <span class="err">$</span><span class="mi">2</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p>The <code class="highlighter-rouge">$1, $2</code> references are Javassist constructs which allow you to specify the first and second argument passed in
to the calling method.</p>
<h2 id="expressionbinding">ExpressionBinding</h2>
<p>As stated previously, this class represents a single OGNL expression in Tapestry when used directly in html templates</p>
<ul>
<li>such as:</li>
</ul>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;div</span> <span class="na">jwcid=</span><span class="s">"@Input"</span> <span class="na">value=</span><span class="s">"ognl:user.firstName"</span> <span class="nt">/&gt;</span>
</code></pre></div></div>
<p>What you will want to examine in this class is how it deals with incrementally attempting expression evaluations using
the local members <code class="highlighter-rouge">_writeFailed, _accessor</code>. Looking through the source of this implementation will probably be the best
documentation available - but keep in mind that in many instances this object also has to deal with the possibility
that a write statement may never happen.</p>
<h2 id="beanproviderpropertyaccessor--custom-propertyaccessor-implementations">BeanProviderPropertyAccessor / Custom PropertyAccessor implementations</h2>
<p>Besides the <code class="highlighter-rouge">OgnlExpressionCompiler</code> logic this will probably be the second most impactual area people will have to deal
with in terms of having to write new code. In this specific instance there are three new <code class="highlighter-rouge">PropertyAccessor</code> methods
you must implement in order to compile your expressions:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="n">Class</span> <span class="nf">getPropertyClass</span><span class="o">(</span><span class="n">OgnlContext</span> <span class="n">context</span><span class="o">,</span> <span class="n">Object</span> <span class="n">target</span><span class="o">,</span> <span class="n">Object</span> <span class="n">name</span><span class="o">)</span>
<span class="o">{</span>
<span class="n">IBeanProvider</span> <span class="n">provider</span> <span class="o">=</span> <span class="o">(</span><span class="n">IBeanProvider</span><span class="o">)</span><span class="n">target</span><span class="o">;</span>
<span class="n">String</span> <span class="n">beanName</span> <span class="o">=</span> <span class="o">((</span><span class="n">String</span><span class="o">)</span><span class="n">name</span><span class="o">).</span><span class="na">replaceAll</span><span class="o">(</span><span class="s">"\""</span><span class="o">,</span> <span class="s">""</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(</span><span class="n">provider</span><span class="o">.</span><span class="na">canProvideBean</span><span class="o">(</span><span class="n">beanName</span><span class="o">))</span>
<span class="k">return</span> <span class="n">provider</span><span class="o">.</span><span class="na">getBean</span><span class="o">(</span><span class="n">beanName</span><span class="o">).</span><span class="na">getClass</span><span class="o">();</span>
<span class="k">return</span> <span class="kd">super</span><span class="o">.</span><span class="na">getPropertyClass</span><span class="o">(</span><span class="n">context</span><span class="o">,</span> <span class="n">target</span><span class="o">,</span> <span class="n">name</span><span class="o">);</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">getSourceAccessor</span><span class="o">(</span><span class="n">OgnlContext</span> <span class="n">context</span><span class="o">,</span> <span class="n">Object</span> <span class="n">target</span><span class="o">,</span> <span class="n">Object</span> <span class="n">name</span><span class="o">)</span>
<span class="o">{</span>
<span class="n">IBeanProvider</span> <span class="n">provider</span> <span class="o">=</span> <span class="o">(</span><span class="n">IBeanProvider</span><span class="o">)</span><span class="n">target</span><span class="o">;</span>
<span class="n">String</span> <span class="n">beanName</span> <span class="o">=</span> <span class="o">((</span><span class="n">String</span><span class="o">)</span><span class="n">name</span><span class="o">).</span><span class="na">replaceAll</span><span class="o">(</span><span class="s">"\""</span><span class="o">,</span> <span class="s">""</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(</span><span class="n">provider</span><span class="o">.</span><span class="na">canProvideBean</span><span class="o">(</span><span class="n">beanName</span><span class="o">))</span> <span class="o">{</span>
<span class="n">Class</span> <span class="n">type</span> <span class="o">=</span> <span class="n">OgnlRuntime</span><span class="o">.</span><span class="na">getCompiler</span><span class="o">().</span><span class="na">getInterfaceClass</span><span class="o">(</span><span class="n">provider</span><span class="o">.</span><span class="na">getBean</span><span class="o">(</span><span class="n">beanName</span><span class="o">).</span><span class="na">getClass</span><span class="o">());</span>
<span class="n">ExpressionCompiler</span><span class="o">.</span><span class="na">addCastString</span><span class="o">(</span><span class="n">context</span><span class="o">,</span> <span class="s">"(("</span> <span class="o">+</span> <span class="n">type</span><span class="o">.</span><span class="na">getName</span><span class="o">()</span> <span class="o">+</span> <span class="s">")"</span><span class="o">);</span>
<span class="n">context</span><span class="o">.</span><span class="na">setCurrentAccessor</span><span class="o">(</span><span class="n">IBeanProvider</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
<span class="n">context</span><span class="o">.</span><span class="na">setCurrentType</span><span class="o">(</span><span class="n">type</span><span class="o">);</span>
<span class="k">return</span> <span class="s">".getBean("</span> <span class="o">+</span> <span class="n">name</span> <span class="o">+</span> <span class="s">"))"</span><span class="o">;</span>
<span class="o">}</span>
<span class="k">return</span> <span class="kd">super</span><span class="o">.</span><span class="na">getSourceAccessor</span><span class="o">(</span><span class="n">context</span><span class="o">,</span> <span class="n">target</span><span class="o">,</span> <span class="n">name</span><span class="o">);</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">getSourceSetter</span><span class="o">(</span><span class="n">OgnlContext</span> <span class="n">context</span><span class="o">,</span> <span class="n">Object</span> <span class="n">target</span><span class="o">,</span> <span class="n">Object</span> <span class="n">name</span><span class="o">)</span>
<span class="o">{</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nf">UnsupportedCompilationException</span><span class="o">(</span><span class="s">"Can't set beans on IBeanProvider."</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Although this example may not provide with all of the possible use cases you may need to learn to properly implement
these methods in your own <code class="highlighter-rouge">PropertyAccessor</code> implementations - the built in OGNL versions like <code class="highlighter-rouge">ObjectPropertyAccessor</code>,
<code class="highlighter-rouge">MapPropertyAccessor</code>, <code class="highlighter-rouge">ListPropertyAccessor</code>, etc should provide more than enough data to work from.</p>
<p>The most important part of the above logic you will want to look at is in how the new <code class="highlighter-rouge">OgnlContext</code> methods for setting
object/accessor types are being set:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">context</span><span class="o">.</span><span class="na">setCurrentAccessor</span><span class="o">(</span><span class="n">IBeanProvider</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
<span class="n">context</span><span class="o">.</span><span class="na">setCurrentType</span><span class="o">(</span><span class="n">type</span><span class="o">);</span>
</code></pre></div></div>
<p>This meta information is used by the <code class="highlighter-rouge">OgnlExpressionCompiler</code> to correctly cast your specific expression object types
during compilation. This process of casting/converting in to and out of native types is the most complicated part of this
new logic and also the source of the greatest number of bugs reported in the OGNL jira.</p>
<p>In this property accessor example the goal is to turn general statements like <code class="highlighter-rouge">beans.emailValidator</code> in to their pure
source form - which would look something like this when all is said and done:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">((</span><span class="n">ValidatingBean</span><span class="o">)</span><span class="n">beanProvider</span><span class="o">.</span><span class="na">getBean</span><span class="o">(</span><span class="s">"emailValidator"</span><span class="o">))</span>
</code></pre></div></div>
<p>There is also the ever important cast handling which you must do:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Class</span> <span class="n">type</span> <span class="o">=</span> <span class="n">OgnlRuntime</span><span class="o">.</span><span class="na">getCompiler</span><span class="o">().</span><span class="na">getInterfaceClass</span><span class="o">(</span><span class="n">provider</span><span class="o">.</span><span class="na">getBean</span><span class="o">(</span><span class="n">beanName</span><span class="o">).</span><span class="na">getClass</span><span class="o">());</span>
<span class="n">ExpressionCompiler</span><span class="o">.</span><span class="na">addCastString</span><span class="o">(</span><span class="n">context</span><span class="o">,</span> <span class="s">"(("</span> <span class="o">+</span> <span class="n">type</span><span class="o">.</span><span class="na">getName</span><span class="o">()</span> <span class="o">+</span> <span class="s">")"</span><span class="o">);</span>
</code></pre></div></div>
<p>In this example the <code class="highlighter-rouge">PropertyAccessor</code> is trying to determine the class type and manually adding the cast string for
the specific type to the overall statement by invoking the utility method <code class="highlighter-rouge">addCastString(OgnlContext, String)</code> on
<code class="highlighter-rouge">ExpressionCompiler</code>. In many instances of expression compilation you might also be dealing with unknown method calls,
where the more preferred way to do this kind of logic would be something like this: (taken from the OGNL
<code class="highlighter-rouge">ObjectPropertyAccessor</code> implementation)</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Method</span> <span class="n">m</span> <span class="o">=</span> <span class="o">...(</span><span class="n">various</span> <span class="n">reflection</span> <span class="n">gynamistics</span> <span class="n">used</span> <span class="n">to</span> <span class="n">find</span> <span class="n">a</span> <span class="n">java</span><span class="o">.</span><span class="na">reflect</span><span class="o">.</span><span class="na">Method</span> <span class="n">instance</span><span class="o">)</span>
<span class="n">context</span><span class="o">.</span><span class="na">setCurrentType</span><span class="o">(</span><span class="n">m</span><span class="o">.</span><span class="na">getReturnType</span><span class="o">());</span>
<span class="n">context</span><span class="o">.</span><span class="na">setCurrentAccessor</span><span class="o">(</span><span class="n">OgnlRuntime</span><span class="o">.</span><span class="na">getCompiler</span><span class="o">().</span><span class="na">getSuperOrInterfaceClass</span><span class="o">(</span><span class="n">m</span><span class="o">,</span> <span class="n">m</span><span class="o">.</span><span class="na">getDeclaringClass</span><span class="o">()));</span>
</code></pre></div></div>
<p>When dealing with method calls it is very important that you do this specific kind of type setting on the <code class="highlighter-rouge">OgnlContext</code>
class so that the casting done on your statements (which happens outside of the <code class="highlighter-rouge">ObjectPropertyAccessor</code> in this instance)
can be done on the highest level interface defining that method. This becomes important when you are dealing with expressions
that you would like to re-use against different object instances. For example, suppose we had an ognl expression like
this (for Tapestry):</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>user.firstName
</code></pre></div></div>
<p>and the object it was compiled against was an instance of something looking like this:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">abstract</span> <span class="n">LoginPage</span> <span class="kd">extends</span> <span class="n">BasePage</span> <span class="kd">implements</span> <span class="n">UserPermissions</span> <span class="o">{</span>
<span class="kd">public</span> <span class="kd">abstract</span> <span class="n">User</span> <span class="nf">getUser</span><span class="o">();</span>
<span class="o">}</span>
<span class="o">..</span>
<span class="cm">/**
* Interface for any page/component that holds references to the current system
* User.
*/</span>
<span class="kd">public</span> <span class="kd">interface</span> <span class="nc">UserPermissions</span> <span class="o">{</span>
<span class="n">User</span> <span class="nf">getUser</span><span class="o">();</span>
<span class="o">}</span>
</code></pre></div></div>
<p><code class="highlighter-rouge">BasePage</code> is a Tapestry specific class which is unimportant in this example. What is important to know is that if we
had done something like this in the previous context setting example:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">context</span><span class="o">.</span><span class="na">setCurrentType</span><span class="o">(</span><span class="n">m</span><span class="o">.</span><span class="na">getReturnType</span><span class="o">());</span>
<span class="n">context</span><span class="o">.</span><span class="na">setCurrentAccessor</span><span class="o">(</span><span class="n">m</span><span class="o">.</span><span class="na">getDeclaringClass</span><span class="o">());</span>
</code></pre></div></div>
<p>It would have resulted in a compiled expression of:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kt">void</span> <span class="nf">get</span><span class="o">(</span><span class="n">OgnlContext</span> <span class="n">context</span><span class="o">,</span> <span class="n">Object</span> <span class="n">root</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="o">((</span><span class="n">LoginPage</span><span class="o">)</span><span class="n">root</span><span class="o">).</span><span class="na">getUser</span><span class="o">();</span>
<span class="o">}</span>
</code></pre></div></div>
<p>This is undesirable in situations where you would like to re-use OGNL expressions across many different class instances
(which is what Tapestry does via the <code class="highlighter-rouge">ExpressionCacheImpl</code> listed above). The better/more re-usable compiled version
should really look like:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kt">void</span> <span class="nf">get</span><span class="o">(</span><span class="n">OgnlContext</span> <span class="n">context</span><span class="o">,</span> <span class="n">Object</span> <span class="n">root</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="o">((</span><span class="n">UserPermissions</span><span class="o">)</span><span class="n">root</span><span class="o">).</span><span class="na">getUser</span><span class="o">();</span>
<span class="o">}</span>
</code></pre></div></div>
<p>These are the more delicate parts of the compiler API that the majority of people will need to worry about during any
integration efforts.</p>
<p>## Known Issues / Limitations</p>
<h2 id="compiler-errors">Compiler Errors</h2>
<p>Despite the substantially large number of unit tests set up and thorough usage of many different
types of expressions Tapestry users are still currently running in to fatal/non caught runtime errors when some of their
OGNL expressions are compiled. In some instances these errors are blockers and they must either wait for someone
to fix the bug (after being posted to <a href="http://jira.opensymphony.com/browse/OGNL">http://jira.opensymphony.com/browse/OGNL</a>
correctly) or re-work their expression to get around the error. I (jesse) generally try to fix these reported errors
within a day or two (or sooner) when I can and immediately deploy the fixes to the OGNL snapshot maven2 repository.<br />
This doesn’t mean that the vast majority of expressions won’t compile fine, but it is something to keep in mind when
you decide how to integrate the compiler logic in to your own framework.</p>
<h2 id="compile-vs-normal-expression-evaluation">Compile vs. normal expression evaluation</h2>
<p>The current Tapestry implementation compiles OGNL expressions in both development AND production modes. This has
the undesirable side effect of causing needless multiple method invocations on objects when compiling as well as the general
overhead of performing compilations at all when people are just developing applications and not serving them in production
environments. It is hoped that when OGNL becomes final this special development mode can go back to using normal OGNL
expression evaluation during development and save compilation for production environments, but until then we’ve been
worried about giving people false positives when testing their applications. Meaning - something may evaluate just fine
when using</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Ognl</span><span class="o">.</span><span class="na">getValue</span><span class="o">(</span><span class="n">OgnlContext</span><span class="o">,</span> <span class="n">Object</span> <span class="n">root</span><span class="o">,</span> <span class="n">String</span> <span class="n">expression</span>
</code></pre></div></div>
<p>but fail completely when they deploy their app to production and the compiler kicks in. If you framework doesn’t handle
separate modes or have this kind of state set up it is something to keep in mind. The number of JIRA issues reported
has gone way down since this all started but they do still trickle in which is enough to know that things aren’t yet 100%
reliable. I’m sure the plethora of Struts/WebWork/etc users available should be enough to iron out any remaining issues
found but it’s something to keep in mind.</p>
<h2 id="snapshot-repository">Snapshot Repository</h2>
<p>The current maven2 location of the OGNL development/snapshot release are all made to <a href="http://opencomponentry.com/repository/m2-snapshot-repo/">http://opencomponentry.com/repository/m2-snapshot-repo/</a>,
while releases go out to ibiblio as per normal. If someone has a better place for these release to be made please feel free to contact jesse ( jkuhnert at gmail.com) with accessor information / instructions.</p>
</section>
</article>
<footer class="container">
<div class="col-md-12">
Copyright &copy; 2000-2018 <a href="http://www.apache.org/">The Apache Software Foundation </a>.
All Rights Reserved.
</div>
<div class="col-md-12">
Apache Struts, Struts, Apache, the Apache feather logo, and the Apache Struts project logos are
trademarks of The Apache Software Foundation.
</div>
<div class="col-md-12">Logo and website design donated by <a href="https://softwaremill.com/">SoftwareMill</a>.</div>
</footer>
<script>!function (d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (!d.getElementById(id)) {
js = d.createElement(s);
js.id = id;
js.src = "//platform.twitter.com/widgets.js";
fjs.parentNode.insertBefore(js, fjs);
}
}(document, "script", "twitter-wjs");</script>
<script src="https://apis.google.com/js/platform.js" async="async" defer="defer"></script>
<div id="fb-root"></div>
<script>(function (d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return;
js = d.createElement(s);
js.id = id;
js.src = "//connect.facebook.net/en_GB/all.js#xfbml=1";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));</script>
</body>
</html>