| <!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"><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">/></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 © 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> |