http://git-wip-us.apache.org/repos/asf/struts-site/blob/fab964f2/content/tag-developers/index.html ---------------------------------------------------------------------- diff --git a/content/tag-developers/index.html b/content/tag-developers/index.html new file mode 100644 index 0000000..7a8d8e1 --- /dev/null +++ b/content/tag-developers/index.html @@ -0,0 +1,8428 @@ +<!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>Tag Developers Guide (WIP)</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="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.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="/downloads.html">Downloads</a></li> + <li><a href="/announce.html">Announcements</a></li> + <li><a href="http://www.apache.org/licenses/">License</a></li> + <li><a href="http://apache.org/foundation/thanks.html">Thanks!</a></li> + <li><a href="http://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="/maven/project-info.html">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 class="divider"></li> + <li><a href="/maven/struts2-core/apidocs/index.html">Struts Core API</a></li> + <li><a href="/docs/plugins.html">Plugin APIs</a></li> + <li><a href="/docs/tag-reference.html">Tag reference</a></li> + <li><a href="http://cwiki.apache.org/S2PLUGINS/home.html">Plugin registry</a></li> + <li class="divider"></li> + <li><a href="/docs/tutorials.html">Tutorials - DEPRECATED</a></li> + <li><a href="/docs/faqs.html">FAQs - DEPRECATED</a></li> + <li><a href="/docs/guides.html">Guides - DEPRECATED</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 class="divider"></li> + <li><a href="/submitting-patches.html">Submitting patches</a></li> + <li><a href="/builds.html">Source Code</a></li> + <li><a href="/coding-standards.html">Coding standards</a></li> + <li class="divider"></li> + <li><a href="/releases.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://git-wip-us.apache.org/repos/asf?p=struts.git">Source Repository</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/index.md" title="Edit this page on GitHub">Edit on GitHub</a> + <p>#Tag Developers Guide# {#PAGE_14324}</p> + +<p>The framework offers a flexible view layer that supports multiple view technologies, including JSP, FreeMaker, and Velocity.</p> + +<table> + <tbody> + <tr> + </tr> + </tbody> +</table> + +<table> + <tbody> + <tr> + </tr> + </tbody> +</table> + +<p>| |\ +\ ++ <a href="#PAGE_14248">Struts Tags</a>\ +\ + + <a href="#PAGE_19745">Generic Tags</a>\ +\ + + <a href="#PAGE_19736">UI Tags</a>\ +\ + + <a href="#PAGE_14247">Themes and Templates</a>\ +\ + + <a href="#PAGE_19705">Tag Reference</a>\ +\ + + <a href="#PAGE_31510">Ajax Tags</a>\ +\ + + <a href="#PAGE_56182">Ajax and JavaScript Recipes</a>\ +\ ++ <a href="#PAGE_14198">OGNL</a>\ +\ ++ <a href="#PAGE_13927">Tag Syntax</a>\ +\ ++ <em>Alt Syntax</em> \ +|\ +\ ++ <a href="#PAGE_14141">JSP</a>\ +\ + + <a href="#PAGE_13973">specific tags</a>\ +\ ++ <a href="#PAGE_14078">FreeMarker</a>\ +\ + + <a href="#PAGE_14294">specific tags</a>\ +\ ++ <a href="#PAGE_13894">Velocity</a>\ +\ + + <a href="#PAGE_13950">specific tags</a>\ +| +|ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ-|âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ|</p> + +<table> + <tbody> + <tr> + </tr> + </tbody> +</table> + +<p>###Next:###</p> + +<p>##FreeMarker## {#PAGE_14078}</p> + +<p>FreeMarker is a Java-based template engine that is a great alternative to <a href="#PAGE_14141">JSP</a>. FreeMarker is ideal for situations where your action results can possibly be loaded from outside a Servlet container. For example, if you wished to support plugins in your application, you might wish to use FreeMarker so that the plugins could provide the entire action class and view in a single jar that is loaded from the classloader.</p> + +<p>For more information on FreeMarker itself, please visit the <a href="http://freemarker\.sourceforge\.net/">FreeMarker website</a>^[http://freemarker.sourceforge.net/].</p> + +<table> + <tbody> + <tr> + <td>The framework utilizes FreeMarker because the engine includes strong error reporting, built-in internationalization and powerful macro libraries.</td> + </tr> + </tbody> +</table> + +<table> + <tbody> + <tr> + </tr> + </tbody> +</table> + +<table> + <tbody> + <tr> + </tr> + </tbody> +</table> + +<table> + <tbody> + <tr> + <td>Support is also included for <a href="#PAGE_13894">Velocity</a> templates. For a comparison of Velocity vs FreeMarker see <a href="http://freemarker\.org/fmVsVel\.html">here</a>^[http://freemarker.org/fmVsVel.html].</td> + </tr> + </tbody> +</table> + +<table> + <tbody> + <tr> + </tr> + </tbody> +</table> + +<p>####Getting Started####</p> + +<p>Getting started with FreeMarker is as simple as ensuring all the dependencies are included in your projectâs classpath. Typically, the only dependency is</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>freemarker.jar +</code></pre> +</div> +<p>. Other than that, <em>struts-default.xml</em> already configures the <em>FreeMarker Result</em> needed to process your applicationâs templates.</p> + +<div class="highlighter-rouge"><pre class="highlight"><code><action name="test" class="com.acme.TestAction"> + <result name="success" type="freemarker">test-success.ftl</result> +</action> + +</code></pre> +</div> + +<p>Then in</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>test-success.ftl +</code></pre> +</div> +<p>:</p> + +<div class="highlighter-rouge"><pre class="highlight"><code><span class="nt"><html></span> +<span class="nt"><head></span> + <span class="nt"><title></span>Hello<span class="nt"></title></span> +<span class="nt"></head></span> +<span class="nt"><body></span> + +Hello, ${name} + +<span class="nt"></body></span> +<span class="nt"></html></span> + +</code></pre> +</div> + +<p>Where</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>name +</code></pre> +</div> +<p>is a property on your action. Thatâs it! Read the rest of this document for details on how templates are loaded, variables are resolved, and tags can be used.</p> + +<p>####Servlet / JSP Scoped Objects####</p> + +<p>The following are ways to obtained Application scope attributes, Session scope attributes, Request scope attributes, Request parameters, and framework Context scope parameters:-</p> + +<p>#####Application Scope Attribute#####</p> + +<p>Assuming thereâs an attribute with name</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>myApplicationAttribute +</code></pre> +</div> +<p>in the Application scope.</p> + +<div class="highlighter-rouge"><pre class="highlight"><code><#if Application.myApplicationAttribute?exists> + ${Application.myApplicationAttribute} +</#if> + +</code></pre> +</div> + +<p>or</p> + +<div class="highlighter-rouge"><pre class="highlight"><code><@s.property value="%{#application.myApplicationAttribute}" /> + +</code></pre> +</div> + +<p>#####Session Scope Attribute#####</p> + +<p>Assuming thereâs an attribute with name</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>mySessionAttribute +</code></pre> +</div> +<p>in the Session scope.</p> + +<div class="highlighter-rouge"><pre class="highlight"><code><#if Session.mySessionAttribute?exists> + ${Session.mySessionAttribute} +</#if> + +</code></pre> +</div> + +<p>or</p> + +<div class="highlighter-rouge"><pre class="highlight"><code><@s.property value="%{#session.mySessionAttribute}" /> + +</code></pre> +</div> + +<p>#####Request Scope Attribute#####</p> + +<p>Assuming thereâs an attribute with name âmyRequestAttributeâ in the Request scope.</p> + +<div class="highlighter-rouge"><pre class="highlight"><code><#if Request.myRequestAttribute?exists> + ${Request.myRequestAttribute} +</#if> + +</code></pre> +</div> + +<p>or</p> + +<div class="highlighter-rouge"><pre class="highlight"><code><@s.property value="%{#request.myRequestAttribute}" /> + +</code></pre> +</div> + +<p>#####Request Parameter#####</p> + +<p>Assuming thereâs a request parameter myParameter (eg. <a href="http://host/myApp/myAction\.action?myParameter=one">http://host/myApp/myAction.action?myParameter=one</a>).</p> + +<div class="highlighter-rouge"><pre class="highlight"><code><#if Parameters.myParameter?exists> + ${Parameters.myParameter} +</#if> + +</code></pre> +</div> + +<p>or</p> + +<div class="highlighter-rouge"><pre class="highlight"><code><@s.property value="%{#parameters.myParameter}" /> + +</code></pre> +</div> + +<p>#####Context parameter#####</p> + +<p>Assuming thereâs a parameter with the name myContextParam in framework context.</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>${stack.findValue('#myContextParam')} + +</code></pre> +</div> + +<p>or</p> + +<div class="highlighter-rouge"><pre class="highlight"><code><@s.property value="%{#myContextParam}" /> + +</code></pre> +</div> + +<p>####Template Loading####</p> + +<p>The framework looks for FreeMarker templates in two locations (in this order):</p> + +<ol> + <li> + <p>Web application</p> + </li> + <li> + <p>Class path</p> + </li> +</ol> + +<p>This ordering makes it ideal for providing templates inside a fully-built jar, but allowing for overrides of those templates to be defined in your web application. In fact, this is how you can override the default UI tags and <a href="#PAGE_14230">Form Tags</a> included with the framework.</p> + +<p>In addition, you can specify a location (directory on your file system) through the</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>templatePath +</code></pre> +</div> +<p>or</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>TemplatePath +</code></pre> +</div> +<p>context variable (in the</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>web.xml +</code></pre> +</div> +<p>). If a variable is specified, the content of the directory it points to will be searched first.</p> + +<table> + <tbody> + <tr> + <td>This variable is currently NOT relative to the root of your application.</td> + </tr> + </tbody> +</table> + +<table> + <tbody> + <tr> + </tr> + </tbody> +</table> + +<p>####Variable Resolution####</p> + +<p>When using FreeMarker with the framework, variables are looked up in several different places, in this order:</p> + +<ol> + <li> + <p>Built-in variables</p> + </li> + <li> + <p>Value stack</p> + </li> + <li> + <p>Action context</p> + </li> + <li> + <p>Request scope</p> + </li> + <li> + <p>Session scope</p> + </li> + <li> + <p>Application scope</p> + </li> +</ol> + +<p>Note that the action context is looked up after the value stack. This means that you can reference the variable without the typical preceding has marker (#) like you would have to when using the JSP</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>s:property +</code></pre> +</div> +<p>tag. This is a nice convenience, though be careful because there is a small chance it could trip you up.</p> + +<div class="highlighter-rouge"><pre class="highlight"><code><@s.url id="url" value="http://www.yahoo.com"/> +Click <a xhref="${url}">here</a>! + +</code></pre> +</div> + +<p>The built-in variables that Struts-FreeMarker integration provides are:</p> + +<table> + <thead> + <tr> + <th>Name</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>stack</td> + <td>The value stack itself, useful for calls like ${stack.findString(âognl exprâ)}</td> + </tr> + <tr> + <td>action</td> + <td>The action most recently executed</td> + </tr> + <tr> + <td>response</td> + <td>The HttpServletResponse</td> + </tr> + <tr> + <td>res</td> + <td>Same as response</td> + </tr> + <tr> + <td>request</td> + <td>The HttpServletRequest</td> + </tr> + <tr> + <td>req</td> + <td>Same as request</td> + </tr> + <tr> + <td>session</td> + <td>The HttpSession</td> + </tr> + <tr> + <td>application</td> + <td>The ServletContext</td> + </tr> + <tr> + <td>base</td> + <td>The requestâs context path</td> + </tr> + </tbody> +</table> + +<p>####Tag Support####</p> + +<p>FreeMarker includes complete tag support. See the <a href="#PAGE_14294">FreeMarker Tags</a> documentation for information on how to use the generic <a href="#PAGE_14248">Struts Tags</a> provided by Struts. In addition to this, you can use any JSP tag, like so:</p> + +<div class="highlighter-rouge"><pre class="highlight"><code><#assign mytag=JspTaglibs["/WEB-INF/mytag.tld"]> +<@mytag.tagx attribute1="some ${value}"/> + +</code></pre> +</div> + +<p>Where <strong>mytag.tld</strong> is the JSP Tag Library Definition file for your tag library. Note: in order to use this support in FreeMarker, you must enable the</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>JSPSupportServlet +</code></pre> +</div> +<p>in</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>web.xml +</code></pre> +</div> +<p>:</p> + +<div class="highlighter-rouge"><pre class="highlight"><code><servlet> + <servlet-name>JspSupportServlet</servlet-name> + <servlet-class>org.apache.struts2.views.JspSupportServlet</servlet-class> + <load-on-startup>1</load-on-startup> +</servlet> + +</code></pre> +</div> + +<p>####Tips and Tricks####</p> + +<p>There are some advanced features that may be useful when building Struts applications with FreeMarker.</p> + +<p>#####Type Conversion and Locales#####</p> + +<p>FreeMarker has built in support for formatting dates and numbers. The formatting rules are based on the locale associated with the action request, which is by default set in <em>struts.properties</em> but can be over-ridden using the <em>I18n Interceptor</em> . This is normally perfect for your needs, but it is important to remember that these formatting rules are handled by FreeMarker and not by the frameworkâs <em>Type Conversion</em> support.</p> + +<p>If you want the framework to handle the formatting according to the <em>Type Conversion</em> you have specified, you shouldnât use the normal ${...} syntax. Instead, you should use the <a href="#PAGE_13960">property</a> tag. The difference is that the property tag is specifically designed to take an <a href="#PAGE_14198">OGNL</a> expression, evaluate it, and then convert it to a String using any <em>Type Conversion</em> rules you have specified. The normal ${...} syntax will use a FreeMarker expression language, evaluate it, and then convert it to a String using the built in formatting rules.</p> + +<p>(!) The difference in how type conversion is handled under Freemarker is subtle but important to understand.</p> + +<p>#####Extending#####</p> + +<p>Sometimes you may with to extend the frameworkâs FreeMarker support. For example, you might want to extend the Struts tags that come bundled with the framework.</p> + +<p>To extend the Freemarker support, develop a class that extends</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>org.apache.struts2.views.freemarker.FreemarkerManager +</code></pre> +</div> +<p>, overriding methods as needed, and plugin the class through the <em>struts.properties</em> :</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>struts.freemarker.manager.classname = com.yourcompany.YourFreeMarkerManager + +</code></pre> +</div> + +<p>#####ObjectWrapper Settings#####</p> + +<p>Once you get familiar with FreeMarker, you will find certain <em>subtleties</em> with it that may become frustrating. The most common thing youâll likely run in to is the BeansWrapper provided by FreeMarker. If you donât know what this is, donât worry. However, if you do, know this:</p> + +<div class="highlighter-rouge"><pre class="highlight"><code><span class="p">{</span><span class="err">snippet:id=javadoc|javadoc=true|url=org.apache.struts2.views.freemarker.StrutsBeanWrapper</span><span class="p">}</span><span class="w"> +</span></code></pre> +</div> + +<p>#####Syntax Notes#####</p> + +<p>As of FreeMarker 2.3.4, an alternative syntax is supported. This alternative syntax is great if you find that your IDE (especially IntelliJ IDEA) makes it difficult to work with the default syntax. You can read more about this syntax <a href="http://freemarker\.sourceforge\.net/docs/dgui\_misc\_alternativesyntax\.html">here</a>^[http://freemarker.sourceforge.net/docs/dgui_misc_alternativesyntax.html].</p> + +<p>#####Cache#####</p> + +<p>You can enable FreeMarker cache mechanism by specifying below options in struts.xml:</p> + +<ul> + <li></li> +</ul> + +<div class="highlighter-rouge"><pre class="highlight"><code><constant name="struts.freemarker.mru.max.strong.size" value="250" /> +</code></pre> +</div> +<p>- this option will be used by <a href="http://freemarker\.org/docs/api/freemarker/cache/MruCacheStorage\.html">freemarker.cache.MruCacheStorage</a>^[http://freemarker.org/docs/api/freemarker/cache/MruCacheStorage.html]</p> + +<ul> + <li></li> +</ul> + +<div class="highlighter-rouge"><pre class="highlight"><code><constant name="struts.freemarker.templatesCache.updateDelay" value="1800" /> +</code></pre> +</div> +<p>- default update cache interval (5 seconds)</p> + +<ul> + <li></li> +</ul> + +<div class="highlighter-rouge"><pre class="highlight"><code><constant name="struts.freemarker.templatesCache" value="true" /> +</code></pre> +</div> +<p>- *<strong>DEPRECATED</strong>* this option will use a internal ConcurrentHashMap in FreemarkerTemplateEngine but not freemarker native cache</p> + +<p>Setting</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>devMode +</code></pre> +</div> +<p>to true will disable cache and updateDelay immediately, but you can explicit specify these constants to enable cache even in</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>devMode +</code></pre> +</div> +<p>, see <em>devMode</em></p> + +<p>#####Incompatible Improvements#####</p> + +<p>By default Struts is using FreeMarker in way to be backward compatible as much as possible but if you need to enable new features you can do it via </p> + +<div class="highlighter-rouge"><pre class="highlight"><code>freemarker.properties +</code></pre> +</div> +<p>by defining <a href="http://freemarker\.org/docs/pgui\_config\_incompatible\_improvements\.html\#pgui\_config\_incompatible\_improvements\_how\_to\_set">incompatible improvements</a>^[http://freemarker.org/docs/pgui_config_incompatible_improvements.html#pgui_config_incompatible_improvements_how_to_set] settings, ie.:</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>incompatible_improvements=2.3.22 +</code></pre> +</div> + +<p>You can also pass this setting via </p> + +<div class="highlighter-rouge"><pre class="highlight"><code>ServletContext +</code></pre> +</div> +<p> </p> + +<div class="highlighter-rouge"><pre class="highlight"><code><init-param/> (since Struts 2.5.13): +</code></pre> +</div> + +<div class="highlighter-rouge"><pre class="highlight"><code><init-param> + <param-name>freemarker.incompatible_improvements</param-name> + <param-value>2.3.22</param-value> +</init-param> +</code></pre> +</div> + +<p>This can impact your freemarker powered pages and Struts tags as well, so please careful test this change.</p> + +<p>####Next:####</p> + +<p>##FreeMarker Tags## {#PAGE_14294}</p> + +<p>FreeMarker tags are extensions of the generic <a href="#PAGE_14248">Struts Tags</a> provided by the framework. You can jump right in just by knowing the generic structure in which the tags can be accessed:</p> + +<div class="highlighter-rouge"><pre class="highlight"><code><@s.tag> ...</@s.tag> +</code></pre> +</div> +<p>, where <em>tag</em> is any of the <a href="#PAGE_14248">tags</a> supported by the framework.</p> + +<p>For example, in JSP you might create a form using Struts tags.</p> + +<p><strong>JSP Form</strong></p> + +<div class="highlighter-rouge"><pre class="highlight"><code> +<s:form action="updatePerson"> + <s:textfield label="First name" name="firstName"/> + <s:submit value="Update"/> +</s:form> + +</code></pre> +</div> + +<p>In FreeMarker the same form can also be built using Struts tags.</p> + +<p><strong>FTL Form</strong></p> + +<div class="highlighter-rouge"><pre class="highlight"><code> +<@s.form action="updatePerson"> + <@s.textfield label="First name" name="firstName"/> + <@s.submit value="Update"/> +</@s.form> + +</code></pre> +</div> + +<p><em>But, wait thereâs more!</em></p> + +<p>Aside from doing everything that the JSP tags do, the FTL tags boast additional features that you can use to make your pages even easier to code. You can even invoke third-party JSP taglibs as if there were native FTL tags.</p> + +<p>####Attributes and Parameters####</p> + +<p>Unlike older versions of JSP (in which the <a href="#PAGE_13973">JSP Tags</a> are based), FreeMarker allows for <em>dynamic attributes</em> , much like JSP 2.0. You can supply attributes to the tags that the tag doesnât explicitedly support. Those attributes that cannot be applied directly to the tag object will be set to the tagâs general-purpose</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>parameters +</code></pre> +</div> +<p>Map.</p> + +<p>Suppose we wanted to build an URL in a JSP. The URL needs to take an arbitary parameter to the query string, that (being arbitary) isnât specified on the URL tag. In a JSP, weâd have to use the</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>url +</code></pre> +</div> +<p>and</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>param +</code></pre> +</div> +<p>tags together.</p> + +<p><strong>Creating a URL with a query string (JSP)</strong></p> + +<div class="highlighter-rouge"><pre class="highlight"><code> +<s:url value="somePage"> + <s:param name="personId" value="%{personId}"/> +</s:url> + +</code></pre> +</div> + +<p>In FreeMarker, we can pass the arbitrary parameter directly and create the URL in one simple statement.</p> + +<p><strong>Creating a URL with a query string (FTL)</strong></p> + +<div class="highlighter-rouge"><pre class="highlight"><code> +<@s.url value="somePage" personId="${personId}"/> + +</code></pre> +</div> + +<p>#####Using inline attributes with templates#####</p> + +<p>Suppose you created a âthree columnâ theme to replace the typical two column theme (xhtml). You might want an additional parameter to display in the third column called âdescriptionâ. Using FreeMarker, you can just pop the description attribute into the textfield tag, no fuss, no muss.</p> + +<p><strong>Passing an attribute to the template</strong></p> + +<div class="highlighter-rouge"><pre class="highlight"><code> +<@s.form action="updatePerson"> + <@s.textfield label="First name" name="firstName" description="..."/> + <@s.submit value="Update"/> +</@s.form> + +</code></pre> +</div> + +<p>In the new template, the description is referenced via the parameters Map: â${parameters.description}â.</p> + +<table> + <tbody> + <tr> + <td>For simple cases, inline attributes are much easier to use than the param} tag. But, the {{param} tag is more flexible than inline attributes for advanced use cases. For example, {{param can take the entire body of the tag and apply that as the <em>value</em> attribute.</td> + </tr> + </tbody> +</table> + +<table> + <tbody> + <tr> + </tr> + </tbody> +</table> + +<p>####Attribute Types####</p> + +<p>Remember that all tag attributes must first be set as Strings - they are then later evaluated (using <a href="#PAGE_14198">OGNL</a>) to a different type, such as List, int, or boolean. This generally works just fine, but it can be limiting when using FreeMarker which provides more advanced ways to apply attributes. Suppose the following example:</p> + +<div class="highlighter-rouge"><pre class="highlight"><code> +<@s.select label="Foo label - ${foo}" name="${name}" list="%1"/> + +</code></pre> +</div> + +<p>What will happen here is that each attribute will be evaluated to a String as best it can. This may involve calling the</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>toString +</code></pre> +</div> +<p>method on the internal FreeMarker objects. In this case, all objects will end up being exactly what you would expect. Then, when the tag runs, the</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>list +</code></pre> +</div> +<p>attribute will be converted from a String to a List using <a href="#PAGE_14198">OGNL</a>âs advanced collection support.</p> + +<p>But suppose you wish to use FreeMarkerâs list or hash support instead? You can do this:</p> + +<div class="highlighter-rouge"><pre class="highlight"><code> +<@s.select label="Foo label - ${foo}" name="${name}" list=[1, 2, 3]/> + +</code></pre> +</div> + +<p>Notice that the list attribute no longer has quotes around it. Now it will come in to the tag as an object that canât easily be converted to a String. Normally, the tag would just call</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>toString +</code></pre> +</div> +<p>, which would return â[1, 2, 3]â and be unable to be converted back to a List by OGNL. Rather than go through all this back and forth, the frameworksâs FreeMarker tag support will recognize collections and not pass them through the normal tag attribute. Instead, the framework will set them directly in the</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>parameters +</code></pre> +</div> +<p>Map, ready to be consumed by the template.</p> + +<p>In the end, everything tends to do what you would expect, but it can help to understand the difference of when OGNL is being used and when it isnât, and how attribute types get converted.</p> + +<p>####JSP Tag Support####</p> + +<p>While the framework provides native FreeMarker Tags, you might wish to use other third-party tags that are only available for JSP. Fortunately, FreeMarker has the ability to run JSP tags. To do so, you must include the JspSupportServlet in the applicationâs</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>web.xml +</code></pre> +</div> +<p>, as this allows the FreeMarker integration to get access to the required objects needed to emulate a JSP taglib container.</p> + +<p><strong>Adding JspSupportSerlvet to web.xml</strong></p> + +<div class="highlighter-rouge"><pre class="highlight"><code> +<servlet> + <servlet-name>JspSupportServlet</servlet-name> + <servlet-class>org.apache.struts2.views.JspSupportServlet</servlet-class> + <load-on-startup>1</load-on-startup> +</servlet> + +</code></pre> +</div> + +<p>Once youâve done that, you can simply add something like this in your templates:</p> + +<div class="highlighter-rouge"><pre class="highlight"><code> +<#assign cewolf=JspTaglibs["/WEB-INF/cewolf.tld"] /> +... +<@cewold.xxx ... /> + +</code></pre> +</div> + +<p>####Next:####</p> + +<p>##JSP## {#PAGE_14141}</p> + +<p>The default configuration (<em>struts-default.xml</em> ) configures the <em>Dispatcher Result</em> as the default result, which works well with JavaServer Pages. Any JSP 1.2+ container can work with Struts 2 JSP tags immediately.</p> + +<p>####Getting Started####</p> + +<p>Because JSP support occurs through the <em>Dispatcher Result</em> , which is the default result type, you donât need to specify the type attribute when configuring <em>struts.xml</em> :</p> + +<div class="highlighter-rouge"><pre class="highlight"><code> +<action name="test" class="com.acme.TestAction"> + <result name="success">test-success.jsp</result> +</action> + +</code></pre> +</div> + +<p>Then in <strong>test-success.jsp</strong>:</p> + +<div class="highlighter-rouge"><pre class="highlight"><code> +<span class="err"><</span>%@ taglib prefix="s" uri="/struts-tags" %> + +<span class="nt"><html></span> +<span class="nt"><head></span> + <span class="nt"><title></span>Hello<span class="nt"></title></span> +<span class="nt"></head></span> +<span class="nt"><body></span> + +Hello, <span class="nt"><s:property</span> <span class="na">value=</span><span class="s">"name"</span><span class="nt">/></span> + +<span class="nt"></body></span> +<span class="nt"></html></span> + +</code></pre> +</div> + +<p>Where <strong>name</strong> is a property on your action. Thatâs it!</p> + +<p>####Servlet / JSP Scoped Objects####</p> + +<p>The following are ways to obtain Application scope attributes, Session scope attributes, Request scope attributes, Request parameters and framework Context scope parameters:-</p> + +<p>#####Application Scope Attribute#####</p> + +<p>Assuming thereâs an attribute with name âmyApplicationAttributeâ in the Application scope.</p> + +<div class="highlighter-rouge"><pre class="highlight"><code> +<s:property value="%{#application.myApplicationAttribute}" /> + +</code></pre> +</div> + +<p>#####Session Scope Attribute#####</p> + +<p>Assuming thereâs an attribute with name âmySessionAttributeâ in the Session scope.</p> + +<div class="highlighter-rouge"><pre class="highlight"><code> +<s:property value="%{#session.mySessionAttribute}" /> + +</code></pre> +</div> + +<p>#####Request Scope Attribute#####</p> + +<p>Assuming thereâs an attribute with name âmyRequestAttributeâ in the Request scope.</p> + +<div class="highlighter-rouge"><pre class="highlight"><code> +<s:property value="%{#request.myRequestAttribute}" /> + +</code></pre> +</div> + +<p>#####Request Parameter#####</p> + +<p>Assuming thereâs a request parameter myParameter (e.g. <a href="http://host/myApp/myAction\.action?myParameter=one">http://host/myApp/myAction.action?myParameter=one</a>).</p> + +<div class="highlighter-rouge"><pre class="highlight"><code> +<s:property value="%{#parameters.myParameter}" /> + +</code></pre> +</div> + +<p>#####Context Scope Parameter#####</p> + +<p>Assuming thereâs a parameter with the name myContextParam in our context.</p> + +<div class="highlighter-rouge"><pre class="highlight"><code> +<s:property value="%{#myContextParam}" /> + +</code></pre> +</div> + +<p>####Tag Support####</p> + +<p>See the <a href="#PAGE_13973">JSP Tags</a> documentation for information on how to use the generic <a href="#PAGE_14248">Struts Tags</a> provided by the framework.</p> + +<p>####Exposing the ValueStack####</p> + +<p>There are a couple of ways to obtain <em>access to ValueStack from JSPs</em> .</p> + +<p>####Next:####</p> + +<p>###Access to ValueStack from JSPs### {#PAGE_14315}</p> + +<p>To access the ValueStack from third-party JSP taglibs, expose property values to JSP using the</p> + +<div class="highlighter-rouge"><pre class="highlight"><code><s:set +</code></pre> +</div> +<p>tag.</p> + +<p><strong>Set a request scoped parameter âaâ to list of integers</strong></p> + +<div class="highlighter-rouge"><pre class="highlight"><code><s:set name="'a'" value="{ 1, 2, 3, 4 }" scope="request"/> + +</code></pre> +</div> + +<p>After setting parameters, third-party JSP taglibs can access variables or use JSP 2.0 EL (Expression Language). This is convenient as short hand EL expression syntax</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>${expression +</code></pre> +</div> +<p>}</p> + +<p>can be used in a text or inside of tag attributes:</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>a[0] = ${a[0]} + +<sample:tag value="${a[1]}"/> + +</code></pre> +</div> + +<p>In practice, several variables must be exposed to make effective use of third party taglibs like <a href="http://displaytag\.sourceforge\.net/11/">DisplayTag</a>^[http://displaytag.sourceforge.net/11/]. Unfortunately, this approach leads to a lot of</p> + +<div class="highlighter-rouge"><pre class="highlight"><code><ww:set/> +</code></pre> +</div> +<p>tags.</p> + +<table> + <tbody> + <tr> + <td>Unfortunately, it isnât that simple. we tinkered with JSPFactory.setDefault() to wrap around getPageContext() and create ExpressionEvaluator that would use OGNL. This strategy works in practice, but code generated by Jasper2 doesnât call JSPFactory.getPageContext().getExpressionEvaluator() but goes directly to static method that is hardwired to Jakarta Commons-EL implementation.</td> + </tr> + </tbody> +</table> + +<table> + <tbody> + <tr> + </tr> + </tbody> +</table> + +<table> + <tbody> + <tr> + </tr> + </tbody> +</table> + +<table> + <tbody> + <tr> + <td>Even if this approach did work, it wouldnât be <em>clean</em> as JSPFactory.setDefault() should only be called by JSP implementation.</td> + </tr> + </tbody> +</table> + +<table> + <tbody> + <tr> + </tr> + </tbody> +</table> + +<p>There is a simple, if not elegant, solution available in JSP 2.0 EL, for exposing ValueStack to OGNL. It is possible to create custom functions that can be called from EL expressions. Functions have to be âpublic staticâ and specified in a TLD file. + To use a function, import the TLD in a JSP file where youâve want to use a function. For example, you could access Action properties by evaluating OGNL expression by a function âvsâ (for valuestack) in EL.</p> + +<div class="highlighter-rouge"><pre class="highlight"><code><%@ taglib uri="/WEB-INF/tld/wwel.tld" prefix="x" %> + +a[0] = ${x:vs('a[0]')} +a[0] * 4 = ${x:vs('a[0] * 4')} + +Current action name: ${x:name()} +Top of ValueStack: ${x:top()} + +</code></pre> +</div> + +<p>To use this code youâve got to add</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>wwel.tld +</code></pre> +</div> +<p>and</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>Functions.java +</code></pre> +</div> +<p>to your webapp project.</p> + +<table> + <tbody> + <tr> + <td>If someone were interested, it would be helpful for a developer (like you!) to define a set of functions that we could include in a future release of the framework.</td> + </tr> + </tbody> +</table> + +<table> + <tbody> + <tr> + </tr> + </tbody> +</table> + +<p><strong>wwel.tld</strong></p> + +<div class="highlighter-rouge"><pre class="highlight"><code><span class="cp"><?xml version="1.0"?></span> +<span class="nt"><taglib</span> <span class="na">xmlns=</span><span class="s">"http://java.sun.com/xml/ns/j2ee"</span> + <span class="na">xmlns:xsi=</span><span class="s">"http://www.w3.org/2001/XMLSchema-instance"</span> + <span class="na">xsi:schemaLocation=</span><span class="s">"http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"</span> + <span class="na">version=</span><span class="s">"2.0"</span><span class="nt">></span> + +<span class="nt"><description></span> +This taglib enables access to the ValueStack +from JSP 2.0 Expression Language +<span class="nt"></description></span> + +<span class="nt"><tlib-version></span>1.0<span class="nt"></tlib-version></span> + +<span class="nt"><short-name></span>wwel<span class="nt"></short-name></span> + +<span class="nt"><function></span> + <span class="nt"><name></span>vs<span class="nt"></name></span> + <span class="nt"><function-class></span>com.nmote.wwel.Functions<span class="nt"></function-class></span> + <span class="nt"><function-signature></span> + java.lang.Object findOnValueStack(java.lang.String) + <span class="nt"></function-signature></span> +<span class="nt"></function></span> + +<span class="nt"><function></span> + <span class="nt"><name></span>name<span class="nt"></name></span> + <span class="nt"><function-class></span>com.nmote.wwel.Functions<span class="nt"></function-class></span> + <span class="nt"><function-signature></span> + java.lang.Object getActionName() + <span class="nt"></function-signature></span> +<span class="nt"></function></span> + +<span class="nt"><function></span> + <span class="nt"><name></span>top<span class="nt"></name></span> + <span class="nt"><function-class></span>com.nmote.wwel.Functions<span class="nt"></function-class></span> + <span class="nt"><function-signature></span> + java.lang.Object getTopOfValueStack() + <span class="nt"></function-signature></span> +<span class="nt"></function></span> + +<span class="nt"></taglib></span> + +</code></pre> +</div> + +<p><strong>Functions.java</strong></p> + +<div class="highlighter-rouge"><pre class="highlight"><code>package com.nmote.wwel; + +import com.opensymphony.xwork.ActionContext; + +/** + * Utility functions for accessing value stack and action context + * from JSP 2.0 EL taglibs. + */ +public class Functions { + + public static Object findOnValueStack(String expr) { + ActionContext a = ActionContext.getContext(); + Object value = a.getValueStack().findValue(expr); + return value; + } + + public static Object getTopOfValueStack() { + ActionContext a = ActionContext.getContext(); + Object value = a.getValueStack().peek(); + return value; + } + + public static Object getActionName() { + ActionContext a = ActionContext.getContext(); + Object value = a.getName(); + return value; + } +} + +</code></pre> +</div> + +<p>##JSP Tags## {#PAGE_13973}</p> + +<p>JSP tags are extensions of the generic tags provided by the framework. You can get started almost immediately by simply knowing the generic structure in which the tags can be accessed: <s:tag> ... </s:tag>, where tag is any of the tags supported by the framework.</p> + +<p>####Tag Library Definition (TLD)####</p> + +<p>The JSP TLD is included in the</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>struts-core.jar +</code></pre> +</div> +<p>. To use, just include the usual red-tape at the top of your JSP.</p> + +<div class="highlighter-rouge"><pre class="highlight"><code> +<span class="err"><</span>%@ taglib prefix="s" uri="/struts-tags" %> +<span class="nt"><html></span> + <span class="nt"><body></span> + <span class="nt"><p></span>Now you can use the tags, like so:<span class="nt"></p></span> + <span class="nt"><s:iterator</span> <span class="na">value=</span><span class="s">"people"</span><span class="nt">></span> + <span class="nt"><s:property</span> <span class="na">value=</span><span class="s">"lastName"</span><span class="nt">/></span>, <span class="nt"><s:property</span> <span class="na">value=</span><span class="s">"firstName"</span><span class="nt">/></span> + <span class="nt"></s:iterator></span> + ... + +</code></pre> +</div> + +<p>####Next:####</p> + +<p>##OGNL## {#PAGE_14198}</p> + +<p>OGNL is the Object Graph Navigation Language (see <a href="http://commons\.apache\.org/proper/commons\-ognl/">http://commons.apache.org/proper/commons-ognl/</a> for the full documentation of OGNL). Here, we will cover a few examples of OGNL features that co-exist with the framework. To review basic concepts, refer to <a href="#PAGE_14000">OGNL Basics</a>.</p> + +<p>The framework uses a standard naming context to evaluate OGNL expressions. The top level object dealing with OGNL is a Map (usually referred as a context map or context). OGNL has a notion of there being a root (or default) object within the context. In expression, the properties of the root object can be referenced without any special âmarkerâ notion. References to other objects are marked with a pound sign (</p> + +<div class="highlighter-rouge"><pre class="highlight"><code># +</code></pre> +</div> +<p>).</p> + +<p>The framework sets the OGNL context to be our ActionContext, and the value stack to be the OGNL root object. (The value stack is a set of several objects, but to OGNL it appears to be a single object.) Along with the value stack, the framework places other objects in the ActionContext, including Maps representing the application, session, and request contexts. These objects coexist in the ActionContext, alongside the value stack (our OGNL root).</p> + +<div class="highlighter-rouge"><pre class="highlight"><code> | + |--application + | + |--session + context map---| + |--value stack(root) + | + |--action (the current action) + | + |--request + | + |--parameters + | + |--attr (searches page, request, session, then application scopes) + | + +</code></pre> +</div> + +<p>(information) There are other objects in the context map. The diagram is for example only.</p> + +<p>The Action instance is always pushed onto the value stack. Because the Action is on the stack, and the stack is the OGNL root, references to Action properties can omit the</p> + +<div class="highlighter-rouge"><pre class="highlight"><code># +</code></pre> +</div> +<p>marker. But, to access other objects in the ActionContext, we must use the</p> + +<div class="highlighter-rouge"><pre class="highlight"><code># +</code></pre> +</div> +<p>notation so OGNL knows not to look in the root object, but for some other object in the ActionContext.</p> + +<p><strong>Referencing an Action property</strong></p> + +<div class="highlighter-rouge"><pre class="highlight"><code><s:property value="postalCode"/> + +</code></pre> +</div> + +<p>Other (non-root) objects in the ActionContext can be rendered use the</p> + +<div class="highlighter-rouge"><pre class="highlight"><code># +</code></pre> +</div> +<p>notation.</p> + +<div class="highlighter-rouge"><pre class="highlight"><code><s:property value="#session.mySessionPropKey"/> or +<s:property value="#session['mySessionPropKey']"/> or +<s:property value="#request['myRequestPropKey']"/> + +</code></pre> +</div> + +<p>The ActionContext is also exposed to Action classes via a static method.</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>ActionContext.getContext().getSession().put("mySessionPropKey", mySessionObject); + +</code></pre> +</div> + +<p>You can also put expression for attributes that donât support dynamic content, like below:</p> + +<div class="highlighter-rouge"><pre class="highlight"><code><c:set var="foo" value="bar" scope="request"/> +<s:textfield name="username" label="%{#request.foo}" /> + +</code></pre> +</div> + +<p>####Collections (Maps, Lists, Sets)####</p> + +<p>Dealing with Collections (Maps, Lists, and Sets) in the framework comes often, so below please there are a few examples using the select tag. The <a href="http://commons\.apache\.org/proper/commons\-ognl/language\-guide\.html\#Collection\_Construction">OGNL documentation</a>^[http://commons.apache.org/proper/commons-ognl/language-guide.html#Collection_Construction] also includes some examples.</p> + +<p>Syntax for list: {e1,e2,e3}. This idiom creates a List containing the String âname1â, âname2â and âname3â. It also selects âname2â as the default value.</p> + +<div class="highlighter-rouge"><pre class="highlight"><code><s:select label="label" name="name" list="{'name1','name2','name3'}" value="%{'name2'}" /> + +</code></pre> +</div> + +<p>Syntax for map: #{key1:value1,key2:value2}. This idiom creates a map that maps the string âfooâ to the string âfoovalueâ and âbarâ to the string âbarvalueâ:</p> + +<div class="highlighter-rouge"><pre class="highlight"><code><s:select label="label" name="name" list="#{'foo':'foovalue', 'bar':'barvalue'}" /> + +</code></pre> +</div> + +<p>To determine if an element exists in a Collection, use the operations</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>in +</code></pre> +</div> +<p>and</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>not in +</code></pre> +</div> +<p>.</p> + +<div class="highlighter-rouge"><pre class="highlight"><code><s:if test="'foo' in {'foo','bar'}"> + muhahaha +</s:if> +<s:else> + boo +</s:else> + +<s:if test="'foo' not in {'foo','bar'}"> + muhahaha +</s:if> +<s:else> + boo +</s:else> + +</code></pre> +</div> + +<p>To select a subset of a collection (called projection), use a wildcard within the collection.</p> + +<ul> + <li> + <p>? - All elements matching the selection logic</p> + </li> + <li> + <p>^ - Only the first element matching the selection logic</p> + </li> + <li> + <p>$ - Only the last element matching the selection logic</p> + </li> +</ul> + +<p>To obtain a subset of just male relatives from the object person:</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>person.relatives.{? #this.gender == 'male'} + +</code></pre> +</div> + +<p>####Lambda Expressions####</p> + +<p>OGNL supports basic lamba expression syntax enabling you to write simple functions.</p> + +<p>(Dedicated to all you math majors who didnât think you would ever see this one again.)</p> + +<p>Fibonacci: if n==0 return 0; elseif n==1 return 1; else return fib(n-2)+fib(n-1); + fib(0) = 0 + fib(1) = 1 + fib(11) = 89</p> + +<p><strong>(i) How the expression works</strong></p> + +<blockquote> + +</blockquote> + +<blockquote> + +</blockquote> + +<blockquote> + <p>The lambda expression is everything inside the square brackets. The #this variable holds the argument to the expression, which in the following example is the number 11 (the code after the square-bracketed lamba expression, #fib(11)).</p> +</blockquote> + +<blockquote> + +</blockquote> + +<div class="highlighter-rouge"><pre class="highlight"><code><s:property value="#fib =:[#this==0 ? 0 : #this==1 ? 1 : #fib(#this-2)+#fib(#this-1)], #fib(11)" /> + +</code></pre> +</div> + +<p>####Next:####</p> + +<p>###OGNL Basics### {#PAGE_14000}</p> + +<p>#####XWork-specific language features#####</p> + +<p>The biggest addition that XWork provides on top of OGNL is the support for the ValueStack. While OGNL operates under the assumption there is only one ârootâ, XWorkâs ValueStack concept requires there be many ârootsâ.</p> + +<p>For example, suppose we are using standard OGNL (not using XWork) and there are two objects in the OgnlContext map: âfooâ -> foo and âbarâ -> bar and that the foo object is also configured to be the single <strong>root</strong> object. The following code illustrates how OGNL deals with these three situations:</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>#foo.blah // returns foo.getBlah() +#bar.blah // returns bar.getBlah() +blah // returns foo.getBlah() because foo is the root + +</code></pre> +</div> + +<p>What this means is that OGNL allows many objects in the context, but unless the object you are trying to access is the root, it must be prepended with a namespaces such as @bar. Now letâs talk about how XWork is a little different...</p> + +<p><strong>(i) Useful Information</strong></p> + +<blockquote> + +</blockquote> + +<blockquote> + +</blockquote> + +<blockquote> + <p>In XWork, the entire ValueStack is the root object in the context. Rather than having your expressions get the object you want from the stack and then get properties from that (ie: peek().blah), XWork has a special OGNL PropertyAccessor that will automatically look at the all entries in the stack (from the top down) until it finds an object with the property you are looking for.</p> +</blockquote> + +<blockquote> + +</blockquote> + +<p>For example, suppose the stack contains two objects: Animal and Person. Both objects have a ânameâ property, Animal has a âspeciesâ property, and Person has a âsalaryâ property. Animal is on the top of the stack, and Person is below it. The follow code fragments help you get an idea of what is going on here:</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>species // call to animal.getSpecies() +salary // call to person.getSalary() +name // call to animal.getName() because animal is on the top + +</code></pre> +</div> + +<p>In the last example, there was a tie and so the animalâs name was returned. Usually this is the desired effect, but sometimes you want the property of a lower-level object. To do this, XWork has added support for indexes on the ValueStack. All you have to do is:</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>[0].name // call to animal.getName() +[1].name // call to person.getName() + +</code></pre> +</div> + +<p>With expression like [0] ... [3] etc. Struts 2 will cut the stack and still return back a CompoundRoot object. To get the top of that particular stack cut, use <em>0</em> .top</p> + +<table> + <thead> + <tr> + <th>ognl expression</th> + <th>description</th> + </tr> + </thead> + <tbody> + <tr> + <td>[0].top</td> + <td>would get the top of the stack cut starting from element 0 in the stack (similar to top in this case)</td> + </tr> + <tr> + <td>[1].top</td> + <td>would get the top of the stack cut starting from element 1 in the stack</td> + </tr> + </tbody> +</table> + +<p>#####Accessing static properties#####</p> + +<p>OGNL supports accessing static properties as well as static methods.</p> + +<p>By default, Struts 2 is configured to disallow this--to enable OGNLâs static member support you must set the</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>struts.ognl.allowStaticMethodAccess +</code></pre> +</div> +<p>constant to</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>true +</code></pre> +</div> +<p>via any of the <em>Constant Configuration</em> methods.</p> + +<p>OGNLâs static access looks like this:</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>@some.package.ClassName@FOO_PROPERTY +@some.package.ClassName@someMethod() + +</code></pre> +</div> + +<p>However, XWork allows you to avoid having to specify the full package name and call static properties and methods of your action classes using the âvsâ prefix:</p> + +<div class="highlighter-rouge"><pre class="highlight"><code><at:var at:name="vs" />FOO_PROPERTY +<at:var at:name="vs" />someMethod() + +<at:var at:name="vs1" />FOO_PROPERTY +<at:var at:name="vs1" />someMethod() + +<at:var at:name="vs2" />BAR_PROPERTY +<at:var at:name="vs2" />someOtherMethod() + +</code></pre> +</div> + +<p>âvsâ stands for âvalue stackâ. The important thing to note here is that if the class name you specify is just âvsâ, the class for the object on the top of the stack is used. If you specify a number after the âvsâ string, an objectâs class deeper in the stack is used instead.</p> + +<p>#####Differences from the WebWork 1.x EL#####</p> + +<p>Besides the examples and descriptions given above, there are a few major changes in the EL since WebWork 1.x. The biggest one is that properties are no longer accessed with a forward slash (/) but with a dot (.). Also, rather than using â..â to traverse down the stack, we now use â[n]â where n is some positive number. Lastly, in WebWork 1.x one could access special named objects (the request scope attributes to be exact) by using â@fooâ, but now special variables are accessed using â#fooâ. However, it is important to note that â#fooâ does NOT access the request attributes. Because XWork is not built only for the web, there is no concept of ârequest attributesâ, and thus â#fooâ is merely a request to another object in the OgnlContext other than the root.</p> + +<table> + <thead> + <tr> + <th>Old Expression</th> + <th>New Expression</th> + </tr> + </thead> + <tbody> + <tr> + <td>foo/blah</td> + <td>foo.blah</td> + </tr> + <tr> + <td>foo/someMethod()</td> + <td>foo.someMethod()</td> + </tr> + <tr> + <td>../bar/blah</td> + <td>[1].bar.blah</td> + </tr> + <tr> + <td>@baz</td> + <td>not directly supported, but #baz is similar</td> + </tr> + <tr> + <td>.</td> + <td>âtopâ or [0]</td> + </tr> + </tbody> +</table> + +<p>#####Struts 2 Named Objects#####</p> + +<p>Struts 2 places request parameters and request, session, and application attributes on the OGNL stack. They may be accessed as shown below.</p> + +<table> + <thead> + <tr> + <th>name</th> + <th>value</th> + </tr> + </thead> + <tbody> + <tr> + <td>#action[âfooâ] or #action.foo</td> + <td>current action getter (getFoo())</td> + </tr> + <tr> + <td>#parameters[âfooâ] or #parameters.foo</td> + <td>request parameter [âfooâ] (request.getParameter())</td> + </tr> + <tr> + <td>#request[âfooâ] or #request.foo</td> + <td>request attribute [âfooâ] (request.getAttribute())</td> + </tr> + <tr> + <td>#session[âfooâ] or #session.foo</td> + <td>session attribute âfooâ</td> + </tr> + <tr> + <td>#application[âfooâ] or #application.foo</td> + <td>ServletContext attributes âfooâ</td> + </tr> + <tr> + <td>#attr[âfooâ] or #attr.foo</td> + <td>Access to PageContext if available, otherwise searches request/session/application respectively</td> + </tr> + </tbody> +</table> + +<p>###OGNL Expression Compilation### {#PAGE_61661}</p> + +<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="highlighter-rouge"><pre class="highlight"><code> +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); + +</code></pre> +</div> + +<p>Youâll notice that this example references the new</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>ognl.enhance.ExpressionAccessor +</code></pre> +</div> +<p>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</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>Ognl.getValue(node, context, root) +</code></pre> +</div> +<p>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</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>ognl.enhance.ExpressionCompiler +</code></pre> +</div> +<p>, which implements</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>ognl.enhance.OgnlExpressionCompiler +</code></pre> +</div> +<p>. 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> + +<p><strong> Tapestry OGNL Integration</strong></p> + +<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>^[http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/services/impl/HiveMindExpressionCompiler.java?view=markup] The Tapestry implementation of</li> +</ul> + +<div class="highlighter-rouge"><pre class="highlight"><code>ognl.enhance.OgnlExpressionCompiler +</code></pre> +</div> +<p>- which is a subclass of the</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>ognl.enhance.ExpressionCompiler +</code></pre> +</div> +<p>default implementation.</p> + +<ul> + <li> + <p><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>^[http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/services/impl/ExpressionEvaluatorImpl.java?view=markup] 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.</p> + </li> + <li> + <p><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>^[http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/services/impl/ExpressionCacheImpl.java?view=markup] Service responsible for caching OGNL statements where appropriate.</p> + </li> + <li> + <p><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>^[http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/binding/ExpressionBinding.java?view=markup] 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</p> + </li> +</ul> + +<div class="highlighter-rouge"><pre class="highlight"><code>IBinding +</code></pre> +</div> +<p>,</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>ExpressionBinding +</code></pre> +</div> +<p>represents the type of bindings for OGNL expressions.</p> + +<p>*<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>^[http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/bean/BeanProviderPropertyAccessor.java?view=markup] One of the custom</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>PropertyAccessor +</code></pre> +</div> +<p>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</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>PropertyAccessor +</code></pre> +</div> +<p>classes if you want to compile expressions.</p> + +<p><strong> ExpressionEvaluator</strong></p> + +<p>If you look at the</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>ExpressionEvaluator +</code></pre> +</div> +<p>source youâll see a block of initialization where the</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>HiveMindExpressionCompiler +</code></pre> +</div> +<p>and</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>OgnlContext +</code></pre> +</div> +<p>pools are setup:</p> + +<div class="highlighter-rouge"><pre class="highlight"><code> +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); + +</code></pre> +</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</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>OgnlRuntime +</code></pre> +</div> +<p>:</p> + +<div class="highlighter-rouge"><pre class="highlight"><code> +OgnlRuntime.clearCache(); +Introspector.flushCaches(); + +</code></pre> +</div> + +<p>The OgnlRuntime class stores static</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>Map +</code></pre> +</div> +<p>-like instances of reflection meta cache information for all objects evaluated in OGNL expressions. The new</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>clearCache +</code></pre> +</div> +<p>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> + +<p><strong> HiveMindExpressionCompiler</strong></p> + +<p>Perhaps the most important class to examine is Tapestrys implementation of</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>OgnlExpressionCompiler +</code></pre> +</div> +<p>. This class still extends the default</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>ExpressionCompiler +</code></pre> +</div> +<p>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</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>ClassPool +</code></pre> +</div> +<p>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</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>UnsupportedCompilationException +</code></pre> +</div> +<p>if the âuserâ object returned was null - resulting in no direct compilation being done at all:</p> + +<div class="highlighter-rouge"><pre class="highlight"><code> +"user.firstName" + +</code></pre> +</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</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>ExpressionAccessor +</code></pre> +</div> +<p>instance is attached to the root</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>Node +</code></pre> +</div> +<p>object by calling</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>SimpleNode.setAccessor(newInstance) +</code></pre> +</div> +<p>.</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</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>ExpressionAccessor +</code></pre> +</div> +<p>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="highlighter-rouge"><pre class="highlight"><code> +public Object get(OgnlContext context, Object root) +{ + return _node.getValue($1, $2); +} + +</code></pre> +</div> + +<p>The</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>$1, $2 +</code></pre> +</div> +<p>references are Javassist constructs which allow you to specify the first and second argument passed in to the calling method.</p> + +<p><strong> ExpressionBinding</strong></p> + +<p>As stated previously, this class represents a single OGNL expression in Tapestry when used directly in html templates - such as:</p> + +<div class="highlighter-rouge"><pre class="highlight"><code> +<div jwcid="@Input" value="ognl:user.firstName" /> + +</code></pre> +</div> + +<p>What you will want to examine in this class is how it deals with incrementally attempting expression evaluations using the local members</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>_writeFailed, _accessor +</code></pre> +</div> +<p>. 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> + +<p><strong> BeanProviderPropertyAccessor / Custom PropertyAccessor implementations</strong></p> + +<p>Besides the</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>OgnlExpressionCompiler +</code></pre> +</div> +<p>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</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>PropertyAccessor +</code></pre> +</div> +<p>methods you must implement in order to compile your expressions:</p> + +<div class="highlighter-rouge"><pre class="highlight"><code> +public Class getPropertyClass(OgnlContext context, Object target, Object name) +{ + IBeanProvider provider = (IBeanProvider)target; + String beanName = ((String)name).replaceAll("\"", ""); + + if (provider.canProvideBean(beanName)) + return provider.getBean(beanName).getClass(); + + return super.getPropertyClass(context, target, name); +} + +public String getSourceAccessor(OgnlContext context, Object target, Object name) +{ + IBeanProvider provider = (IBeanProvider)target; + String beanName = ((String)name).replaceAll("\"", ""); + + if (provider.canProvideBean(beanName)) { + + Class type = OgnlRuntime.getCompiler().getInterfaceClass(provider.getBean(beanName).getClass()); + + ExpressionCompiler.addCastString(context, "((" + type.getName() + ")"); + + context.setCurrentAccessor(IBeanProvider.class); + context.setCurrentType(type); + + return ".getBean(" + name + "))"; + } + + return super.getSourceAccessor(context, target, name); +} + +public String getSourceSetter(OgnlContext context, Object target, Object name) +{ + throw new UnsupportedCompilationException("Can't set beans on IBeanProvider."); +} + +</code></pre> +</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</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>PropertyAccessor +</code></pre> +</div> +<p>implementations - the built in OGNL versions like</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>ObjectPropertyAccessor, MapPropertyAccessor, ListPropertyAccessor, etc +</code></pre> +</div> +<p>should provide more than enough data to work from. <a href="http://svn\.opensymphony\.com/svn/ognl/trunk/">http://svn.opensymphony.com/svn/ognl/trunk/</a></p> + +<p>The most important part of the above logic you will want to look at is in how the new</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>OgnlContext +</code></pre> +</div> +<p>methods for setting object/accessor types are being set:</p> + +<div class="highlighter-rouge"><pre class="highlight"><code> +context.setCurrentAccessor(IBeanProvider.class); +context.setCurrentType(type); + +</code></pre> +</div> + +<p>This meta information is used by the</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>OgnlExpressionCompiler +</code></pre> +</div> +<p>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. <a href="http://jira\.opensymphony\.com/browse/OGNL">http://jira.opensymphony.com/browse/OGNL</a></p> + +<p>In this property accessor example the goal is to turn general statements like</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>beans.emailValidator +</code></pre> +</div> +<p>in to their pure source form - which would look something like this when all is said and done:</p> + +<div class="highlighter-rouge"><pre class="highlight"><code> +((ValidatingBean)beanProvider.getBean("emailValidator")) + +</code></pre> +</div> + +<p>There is also the ever important cast handling which you must do:</p> + +<div class="highlighter-rouge"><pre class="highlight"><code> +Class type = OgnlRuntime.getCompiler().getInterfaceClass(provider.getBean(beanName).getClass()); + +ExpressionCompiler.addCastString(context, "((" + type.getName() + ")"); + +</code></pre> +</div> + +<p>In this example the</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>PropertyAccessor +</code></pre> +</div> +<p>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</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>addCastString(OgnlContext, String) +</code></pre> +</div> +<p>on</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>ExpressionCompiler +</code></pre> +</div> +<p>. 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 ObjectPropertyAccessor implementation)</p> + +<div class="highlighter-rouge"><pre class="highlight"><code> +Method m = ...(various reflection gynamistics used to find a java.reflect.Method instance) + +context.setCurrentType(m.getReturnType()); +context.setCurrentAccessor(OgnlRuntime.getCompiler().getSuperOrInterfaceClass(m, m.getDeclaringClass())); + +</code></pre> +</div> + +<p>When dealing with method calls it is very important that you do this specific kind of type setting on the</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>OgnlContext +</code></pre> +</div> +<p>class so that the casting done on your statements (which happens outside of the ObjectPropertyAccessor 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"><pre class="highlight"><code> +user.firstName + +</code></pre> +</div> + +<p>and the object it was compiled against was an instance of something looking like this:</p> + +<div class="highlighter-rouge"><pre class="highlight"><code> +public abstract LoginPage extends BasePage implements UserPermissions { + + public abstract User getUser(); + +} + +.. +/** + * Interface for any page/component that holds references to the current system + * User. + */ +public interface UserPermissions { + User getUser(); +} + +</code></pre> +</div> + +<div class="highlighter-rouge"><pre class="highlight"><code>BasePage +</code></pre> +</div> +<p>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="highlighter-rouge"><pre class="highlight"><code> +context.setCurrentType(m.getReturnType()); +context.setCurrentAccessor(m.getDeclaringClass()); + +</code></pre> +</div> + +<p>It would have resulted in a compiled expression of:</p> + +<div class="highlighter-rouge"><pre class="highlight"><code> +public void get(OgnlContext context, Object root) +{ + return ((LoginPage)root).getUser(); +} + +</code></pre> +</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</p> + +<div class="highlighter-rouge"><pre class="highlight"><code>ExpressionCacheImpl +</code></pre> +</div> +<p>listed above). The better/more re-usable compiled version should really look like:</p> + +<div class="highlighter-rouge"><pre class="highlight"><code> +public void get(OgnlContext context, Object root) +{ + return ((UserPermissions)root).getUser(); +} + +</code></pre> +</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> + +<ul> + <li> + <p><strong>Compiler Errors</strong> - 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. 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> + </li> + <li> + <p><strong>Compile vs. normal expression evaluation</strong> - 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> + </li> +</ul> + +<div class="highlighter-rouge"><pre class="highlight"><code>Ognl.getValue(OgnlContext, Object root, String expression +</code></pre> +</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> + +<ul> + <li><strong>Snapshot Repository</strong> - 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.</li> +</ul> + +<p>##Struts Tags## {#PAGE_14248}</p> + +<p>The framework provides a tag library decoupled from the view technology. In this section, we describe each tag in general terms, such as the attributes it supports, what the behaviors are, and so forth. Most tags are supported in all template languages (see <a href="#PAGE_13973">JSP Tags</a>, <a href="#PAGE_13950">Velocity Tags</a>, and <a href="#PAGE_14294">FreeMarker Tags</a>), but some are currently only specific to one language. Whenever a tag doesnât have complete support for every language, it is noted on the tagâs reference page.</p> + +<p>The types of tags can be broken in to two types: generic and UI. Besides function and responsibility, the biggest difference between the two is that the HTML tags support <em>templates</em> and <em>themes</em> . In addition to the general tag reference, we also provide examples for using these generic tags in each of the support languages.</p> + +<p>(ok) Be sure to read the <a href="#PAGE_13927">Tag Syntax</a> document to learn how tag attribute syntax works.</p> + +<p>####FAQs####</p> + +<ul> + <li> + <p><em>Why do the form tags put table tags around controls</em> ?</p> + </li> + <li> + <p><em>How can I put a String literal in a Javascript call, for instance in an onChange attribute</em> ?</p> + </li> + <li> + <p><em>Why wonât the âifâ tag evaluate a one char string</em> ?</p> + </li> + <li> + <p><em>Why does FreeMarker complain that thereâs an error in my user-directive when I used JSP Tag</em> ?</p> + </li> + <li> + <p><em>Can an action tag run another method apart from the default execute method</em> ?</p> + </li> + <li> + <p><em>Why didnât my action tag get executed when I have validation errors</em> ?</p> + </li> + <li> + <p><em>Why are request parameters appended to our hyperlinks</em> ?</p> + </li> +</ul> + +<p>####Resources####</p> + +<ul> + <li> + <p><a href="http://www\.vitarara\.org/cms/struts\_2\_cookbook/creating\_a\_ui\_component">Creating a UI Component in Struts 2</a>^[http://www.vitarara.org/cms/struts_2_cookbook/creating_a_ui_component] (Mark Menard)</p> + </li> + <li> + <p><a href="http://www\.roseindia\.net/struts/struts2/struts\-2\-tags\.shtml">Struts 2 Tags</a>^[http://www.roseindia.net/struts/struts2/struts-2-tags.shtml] (Rose India)</p> + </li> +</ul> + +<p>####Next:####</p> + +<p>###Ajax Tags### {#PAGE_31510}</p> + +<p><strong>(!) Dojo plugin is deprecated</strong></p> + +<blockquote> + +</blockquote> + +<blockquote> + +</blockquote> + +<blockquote> + <p>The Dojo plugin will be deprecated on Struts 2.1</p> +</blockquote> + +<blockquote> + +</blockquote> + +<table> + <tbody> + <tr> + </tr> + </tbody> +</table> + +<table> + <tbody> + <tr> + <td>The easiest way to get documentation for Struts 2.0 Dojo tag usage is to look at older Struts 2 documentation, like the <a href="http://struts\.apache\.org/2\.0\.11/docs/ajax\-tags\.html">Struts 2.0.11 Ajax tags wiki documentation</a>^[http://struts.apache.org/2.0.11/docs/ajax-tags.html].</td> + </tr> + </tbody> +</table> + +<table> + <tbody> + <tr> + </tr> + </tbody> +</table> + +<table> + <tbody> + <tr> + </tr> + </tbody> +</table> + +<table> + <tbody> + <tr> + <td><strong>Please</strong> check that documentation and the Dojo tag examples in the showcase app of the appropriate Struts 2 version before asking questions on the struts-user mailing list!</td> + </tr> + </tbody> +</table> + +<h2 id="section">|</h2> + +<table> + <tbody> + <tr> + </tr> + </tbody> +</table> + +<table> + <tbody> + <tr> + <td><strong>THE WIKI IS NOT VERSIONABLE</strong> (in a practical way).</td> + </tr> + </tbody> +</table> + +<table> + <tbody> + <tr> + </tr> + </tbody> +</table> + +<table> + <tbody> + <tr> + </tr> + </tbody> +</table> + +<table> + <tbody> + <tr> + <td>The documentation here is for the most current Struts 2, not necessarily the most current <em>release</em> . We try to add version-specific documentation notes but have undoubtedly missed some locations.</td> + </tr> + </tbody> +</table> + +<table> + <tbody> + <tr> + </tr> + </tbody> +</table> + +<p>#####Description#####</p> + +<p>To use the AJAX tags from 2.1 on you must:</p> + +<ul> + <li> + <p>Include the Dojo Plugin distributed with Struts 2 in your /WEB-INF/lib folder.</p> + </li> + <li> + <p>Add <em><%@ taglib prefix=âsxâ uri=â/struts-dojo-tagsâ %></em> to your page.</p> + </li> + <li> + <p>Include the <a href="#PAGE_66757">head</a> tag on the page, which can be configured for performance or debugging purposes.</p> + </li> +</ul> + +<p>#####Handling AJAX Responses#####</p> + +<p>The following attributes affect the handling of all ajax responses.</p> + +<p>|Attribute|Default Value|Description| +|âââ|ââââ-|ââââ| +|parseContent|true|When true, Dojo will parse the response into an XHTML Document Object and traverse the nodes searching for Dojo Widget markup. The parse and traversal is performed prior to inserting the nodes into the DOM. This attribute must be enabled to nest Dojo widgets (dojo tags) within responses. Thereâs significant processing involved to create and parse the document so switch this feature off when not required. Note also that the response must be valid XHTML for cross-browser support and widgets must have unique IDs.| +|separateScripts|true|When true, Dojo will extract the <script> tags from the response, concatenate the extracted code into one block, create a new Function whose body is the extracted code and immediately invoke the function. The invocation is performed after the DOM has been updated with the XHTML. The function is executed within the scope of the widget (that is, the <strong>this</strong> variable points to the widget instance).\ +\ +When false, Dojo will extract the <script> tags from the response, concatenate the extracted code into one block and:\ +\ +*in IE:
<TRUNCATED>