blob: 2baaf088932051c58b8efaba4704def8dfa01d57 [file] [log] [blame]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta name="Date-Revision-yyyymmdd" content="20140918"/>
<meta http-equiv="Content-Language" content="en"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Convention plugin</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/plugins/convention/index.md" title="Edit this page on GitHub">Edit on GitHub</a>
<a href="../" title="back to Plugins"><< back to Plugins</a>
<h1 class="no_toc" id="convention-plugin">Convention Plugin</h1>
<ul id="markdown-toc">
<li><a href="#introduction" id="markdown-toc-introduction">Introduction</a></li>
<li><a href="#setup" id="markdown-toc-setup">Setup</a></li>
<li><a href="#converting-a-codebehind-based-application-to-convention" id="markdown-toc-converting-a-codebehind-based-application-to-convention">Converting a Codebehind based application to Convention</a></li>
<li><a href="#hello-world" id="markdown-toc-hello-world">Hello world</a></li>
<li><a href="#code-behind-hello-world" id="markdown-toc-code-behind-hello-world">Code behind hello world</a></li>
<li><a href="#results-and-result-codes" id="markdown-toc-results-and-result-codes">Results and result codes</a></li>
<li><a href="#multiple-names" id="markdown-toc-multiple-names">Multiple names</a></li>
<li><a href="#xwork-packages" id="markdown-toc-xwork-packages">XWork packages</a></li>
<li><a href="#annotation-reference" id="markdown-toc-annotation-reference">Annotation reference</a> <ul>
<li><a href="#action-annotation" id="markdown-toc-action-annotation">Action annotation</a> <ul>
<li><a href="#applying-action-and-actions-at-the-class-level" id="markdown-toc-applying-action-and-actions-at-the-class-level">Applying <code class="highlighter-rouge">@Action</code> and <code class="highlighter-rouge">@Actions</code> at the class level</a></li>
</ul>
</li>
<li><a href="#interceptorref-annotation" id="markdown-toc-interceptorref-annotation">InterceptorRef annotation</a></li>
<li><a href="#result-annotation" id="markdown-toc-result-annotation">Result annotation</a></li>
<li><a href="#namespace-annotation" id="markdown-toc-namespace-annotation">Namespace annotation</a></li>
<li><a href="#resultpath-annotation" id="markdown-toc-resultpath-annotation">ResultPath annotation</a></li>
<li><a href="#parentpackage-annotation" id="markdown-toc-parentpackage-annotation">ParentPackage annotation</a></li>
<li><a href="#exceptionmapping-annotation" id="markdown-toc-exceptionmapping-annotation">ExceptionMapping Annotation</a></li>
</ul>
</li>
<li><a href="#actions-in-jar-files" id="markdown-toc-actions-in-jar-files">Actions in jar files</a></li>
<li><a href="#automatic-configuration-reloading" id="markdown-toc-automatic-configuration-reloading">Automatic configuration reloading</a></li>
<li><a href="#jboss" id="markdown-toc-jboss">JBoss</a></li>
<li><a href="#jetty-embedded" id="markdown-toc-jetty-embedded">Jetty (embedded)</a></li>
<li><a href="#troubleshooting" id="markdown-toc-troubleshooting">Troubleshooting</a> <ul>
<li><a href="#tips" id="markdown-toc-tips">Tips</a></li>
<li><a href="#common-errors" id="markdown-toc-common-errors">Common Errors</a></li>
</ul>
</li>
<li><a href="#overwriting-plugin-classes" id="markdown-toc-overwriting-plugin-classes">Overwriting plugin classes</a></li>
<li><a href="#configuration-reference" id="markdown-toc-configuration-reference">Configuration reference</a></li>
</ul>
<h2 id="introduction">Introduction</h2>
<p>The Convention Plugin is bundled with Struts since 2.1 and replaces the <em>Codebehind Plugin</em> and Zero Config plugins.
It provides the following features:</p>
<ul>
<li>Action location by package naming conventions</li>
<li>Result (JSP, FreeMarker, etc) location by naming conventions</li>
<li>Class name to URL naming convention</li>
<li>Package name to namespace convention</li>
<li>SEO compliant URLs (i.e. <code class="highlighter-rouge">my-action</code> rather than <code class="highlighter-rouge">MyAction</code>)</li>
<li>Action name overrides using annotations</li>
<li>Interceptor overrides using annotations</li>
<li>Namespace overrides using annotations</li>
<li>XWork package overrides using annotations</li>
<li>Default action and result handling (i.e. <code class="highlighter-rouge">/products</code> will try <code class="highlighter-rouge">com.example.actions.Products</code> as well as
<code class="highlighter-rouge">com.example.actions.products.Index</code>)</li>
</ul>
<p>The Convention Plugin should require no configuration to use. Many of the conventions can be controlled using
configuration properties and many of the classes can be extended or overridden.</p>
<h2 id="setup">Setup</h2>
<p>In order to use the Convention plugin, you first need to add the JAR file to the <code class="highlighter-rouge">WEB-INF/lib</code> directory of your
application or include the dependency in your project’s Maven POM file.</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;dependency&gt;</span>
<span class="nt">&lt;groupId&gt;</span>org.apache.struts<span class="nt">&lt;/groupId&gt;</span>
<span class="nt">&lt;artifactId&gt;</span>struts2-convention-plugin<span class="nt">&lt;/artifactId&gt;</span>
<span class="nt">&lt;version&gt;</span>X.X.X<span class="nt">&lt;/version&gt;</span>
<span class="nt">&lt;/dependency&gt;</span>
</code></pre></div></div>
<p>Where <code class="highlighter-rouge">X.X.X</code> is the current version of Struts 2. Please remember that the Convention Plugin is available from
version 2.1.6.</p>
<h2 id="converting-a-codebehind-based-application-to-convention">Converting a Codebehind based application to Convention</h2>
<p>See <a href="converting.html">this page</a> for the required changes and tips.</p>
<p>If you are using REST with the Convention plugin, make sure you set these constants in <code class="highlighter-rouge">struts.xml</code>:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;constant</span> <span class="na">name=</span><span class="s">"struts.convention.action.suffix"</span> <span class="na">value=</span><span class="s">"Controller"</span><span class="nt">/&gt;</span>
<span class="nt">&lt;constant</span> <span class="na">name=</span><span class="s">"struts.convention.action.mapAllMatches"</span> <span class="na">value=</span><span class="s">"true"</span><span class="nt">/&gt;</span>
<span class="nt">&lt;constant</span> <span class="na">name=</span><span class="s">"struts.convention.default.parent.package"</span> <span class="na">value=</span><span class="s">"rest-default"</span><span class="nt">/&gt;</span>
</code></pre></div></div>
<h2 id="hello-world">Hello world</h2>
<p>Now that the Convention plugin has been added to your application, let’s start with a very simple example. This example
will use an actionless result that is identified by the URL. By default, the Convention plugin assumes that all
of the results are stored in <strong>WEB-INF/content</strong>. This can be changed by setting the property
<code class="highlighter-rouge">struts.convention.result.path</code> in the Struts properties file to the new location. Don’t worry about trailing slashes,
the Convention plugin handles this for you. Here is our hello world JSP:</p>
<pre><code class="language-jsp">&lt;html&gt;
&lt;body&gt;
Hello world!
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>If you start Tomcat (or whichever J2EE container you are using) and type in
<a href="http://localhost:8080/hello-world">http://localhost:8080/hello-world</a> (assuming that your context path is <code class="highlighter-rouge">/</code>,
ie. starting application from Eclipse) into your browser you should get this result:</p>
<p><strong>WEB-INF/content/hello-world.jsp</strong></p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Hello world!
</code></pre></div></div>
<p>This illustrates that the Convention plugin will find results even when no action exists and it is all based on the URL
passed to Struts.</p>
<h2 id="code-behind-hello-world">Code behind hello world</h2>
<p>Let’s expand on this example and add a code behind class. In order to do this we need to ensure that the Convention
plugin is able to find our action classes. By default, the Convention plugin will find all action classes that implement
<code class="highlighter-rouge">com.opensymphony.xwork2.Action</code> or whose name ends with the word <strong>Action</strong> in specific packages.</p>
<p>These packages are located by the Convention plugin using a search methodology. First the Convention plugin finds
packages named <code class="highlighter-rouge">struts</code>, <code class="highlighter-rouge">struts2</code>, <code class="highlighter-rouge">action</code> or <code class="highlighter-rouge">actions</code>. Any packages that match those names are considered the root
packages for the Convention plugin. Next, the plugin looks at all of the classes in those packages as well
as sub-packages and determines if the classes implement <code class="highlighter-rouge">com.opensymphony.xwork2.Action</code> or if their name ends with
<strong>Action</strong> (i.e. <code class="highlighter-rouge">FooAction</code>). Here’s an example of a few classes that the Convention plugin will find:</p>
<p><strong>Classes</strong></p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>com.example.actions.MainAction
com.example.actions.products.Display (implements com.opensymphony.xwork2.Action)
com.example.struts.company.details.ShowCompanyDetailsAction
</code></pre></div></div>
<p>Each of the action classes that the plugin finds will be configured to respond to specific URLs. The URL is based on
the package name that the class is defined in and the class name itself. First the plugin determines the namespace of
the URL using the package names between the root package and the package the class is defined in. For our examples above,
the namespaces would be:</p>
<p><strong>Namespaces</strong></p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>com.example.actions.MainAction -&gt; /
com.example.actions.products.Display -&gt; /products
com.example.struts.company.details.ShowCompanyDetailsAction -&gt; /company/details
</code></pre></div></div>
<p>Next, the plugin determines the URL of the resource using the class name. It first removes the word <strong>Action</strong> from the end of the class name and then converts camel case names to dashes. In our example the full URLs would be:</p>
<p><strong>Full URLs</strong></p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>com.example.actions.MainAction -&gt; /main
com.example.actions.products.Display -&gt; /products/display
com.example.struts.company.details.ShowCompanyDetailsAction -&gt; /company/details/show-company-details
</code></pre></div></div>
<p>You can tell the Convention plugin to ignore certain packages using the property <code class="highlighter-rouge">struts.convention.exclude.packages</code>.
You can also tell the plugin to use different strings to locate root packages using the property
<code class="highlighter-rouge">struts.convention.package.locators</code>. Finally, you can tell the plugin to search specific root packages using
the property <code class="highlighter-rouge">struts.convention.action.packages</code>.</p>
<p>Here is our code behind action class:</p>
<p><strong>com.example.actions.HelloWorld</strong></p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="n">com</span><span class="o">.</span><span class="na">example</span><span class="o">.</span><span class="na">actions</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.opensymphony.xwork2.ActionSupport</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">HelloWorld</span> <span class="kd">extends</span> <span class="n">ActionSupport</span> <span class="o">{</span>
<span class="kd">private</span> <span class="n">String</span> <span class="n">message</span><span class="o">;</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">getMessage</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">message</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">execute</span><span class="o">()</span> <span class="o">{</span>
<span class="n">message</span> <span class="o">=</span> <span class="s">"Hello World!"</span><span class="o">;</span>
<span class="k">return</span> <span class="n">SUCCESS</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>If you compile this class and place it into your application in the WEB-INF/classes, the Convention plugin will find
the class and map the URL <strong>/hello-world</strong> to it. Next, we need to update our JSP to print out the message we setup in
the action class. Here is the new JSP:</p>
<p><strong>WEB-INF/content/hello-world.jsp</strong></p>
<pre><code class="language-jsp">&lt;html&gt;
&lt;body&gt;
The message is ${message}
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<blockquote>
<p>Please notice that the expression <code class="highlighter-rouge">${message}</code> will work without adding JSP directive <code class="highlighter-rouge">isELIgnored="false"</code>.</p>
</blockquote>
<p>If start up the application server and open up <a href="http://localhost:8080/hello-world">http://localhost:8080/hello-world</a>
in our browser, we should get this result:</p>
<p><strong>Result</strong></p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>The message is Hello World!
</code></pre></div></div>
<h2 id="results-and-result-codes">Results and result codes</h2>
<p>The Convention Plugin will pre-configure all of your action classes when Struts is started. By default, this
configuration will also contain results for any JSPs that it can find within the application. The JSPs have
an additional feature that allows different JSPs to be used based on the result code of the action. Since action
methods return Strings and these Strings are traditionally used to locate results for the action, the Convention plugin
allows you to define different results based on the result code.</p>
<p>Building on our example from above, let’s say we want to provide a different result if the result code from our action
is the String <code class="highlighter-rouge">zero</code> rather than <code class="highlighter-rouge">success</code>. First, we update the action class to return different result codes:</p>
<p><strong>com.example.actions.HelloWorld</strong></p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="n">com</span><span class="o">.</span><span class="na">example</span><span class="o">.</span><span class="na">actions</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.opensymphony.xwork2.ActionSupport</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">HelloWorld</span> <span class="kd">extends</span> <span class="n">ActionSupport</span> <span class="o">{</span>
<span class="kd">private</span> <span class="n">String</span> <span class="n">message</span><span class="o">;</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">getMessage</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">message</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">execute</span><span class="o">()</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(</span><span class="n">System</span><span class="o">.</span><span class="na">currentTimeMillis</span><span class="o">()</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span>
<span class="n">message</span> <span class="o">=</span> <span class="s">"It's 0"</span><span class="o">;</span>
<span class="k">return</span> <span class="s">"zero"</span><span class="o">;</span>
<span class="o">}</span>
<span class="n">message</span> <span class="o">=</span> <span class="s">"It's 1"</span><span class="o">;</span>
<span class="k">return</span> <span class="n">SUCCESS</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Next, we add a new JSP to the application named <code class="highlighter-rouge">WEB-INF/content/hello-world-zero.jsp</code>. Notice that the first part
of the file name is the same as the URL of the action and the last part of the name is the result code. This is
the convention that the plugin uses to determine which results to render. Here is our new JSP:</p>
<p><strong>WEB-INF/content/hello-world.jsp</strong></p>
<pre><code class="language-jsp">&lt;html&gt;
&lt;body&gt;
The error message is ${message}
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>Now, if you compile the action and restart the application, based on the current time, you’ll either see the result from
<code class="highlighter-rouge">WEB-INF/content/hello-world.jsp</code> or <code class="highlighter-rouge">WEB-INF/content/hello-world-zero.jsp</code>.</p>
<p>The result type is based on the extension of the file. The supported extensions are: jsp,ftl,vm,html,html.
Examples of Action and Result to Template mapping:</p>
<table>
<thead>
<tr>
<th>URL</th>
<th>Result</th>
<th>File that could match</th>
<th>Result Type</th>
</tr>
</thead>
<tbody>
<tr>
<td>/hello</td>
<td>success</td>
<td>/WEB-INF/content/hello.jsp</td>
<td>Dispatcher</td>
</tr>
<tr>
<td>/hello</td>
<td>success</td>
<td>/WEB-INF/content/hello-success.htm</td>
<td>Dispatcher</td>
</tr>
<tr>
<td>/hello</td>
<td>success</td>
<td>/WEB-INF/content/hello.ftl</td>
<td>FreeMarker</td>
</tr>
<tr>
<td>/hello-world</td>
<td>input</td>
<td>/WEB-INF/content/hello-world-input.vm</td>
<td>Velocity</td>
</tr>
<tr>
<td>/test1/test2/hello</td>
<td>error</td>
<td>/WEB-INF/content/test/test2/hello-error.html</td>
<td>Dispatcher</td>
</tr>
</tbody>
</table>
<h2 id="multiple-names">Multiple names</h2>
<p>It is possible to define multiple names for the same result:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Action</span><span class="o">(</span><span class="n">results</span> <span class="o">=</span> <span class="o">{</span>
<span class="nd">@Result</span><span class="o">(</span><span class="n">name</span><span class="o">={</span><span class="s">"error"</span><span class="o">,</span> <span class="s">"input"</span><span class="o">},</span> <span class="n">location</span><span class="o">=</span><span class="s">"input-form.jsp"</span><span class="o">),</span>
<span class="nd">@Result</span><span class="o">(</span><span class="n">name</span><span class="o">=</span><span class="s">"success"</span><span class="o">,</span> <span class="n">location</span><span class="o">=</span><span class="s">"success.jsp"</span><span class="o">)</span>
<span class="o">})</span>
</code></pre></div></div>
<p>Such functionality was added in Struts version 2.5.</p>
<p>##Chaining</p>
<p>If one action returns the name of another action in the same package, they will be chained together, if the first action
doesn’t have any result defined for that code. In the following example:</p>
<p><strong>com.example.actions.HelloWorld</strong></p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="n">com</span><span class="o">.</span><span class="na">example</span><span class="o">.</span><span class="na">actions</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.opensymphony.xwork2.Action</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.opensymphony.xwork2.ActionSupport</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">HelloAction</span> <span class="kd">extends</span> <span class="n">ActionSupport</span> <span class="o">{</span>
<span class="nd">@Action</span><span class="o">(</span><span class="s">"foo"</span><span class="o">)</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">foo</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="s">"bar"</span><span class="o">;</span>
<span class="o">}</span>
<span class="nd">@Action</span><span class="o">(</span><span class="s">"foo-bar"</span><span class="o">)</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">bar</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">SUCCESS</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>The “foo” action will be executed, because no result is found, the Convention plugin tries to find an action named
“foo-bar” on the same package where “foo” is defined. If such an action is found, it will be invoked using
the “chain” result.</p>
<h2 id="xwork-packages">XWork packages</h2>
<p>Actions are placed on a custom XWork package which prevents conflicts. The name of this package is based on the Java
package the action is defined in, the namespace part of the URL for the action and the parent XWork package for
the action. The parent XWork package is determined based on the property named <code class="highlighter-rouge">struts.convention.default.parent.package</code>
(defaults to <code class="highlighter-rouge">convention-default</code>), which is a custom XWork package that extends <code class="highlighter-rouge">struts-default</code>.</p>
<p>Therefore the naming for XWork packages used by the Convention plugin are in the form:</p>
<p><strong>XWork package naming</strong></p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&lt;java-package&gt;#&lt;namespace&gt;#&lt;parent-package&gt;
</code></pre></div></div>
<p>Using our example from above, the XWork package for our action would be:</p>
<p><strong>XWork package naming</strong></p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>com.example.actions#/#conventionDefault
</code></pre></div></div>
<h2 id="annotation-reference">Annotation reference</h2>
<p>The Convention plugin uses a number of different annotations to override the default conventions that are used to map
actions to URLs and locate results. In addition, you can modify the parent XWork package that actions are configured with.</p>
<h3 id="action-annotation">Action annotation</h3>
<p>The Convention plugin allows action classes to change the URL that they are mapped to using the <strong>Action</strong> annotation.
This annotation can also be used inside the <strong>Actions</strong> annotation to allow multiple URLs to map to a single action
class. This annotation must be defined on action methods like this:</p>
<p><strong>com.example.actions.HelloWorld</strong></p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="n">com</span><span class="o">.</span><span class="na">example</span><span class="o">.</span><span class="na">actions</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.opensymphony.xwork2.ActionSupport</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.apache.struts2.convention.annotation.Action</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">HelloWorld</span> <span class="kd">extends</span> <span class="n">ActionSupport</span> <span class="o">{</span>
<span class="nd">@Action</span><span class="o">(</span><span class="s">"/different/url"</span><span class="o">)</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">execute</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">SUCCESS</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Our action class will now map to the URL <code class="highlighter-rouge">/different/url</code> rather than <code class="highlighter-rouge">/hello-world</code>. If no <code class="highlighter-rouge">@Result</code> (see next section)
is specified, then the namespace of the action will be used as the path to the result, on our last example it would be
<code class="highlighter-rouge">/WEB-INF/content/different/url.jsp</code>.</p>
<p>A single method within an action class can also map to multiple URLs using the <strong>Actions</strong> annotation like this:</p>
<p><strong>com.example.actions.HelloWorld</strong></p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="n">com</span><span class="o">.</span><span class="na">example</span><span class="o">.</span><span class="na">actions</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.opensymphony.xwork2.ActionSupport</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.apache.struts2.convention.annotation.Action</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.apache.struts2.convention.annotation.Actions</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">HelloWorld</span> <span class="kd">extends</span> <span class="n">ActionSupport</span> <span class="o">{</span>
<span class="nd">@Actions</span><span class="o">({</span>
<span class="nd">@Action</span><span class="o">(</span><span class="s">"/different/url"</span><span class="o">),</span>
<span class="nd">@Action</span><span class="o">(</span><span class="s">"/another/url"</span><span class="o">)</span>
<span class="o">})</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">execute</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">SUCCESS</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Another usage of the <strong>Action</strong> or <strong>Actions</strong> annotation is to define multiple action methods within a single action
class, each of which respond to a different URL. Here is an example of multiple action methods:</p>
<p><strong>com.example.actions.HelloWorld</strong></p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="n">com</span><span class="o">.</span><span class="na">example</span><span class="o">.</span><span class="na">actions</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.opensymphony.xwork2.ActionSupport</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.apache.struts2.convention.annotation.Action</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.apache.struts2.convention.annotation.Actions</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">HelloWorld</span> <span class="kd">extends</span> <span class="n">ActionSupport</span> <span class="o">{</span>
<span class="nd">@Action</span><span class="o">(</span><span class="s">"/different/url"</span><span class="o">)</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">execute</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">SUCCESS</span><span class="o">;</span>
<span class="o">}</span>
<span class="nd">@Action</span><span class="o">(</span><span class="s">"url"</span><span class="o">)</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">doSomething</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">SUCCESS</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>The previous example defines a second URL that is not fully qualified. This means that the namespace for the URL
is determined using the Java package name rather than the Action annotation.</p>
<p>Interceptor and interceptor stacks can be specified using the <code class="highlighter-rouge">interceptorRefs</code> attribute. The following example applies
the <code class="highlighter-rouge">validation</code> interceptor and the <code class="highlighter-rouge">defaultStack</code> interceptor stack to the action:</p>
<p><strong>com.example.actions.HelloWorld</strong></p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="n">com</span><span class="o">.</span><span class="na">example</span><span class="o">.</span><span class="na">actions</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.opensymphony.xwork2.ActionSupport</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.apache.struts2.convention.annotation.Action</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.apache.struts2.convention.annotation.Actions</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">HelloWorld</span> <span class="kd">extends</span> <span class="n">ActionSupport</span> <span class="o">{</span>
<span class="nd">@Action</span><span class="o">(</span><span class="n">interceptorRefs</span><span class="o">={</span><span class="nd">@InterceptorRef</span><span class="o">(</span><span class="s">"validation"</span><span class="o">),</span> <span class="nd">@InterceptorRef</span><span class="o">(</span><span class="s">"defaultStack"</span><span class="o">)})</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">execute</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">SUCCESS</span><span class="o">;</span>
<span class="o">}</span>
<span class="nd">@Action</span><span class="o">(</span><span class="s">"url"</span><span class="o">)</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">doSomething</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">SUCCESS</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Parameters can be passed to results using the <strong>params</strong> attribute. The value of this attribute is a string array with
an even number of elements in the form <code class="highlighter-rouge">{"key0", "value0, "key1", "value1" ... "keyN", "valueN"}</code>. For example:</p>
<p><strong>com.example.actions.HelloWorld</strong></p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="n">com</span><span class="o">.</span><span class="na">example</span><span class="o">.</span><span class="na">actions</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.opensymphony.xwork2.ActionSupport</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.apache.struts2.convention.annotation.Action</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.apache.struts2.convention.annotation.Actions</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">HelloWorld</span> <span class="kd">extends</span> <span class="n">ActionSupport</span> <span class="o">{</span>
<span class="nd">@Action</span><span class="o">(</span><span class="n">interceptorRefs</span><span class="o">=</span><span class="nd">@InterceptorRef</span><span class="o">(</span><span class="n">value</span><span class="o">=</span><span class="s">"validation"</span><span class="o">,</span><span class="n">params</span><span class="o">={</span><span class="s">"programmatic"</span><span class="o">,</span> <span class="s">"false"</span><span class="o">,</span> <span class="s">"declarative"</span><span class="o">,</span> <span class="s">"true}))
public String execute() {
return SUCCESS;
}
@Action("</span><span class="n">url</span><span class="err">"</span><span class="o">)</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">doSomething</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">SUCCESS</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>If interceptors are not specified, the default stack is applied.</p>
<blockquote>
<p>You can specify className parameter which can be especially useful when Spring Framework is used to instantiate actions.</p>
</blockquote>
<h4 id="applying-action-and-actions-at-the-class-level">Applying <code class="highlighter-rouge">@Action</code> and <code class="highlighter-rouge">@Actions</code> at the class level</h4>
<p>There are circumstances when this is desired, like when using <em>Dynamic Method Invocation</em> . If an <code class="highlighter-rouge">execute</code> method is
defined in the class, then it will be used for the action mapping, otherwise the method to be used will be determined
when a request is made (by Dynamic Method Invocation for example)</p>
<h3 id="interceptorref-annotation">InterceptorRef annotation</h3>
<p>Interceptors can be specified at the method level, using the <strong>Action</strong> annotation or at the class level using
the <code class="highlighter-rouge">InterceptorRefs</code> annotation. Interceptors specified at the class level will be applied to all actions defined
on that class. In the following example:</p>
<p><strong>com.example.actions.HelloWorld</strong></p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="n">com</span><span class="o">.</span><span class="na">example</span><span class="o">.</span><span class="na">actions</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.opensymphony.xwork2.ActionSupport</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.apache.struts2.convention.annotation.Action</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.apache.struts2.convention.annotation.Actions</span><span class="o">;</span>
<span class="nd">@InterceptorRefs</span><span class="o">({</span>
<span class="nd">@InterceptorRef</span><span class="o">(</span><span class="s">"interceptor-1"</span><span class="o">),</span>
<span class="nd">@InterceptorRef</span><span class="o">(</span><span class="s">"defaultStack"</span><span class="o">)</span>
<span class="o">})</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">HelloWorld</span> <span class="kd">extends</span> <span class="n">ActionSupport</span> <span class="o">{</span>
<span class="nd">@Action</span><span class="o">(</span><span class="n">value</span><span class="o">=</span><span class="s">"action1"</span><span class="o">,</span> <span class="n">interceptorRefs</span><span class="o">=</span><span class="nd">@InterceptorRef</span><span class="o">(</span><span class="s">"validation"</span><span class="o">))</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">execute</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">SUCCESS</span><span class="o">;</span>
<span class="o">}</span>
<span class="nd">@Action</span><span class="o">(</span><span class="n">value</span><span class="o">=</span><span class="s">"action2"</span><span class="o">)</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">doSomething</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">SUCCESS</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>The following interceptors will be applied to “action1”: <code class="highlighter-rouge">interceptor-1</code>, all interceptors from <code class="highlighter-rouge">defaultStack</code>,
and <code class="highlighter-rouge">validation</code>. For “action2”, <code class="highlighter-rouge">interceptor-1</code> and all interceptors from <code class="highlighter-rouge">defaultStack</code> will be applied to “action2”.</p>
<blockquote>
<p>If you get errors like “Unable to find interceptor class referenced by ref-name XYZ”. This means that the package
where Convention is placing your actions, does not extend the package where the interceptor is defined. To fix this
problem either 1)Use @ParentPackage annotation(or struts.convention.default.parent.package) passing the name of the
package that defines the interceptor, or 2) Create a package in XML that extends the package that defines the
interceptor, and use @ParentPackage(or struts.convention.default.parent.package) to point to it.</p>
</blockquote>
<h3 id="result-annotation">Result annotation</h3>
<p>The Convention plugin allows action classes to define different results for an action. Results fall into two categories,
global and local. Global results are shared across all actions defined within the action class. These results are
defined as annotations on the action class. Local results apply only to the action method they are defined on. Here is
an example of the different types of result annotations:</p>
<p><strong>com.example.actions.HelloWorld</strong></p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="n">com</span><span class="o">.</span><span class="na">example</span><span class="o">.</span><span class="na">actions</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.opensymphony.xwork2.ActionSupport</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.apache.struts2.convention.annotation.Action</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.apache.struts2.convention.annotation.Actions</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.apache.struts2.convention.annotation.Result</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.apache.struts2.convention.annotation.Results</span><span class="o">;</span>
<span class="nd">@Results</span><span class="o">({</span>
<span class="nd">@Result</span><span class="o">(</span><span class="n">name</span><span class="o">=</span><span class="s">"failure"</span><span class="o">,</span> <span class="n">location</span><span class="o">=</span><span class="s">"fail.jsp"</span><span class="o">)</span>
<span class="o">})</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">HelloWorld</span> <span class="kd">extends</span> <span class="n">ActionSupport</span> <span class="o">{</span>
<span class="nd">@Action</span><span class="o">(</span><span class="n">value</span><span class="o">=</span><span class="s">"/different/url"</span><span class="o">,</span>
<span class="n">results</span><span class="o">={</span><span class="nd">@Result</span><span class="o">(</span><span class="n">name</span><span class="o">=</span><span class="s">"success"</span><span class="o">,</span> <span class="n">location</span><span class="o">=</span><span class="s">"http://struts.apache.org"</span><span class="o">,</span> <span class="n">type</span><span class="o">=</span><span class="s">"redirect"</span><span class="o">)}</span>
<span class="o">)</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">execute</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">SUCCESS</span><span class="o">;</span>
<span class="o">}</span>
<span class="nd">@Action</span><span class="o">(</span><span class="s">"/another/url"</span><span class="o">)</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">doSomething</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">SUCCESS</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Parameters can be passed to results using the <strong>params</strong> attribute. The value of this attribute is a string array with
an even number of elements in the form <code class="highlighter-rouge">{"key0", "value0, "key1", "value1" ... "keyN", "valueN"}</code>. For example:</p>
<p><strong>com.example.actions.HelloWorld</strong></p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="n">com</span><span class="o">.</span><span class="na">example</span><span class="o">.</span><span class="na">actions</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.opensymphony.xwork2.ActionSupport</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.apache.struts2.convention.annotation.Action</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.apache.struts2.convention.annotation.Actions</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.apache.struts2.convention.annotation.Result</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.apache.struts2.convention.annotation.Results</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">HelloWorld</span> <span class="kd">extends</span> <span class="n">ActionSupport</span> <span class="o">{</span>
<span class="nd">@Action</span><span class="o">(</span><span class="n">value</span><span class="o">=</span><span class="s">"/different/url"</span><span class="o">,</span>
<span class="n">results</span><span class="o">={</span><span class="nd">@Result</span><span class="o">(</span><span class="n">name</span><span class="o">=</span><span class="s">"success"</span><span class="o">,</span> <span class="n">type</span><span class="o">=</span><span class="s">"httpheader"</span><span class="o">,</span> <span class="n">params</span><span class="o">={</span><span class="s">"status"</span><span class="o">,</span> <span class="s">"500"</span><span class="o">,</span> <span class="s">"errorMessage"</span><span class="o">,</span> <span class="s">"Internal Error"</span><span class="o">})}</span>
<span class="o">)</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">execute</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">SUCCESS</span><span class="o">;</span>
<span class="o">}</span>
<span class="nd">@Action</span><span class="o">(</span><span class="s">"/another/url"</span><span class="o">)</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">doSomething</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">SUCCESS</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>From 2.1.7 on, global results (defined on the class level) defined using annotations will be inherited. Child classes
can override the inherited result(s) by redefining it. Also, results defined at the method level take precedence
(overwrite), over results with the same name at the action level.</p>
<h3 id="namespace-annotation">Namespace annotation</h3>
<p>The namespace annotation allows the namespace for action classes to be changed instead of using the convention of the Java
package name. This annotation can be placed on an action class or within the package-info.java class that allows
annotations to be placed on Java packages. When this annotation is put on an action class, it applies to all actions
defined in the class, that are not fully qualified action URLs. When this annotation is place in the <code class="highlighter-rouge">package-info.java</code>
file, it changes the default namespace for all actions defined in the Java package. Here is an example of the annotation
on an action class:</p>
<p><strong>com.example.actions.HelloWorld</strong></p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="n">com</span><span class="o">.</span><span class="na">example</span><span class="o">.</span><span class="na">actions</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.opensymphony.xwork2.ActionSupport</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.apache.struts2.convention.annotation.Action</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.apache.struts2.convention.annotation.Namespace</span><span class="o">;</span>
<span class="nd">@Namespace</span><span class="o">(</span><span class="s">"/custom"</span><span class="o">)</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">HelloWorld</span> <span class="kd">extends</span> <span class="n">ActionSupport</span> <span class="o">{</span>
<span class="nd">@Action</span><span class="o">(</span><span class="s">"/different/url"</span><span class="o">)</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">execute</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">SUCCESS</span><span class="o">;</span>
<span class="o">}</span>
<span class="nd">@Action</span><span class="o">(</span><span class="s">"url"</span><span class="o">)</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">doSomething</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">SUCCESS</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>In this example, the action will respond to two different URLs <code class="highlighter-rouge">/different/url</code> and <code class="highlighter-rouge">/custom/url</code>. Here is an example
of using this annotation in the package-info.java file:</p>
<p><strong>com/example/actions/package-info.java</strong></p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@org</span><span class="o">.</span><span class="na">apache</span><span class="o">.</span><span class="na">struts2</span><span class="o">.</span><span class="na">convention</span><span class="o">.</span><span class="na">annotation</span><span class="o">.</span><span class="na">Namespace</span><span class="o">(</span><span class="s">"/custom"</span><span class="o">)</span>
<span class="kn">package</span> <span class="n">com</span><span class="o">.</span><span class="na">example</span><span class="o">.</span><span class="na">actions</span><span class="o">;</span>
</code></pre></div></div>
<p>This changes the default namespace for all actions defined in the package <code class="highlighter-rouge">com.example.actions</code>. This annotation however
doesn’t apply to sub-packages.</p>
<h3 id="resultpath-annotation">ResultPath annotation</h3>
<p>The ResultPath annotation allows applications to change the location where results are stored. This annotation can be
placed on an action class and also in the package-info.java file. Here is an example of using this annotation:</p>
<p><strong>com.example.actions.HelloWorl</strong></p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="n">com</span><span class="o">.</span><span class="na">example</span><span class="o">.</span><span class="na">actions</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.opensymphony.xwork2.ActionSupport</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.apache.struts2.convention.annotation.Action</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.apache.struts2.convention.annotation.ResultPath</span><span class="o">;</span>
<span class="nd">@ResultPath</span><span class="o">(</span><span class="s">"/WEB-INF/jsps"</span><span class="o">)</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">HelloWorld</span> <span class="kd">extends</span> <span class="n">ActionSupport</span> <span class="o">{</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">execute</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">SUCCESS</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>The result for this class will be located in <code class="highlighter-rouge">WEB-INF/jsps</code> rather than the default of <code class="highlighter-rouge">WEB-INF/content</code>.</p>
<h3 id="parentpackage-annotation">ParentPackage annotation</h3>
<p>The ParentPackage annotation allows applications to define different parent Struts package for specific action classes
or Java packages. Here is an example of using the annotation on an action class:</p>
<p><strong>com.example.actions.HelloWorld</strong></p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="n">com</span><span class="o">.</span><span class="na">example</span><span class="o">.</span><span class="na">actions</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.opensymphony.xwork2.ActionSupport</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.apache.struts2.convention.annotation.Action</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.apache.struts2.convention.annotation.ParentPackage</span><span class="o">;</span>
<span class="nd">@ParentPackage</span><span class="o">(</span><span class="s">"customXWorkPackage"</span><span class="o">)</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">HelloWorld</span> <span class="kd">extends</span> <span class="n">ActionSupport</span> <span class="o">{</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">execute</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">SUCCESS</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>To apply this annotation to all actions in a package (and subpackages), add it to package-info.java. An alternative
to this annotation is to set <code class="highlighter-rouge">struts.convention.default.parent.package</code> in XML.</p>
<h3 id="exceptionmapping-annotation">ExceptionMapping Annotation</h3>
<p>This annotation can be used to define exception mappings to actions. See the <em>exception mapping documentation</em> for more
details. These mappings can be applied to the class level, in which case they will be applied to all actions defined
on that class:</p>
<p><strong>ExceptionsActionLevelAction.java</strong></p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@ExceptionMappings</span><span class="o">({</span>
<span class="nd">@ExceptionMapping</span><span class="o">(</span><span class="n">exception</span> <span class="o">=</span> <span class="s">"java.lang.NullPointerException"</span><span class="o">,</span> <span class="n">result</span> <span class="o">=</span> <span class="s">"success"</span><span class="o">,</span> <span class="n">params</span> <span class="o">=</span> <span class="o">{</span><span class="s">"param1"</span><span class="o">,</span> <span class="s">"val1"</span><span class="o">})</span>
<span class="o">})</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">ExceptionsActionLevelAction</span> <span class="o">{</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">execute</span><span class="o">()</span> <span class="kd">throws</span> <span class="n">Exception</span> <span class="o">{</span>
<span class="k">return</span> <span class="kc">null</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>The parameters defined by <code class="highlighter-rouge">params</code> are passed to the result. Exception mappings can also be applied to the action level:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">ExceptionsMethodLevelAction</span> <span class="o">{</span>
<span class="nd">@Action</span><span class="o">(</span><span class="n">value</span> <span class="o">=</span> <span class="s">"exception1"</span><span class="o">,</span> <span class="n">exceptionMappings</span> <span class="o">=</span> <span class="o">{</span>
<span class="nd">@ExceptionMapping</span><span class="o">(</span><span class="n">exception</span> <span class="o">=</span> <span class="s">"java.lang.NullPointerException"</span><span class="o">,</span> <span class="n">result</span> <span class="o">=</span> <span class="s">"success"</span><span class="o">,</span> <span class="n">params</span> <span class="o">=</span> <span class="o">{</span><span class="s">"param1"</span><span class="o">,</span> <span class="s">"val1"</span><span class="o">})</span>
<span class="o">})</span>
<span class="kd">public</span> <span class="n">String</span> <span class="nf">run1</span><span class="o">()</span> <span class="kd">throws</span> <span class="n">Exception</span> <span class="o">{</span>
<span class="k">return</span> <span class="kc">null</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<h2 id="actions-in-jar-files">Actions in jar files</h2>
<p>By default the Convention plugin will <strong>not</strong> scan jar files for actions. For a jar to be scanned, its URL needs
to match at least one of the regular expressions in <code class="highlighter-rouge">struts.convention.action.includeJars</code> . In this example
<code class="highlighter-rouge">myjar1.jar</code> and <code class="highlighter-rouge">myjar2.jar</code> will be scanned:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;constant</span> <span class="na">name=</span><span class="s">"struts.convention.action.includeJars"</span> <span class="na">value=</span><span class="s">".*?/myjar1.*?jar(!/)?,.*?/myjar2*?jar(!/)?"</span>
</code></pre></div></div>
<p>Note that <strong>the regular expression will be evaluated against the URL of the jar, and not the file name</strong>, the jar URL
can contain a path to the jar file and a trailing <code class="highlighter-rouge">!/</code>.</p>
<h2 id="automatic-configuration-reloading">Automatic configuration reloading</h2>
<p>The Convention plugin can automatically reload configuration changes, made in classes the contain actions, without
restarting the container. This is a similar behavior to the automatic xml configuration reloading. To enable this
feature, add this to your <code class="highlighter-rouge">struts.xml</code> file:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;constant</span> <span class="na">name=</span><span class="s">"struts.devMode"</span> <span class="na">value=</span><span class="s">"true"</span><span class="nt">/&gt;</span>
<span class="nt">&lt;constant</span> <span class="na">name=</span><span class="s">"struts.convention.classes.reload"</span> <span class="na">value=</span><span class="s">"true"</span> <span class="nt">/&gt;</span>
</code></pre></div></div>
<p>This feature is experimental and has not been tested on all container, and it is <strong>strongly</strong> advised not to use it
in production environments.</p>
<h2 id="jboss">JBoss</h2>
<p>When using this plugin with JBoss, you need to set the following constants:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;constant</span> <span class="na">name=</span><span class="s">"struts.convention.exclude.parentClassLoader"</span> <span class="na">value=</span><span class="s">"true"</span> <span class="nt">/&gt;</span>
<span class="nt">&lt;constant</span> <span class="na">name=</span><span class="s">"struts.convention.action.fileProtocols"</span> <span class="na">value=</span><span class="s">"jar,vfsfile,vfszip"</span> <span class="nt">/&gt;</span>
</code></pre></div></div>
<p>You can also check the <em>JBoss 5</em>  page for more details.</p>
<h2 id="jetty-embedded">Jetty (embedded)</h2>
<p>When using this plugin with Jetty in embedded mode, you need to set the following constants:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;constant</span> <span class="na">name=</span><span class="s">"struts.convention.exclude.parentClassLoader"</span> <span class="na">value=</span><span class="s">"false"</span> <span class="nt">/&gt;</span>
<span class="nt">&lt;constant</span> <span class="na">name=</span><span class="s">"struts.convention.action.fileProtocols"</span> <span class="na">value=</span><span class="s">"jar,code-source"</span> <span class="nt">/&gt;</span>
</code></pre></div></div>
<h2 id="troubleshooting">Troubleshooting</h2>
<h3 id="tips">Tips</h3>
<ul>
<li>
<p>Make sure the namespace of the action is matched by one of the locators. The rest of the namespace after the locator,
will be the namespace of the action, and will be used to find the results. For example, a class called “ViewAction” in
the package “my.example.actions.orders” will be mapped to the URL /orders/view.action, and the results must be under
/WEB-INF/content/orders, like /WEB-INF/content/orders/view-success.jsp.</p>
</li>
<li>
<p>Add the <em>Config Browser Plugin</em> plugin to the lib folder or maven dependencies, and then visit:
<a href="http://localhost:8080/CONTEXT/config-browser/index.action">http://localhost:8080/CONTEXT/config-browser/index.action</a>,
to see the current action mappings.</p>
</li>
<li>
<p>The Convention plugin can generate a rather verbose output when set to debug mode for logging. Use “Trace” logging
level if you are using the JDK logger. If you are using Log4J, you can do something like:
log4j.logger.org.apache.struts2.convention=DEBUG</p>
</li>
</ul>
<h3 id="common-errors">Common Errors</h3>
<ol>
<li>
<p>I get an error like “There is no Action mapped for namespace /orders and action name view.”. This means that the URL
<code class="highlighter-rouge">/orders/view.action</code> is not mapping to any action class. Check the namespace and the name of the action.</p>
</li>
<li>
<p>I get an error like “No result defined for action my.example.actions.orders.ViewAction and result success”. This
means that the action was mapped to the right URL, but the Convention plugin was unable to find a <code class="highlighter-rouge">success</code> result
for it. Check that the result file exists, like <code class="highlighter-rouge">/WEB-INF/content/orders/view-success.jsp</code>.</p>
</li>
<li>
<p>I get lots of errors like “java.lang.Exception: Could not load org/apache/velocity/runtime/resource/loader/ClasspathResourceLoader.class”.
This happens when <code class="highlighter-rouge">struts.convention.action.includeJars</code> is matching jar URLs from external jars.</p>
</li>
<li>
<p>I am using a custom interceptor stack and I get an error like “Unable to find interceptor class referenced by ref-name XYZ”.
This means that the package where Convention is placing your actions, does not extend the package where the interceptor
is defined. To fix this problem either 1)Use @ParentPackage annotation (or <code class="highlighter-rouge">struts.convention.default.parent.package</code>)
passing the name of the package that defines the interceptor, or 2) Create a package in XML that extends the package
that defines the interceptor, and use @ParentPackage (or <code class="highlighter-rouge">struts.convention.default.parent.package</code>) to point to it.</p>
</li>
</ol>
<h2 id="overwriting-plugin-classes">Overwriting plugin classes</h2>
<p>The Convention plugin can be extended in the same fashion that Struts does. The following beans are defined by default:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;bean</span> <span class="na">type=</span><span class="s">"org.apache.struts2.convention.ActionConfigBuilder"</span> <span class="na">name=</span><span class="s">"convention"</span>
<span class="na">class=</span><span class="s">"org.apache.struts2.convention.PackageBasedActionConfigBuilder"</span><span class="nt">/&gt;</span>
<span class="c">&lt;!--
This interface defines how the action configurations for the current web application can be constructed. This must
find all actions that are not specifically defined in the struts XML files or any plugins. Furthermore, it must make
every effort to locate all action results as well.
--&gt;</span>
<span class="nt">&lt;bean</span> <span class="na">type=</span><span class="s">"org.apache.struts2.convention.ActionNameBuilder"</span> <span class="na">name=</span><span class="s">"convention"</span>
<span class="na">class=</span><span class="s">"org.apache.struts2.convention.SEOActionNameBuilder"</span><span class="nt">/&gt;</span>
<span class="c">&lt;!--
This interface defines the method that is used to create action names based on the name of a class.
--&gt;</span>
<span class="nt">&lt;bean</span> <span class="na">type=</span><span class="s">"org.apache.struts2.convention.ResultMapBuilder"</span> <span class="na">name=</span><span class="s">"convention"</span>
<span class="na">class=</span><span class="s">"org.apache.struts2.convention.DefaultResultMapBuilder"</span><span class="nt">/&gt;</span>
<span class="c">&lt;!--
This interface defines how results are constructed for an Action. The action information is supplied and the result is
a mapping of ResultConfig instances to the result name.
--&gt;</span>
<span class="nt">&lt;bean</span> <span class="na">type=</span><span class="s">"org.apache.struts2.convention.InterceptorMapBuilder"</span> <span class="na">name=</span><span class="s">"convention"</span>
<span class="na">class=</span><span class="s">"org.apache.struts2.convention.DefaultInterceptorMapBuilder"</span><span class="nt">/&gt;</span>
<span class="c">&lt;!--
This interface defines how interceptors are built from annotations.
--&gt;</span>
<span class="nt">&lt;bean</span> <span class="na">type=</span><span class="s">"org.apache.struts2.convention.ConventionsService"</span> <span class="na">name=</span><span class="s">"convention"</span>
<span class="na">class=</span><span class="s">"org.apache.struts2.convention.ConventionsServiceImpl"</span><span class="nt">/&gt;</span>
<span class="c">&lt;!--
This interface defines the conventions that are used by the convention plugin. In most cases the methods on this class
will provide the best default for any values and also handle locating overrides of the default via the annotations that
are part of the plugin.
--&gt;</span>
<span class="nt">&lt;constant</span> <span class="na">name=</span><span class="s">"struts.convention.actionConfigBuilder"</span> <span class="na">value=</span><span class="s">"convention"</span><span class="nt">/&gt;</span>
<span class="nt">&lt;constant</span> <span class="na">name=</span><span class="s">"struts.convention.actionNameBuilder"</span> <span class="na">value=</span><span class="s">"convention"</span><span class="nt">/&gt;</span>
<span class="nt">&lt;constant</span> <span class="na">name=</span><span class="s">"struts.convention.resultMapBuilder"</span> <span class="na">value=</span><span class="s">"convention"</span><span class="nt">/&gt;</span>
<span class="nt">&lt;constant</span> <span class="na">name=</span><span class="s">"struts.convention.interceptorMapBuilder"</span> <span class="na">value=</span><span class="s">"convention"</span><span class="nt">/&gt;</span>
<span class="nt">&lt;constant</span> <span class="na">name=</span><span class="s">"struts.convention.conventionsService"</span> <span class="na">value=</span><span class="s">"convention"</span><span class="nt">/&gt;</span>
</code></pre></div></div>
<p>To plugin a different implementation for one of these classes, implement the interface, define a bean for it, and set
the appropriate constant’s value with the name of the new bean, for example:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;bean</span> <span class="na">type=</span><span class="s">"org.apache.struts2.convention.ActionNameBuilder"</span> <span class="na">name=</span><span class="s">"MyActionNameBuilder"</span> <span class="na">class=</span><span class="s">"example.SultansOfSwingNameBuilder"</span><span class="nt">/&gt;</span>
<span class="nt">&lt;constant</span> <span class="na">name=</span><span class="s">"struts.convention.actionNameBuilder"</span> <span class="na">value=</span><span class="s">"MyActionNameBuilder"</span><span class="nt">/&gt;</span>
</code></pre></div></div>
<h2 id="configuration-reference">Configuration reference</h2>
<p>Add a <strong>constant</strong> element to your struts config file to change the value of a configuration setting, like:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;constant</span> <span class="na">name=</span><span class="s">"struts.convention.result.path"</span> <span class="na">value=</span><span class="s">"/WEB-INF/mytemplates/"</span><span class="nt">/&gt;</span>
</code></pre></div></div>
<table>
<thead>
<tr>
<th>Name</th>
<th>Default Value</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>struts.convention.action.alwaysMapExecute</td>
<td>true</td>
<td>Set to false, to prevent Convention from creating a default mapping to “execute” when there are other methods annotated as actions in the class</td>
</tr>
<tr>
<td>struts.convention.action.includeJars</td>
<td> </td>
<td>Comma separated list of regular expressions of jar URLs to be scanned. eg. “.<strong>myJar-0\.2.</strong>, .<strong>thirdparty-0\.1.</strong></td>
</tr>
<tr>
<td>struts.convention.action.packages</td>
<td> </td>
<td>An optional list of action packages that this should create configuration for (they don’t need to match a locator pattern)</td>
</tr>
<tr>
<td>struts.convention.result.path</td>
<td>/WEB-INF/content/</td>
<td>Directory where templates are located</td>
</tr>
<tr>
<td>struts.convention.result.flatLayout</td>
<td>true</td>
<td>If set to false, the result can be put in its own directory: resultsRoot/namespace/actionName/result.extension</td>
</tr>
<tr>
<td>struts.convention.action.suffix</td>
<td>Action</td>
<td>Suffix used to find actions based on class names</td>
</tr>
<tr>
<td>struts.convention.action.disableScanning</td>
<td>false</td>
<td>Scan packages for actions</td>
</tr>
<tr>
<td>struts.convention.action.mapAllMatches</td>
<td>false</td>
<td>Create action mappings, even if no @Action is found</td>
</tr>
<tr>
<td>struts.convention.action.checkImplementsAction</td>
<td>true</td>
<td>Check if an action implements com.opensymphony.xwork2.Action to create an action mapping</td>
</tr>
<tr>
<td>struts.convention.default.parent.package</td>
<td>convention-default</td>
<td>Default parent package for action mappins</td>
</tr>
<tr>
<td>struts.convention.action.name.lowercase</td>
<td>true</td>
<td>Convert action name to lowercase</td>
</tr>
<tr>
<td>struts.convention.action.name.separator</td>
<td>-</td>
<td>Separator used to build the action name, MyAction -&gt; my-action. This character is also used as the separator between the action name and the result in templates, like action-result.jsp</td>
</tr>
<tr>
<td>struts.convention.package.locators</td>
<td>action,actions,struts,struts2</td>
<td>Packages whose name end with one of these strings will be scanned for actions</td>
</tr>
<tr>
<td>struts.convention.package.locators.disable</td>
<td>false</td>
<td>Disable the scanning of packages based on package locators</td>
</tr>
<tr>
<td>struts.convention.exclude.packages</td>
<td><code class="highlighter-rouge">org.apache.struts.\*</code>, <code class="highlighter-rouge">org.apache.struts2.\*</code>, <code class="highlighter-rouge">org.springframework.web.struts.\*</code>, <code class="highlighter-rouge">org.springframework.web.struts2.\*</code>, <code class="highlighter-rouge">org.hibernate.\*</code></td>
<td>Packages excluded from the action scanning, packages already excluded cannot be included in other way, eg. org.demo.actions.exclude is specified as a part of the struts.convention.exclude.packages so all packages below are also excluded, eg. org.demo.actions.exclude.include even if <strong>include</strong> is specified as a struts.convention.package.locators or struts.convention.action.packages</td>
</tr>
<tr>
<td>struts.convention.package.locators.basePackage</td>
<td> </td>
<td>If set, only packages that start with its value will be scanned for actions</td>
</tr>
<tr>
<td>struts.convention.relative.result.types</td>
<td>dispatcher,velocity,freemarker</td>
<td>The list of result types that can have locations that are relative and the result location (which is the resultPath plus the namespace) prepended to them</td>
</tr>
<tr>
<td>struts.convention.redirect.to.slash</td>
<td>true</td>
<td>A boolean parameter that controls whether or not this will handle unknown actions in the same manner as Apache, Tomcat and other web servers. This handling will send back a redirect for URLs such as /foo to /foo/ if there doesn’t exist an action that responds to /foo</td>
</tr>
<tr>
<td>struts.convention.classLoader.excludeParent</td>
<td>true</td>
<td>Exclude URLs found by the parent class loader from the list of URLs scanned to find actions (needs to be set to false for JBoss 5)</td>
</tr>
<tr>
<td>struts.convention.action.eagerLoading</td>
<td>false</td>
<td>If set, found action classes will be instantiated by the ObjectFactory to accelerate future use, setting it up can clash with Spring managed beans</td>
</tr>
</tbody>
</table>
</section>
</article>
<footer class="container">
<div class="col-md-12">
Copyright &copy; 2000-2018 <a href="http://www.apache.org/">The Apache Software Foundation </a>.
All Rights Reserved.
</div>
<div class="col-md-12">
Apache Struts, Struts, Apache, the Apache feather logo, and the Apache Struts project logos are
trademarks of The Apache Software Foundation.
</div>
<div class="col-md-12">Logo and website design donated by <a href="https://softwaremill.com/">SoftwareMill</a>.</div>
</footer>
<script>!function (d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (!d.getElementById(id)) {
js = d.createElement(s);
js.id = id;
js.src = "//platform.twitter.com/widgets.js";
fjs.parentNode.insertBefore(js, fjs);
}
}(document, "script", "twitter-wjs");</script>
<script src="https://apis.google.com/js/platform.js" async="async" defer="defer"></script>
<div id="fb-root"></div>
<script>(function (d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return;
js = d.createElement(s);
js.id = id;
js.src = "//connect.facebook.net/en_GB/all.js#xfbml=1";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));</script>
</body>
</html>