blob: f7c86d0aefe8ea3d8b3c153f086415de1a49c6ba [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>Extending an Application with Custom Plugins</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="/css/syntax.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>
<!-- Matomo -->
<script>
var _paq = window._paq = window._paq || [];
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
/* We explicitly disable cookie tracking to avoid privacy issues */
_paq.push(['disableCookies']);
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
var u="//analytics.apache.org/";
_paq.push(['setTrackerUrl', u+'matomo.php']);
_paq.push(['setSiteId', '41']);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
})();
</script>
<!-- End Matomo Code -->
</head>
<body>
<a href="https://github.com/apache/struts" class="github-ribbon">
<img decoding="async" loading="lazy" style="position: absolute; right: 0; border: 0;" width="149" height="149" src="https://github.blog/wp-content/uploads/2008/12/forkme_right_red_aa0000.png?resize=149%2C149" class="attachment-full size-full" alt="Fork me on GitHub" data-recalc-dims="1">
</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-2024.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>
<li><a href="https://privacy.apache.org/policies/privacy-policy-public.html">Privacy Policy</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><a href="/commercial-support.html">Commercial Support</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 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="/contributors/">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/extending-an-application-with-custom-plugins.md" title="Edit this page on GitHub">Edit on GitHub</a>
<a href="index.html" title="back to Plugins"><< back to Plugins</a>
<h1 class="no_toc" id="extending-an-application-with-custom-plugins">Extending an Application with Custom Plugins</h1>
<ul id="markdown-toc">
<li><a href="#the-interface" id="markdown-toc-the-interface">The Interface</a></li>
<li><a href="#the-plugin" id="markdown-toc-the-plugin">The Plugin</a></li>
<li><a href="#the-action" id="markdown-toc-the-action">The Action</a></li>
<li><a href="#the-jsp" id="markdown-toc-the-jsp">The JSP</a></li>
</ul>
<p>In this tutorial, we will show how easily our application can be made extensible using the Struts 2 plugin mechanism.
To keep the demonstration simple, our plugin will expose a JavaBean that writes a message. Plugins may include any
combination of JavaBeans, Actions, Interceptors, Results or other resources we’d like available to an application.</p>
<h2 id="the-interface">The Interface</h2>
<p>At runtime, plugins are retrieved and referenced via an Interface. So, first, we should define an interface that our
plugin will implement. This interface must be available to both our web application and the plugin. To reduce coupling
between the web application and the plugins, keep the interface in a separate JAR.</p>
<p><strong>IMyPlugin.java</strong></p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">example</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">interface</span> <span class="nc">IMyPlugIn</span> <span class="o">{</span>
<span class="nc">String</span> <span class="nf">saySomething</span><span class="o">();</span>
<span class="o">}</span>
</code></pre></div></div>
<h2 id="the-plugin">The Plugin</h2>
<p>Now that we have an interface to implement we’ll create the plugin. At load time, the framework looks for JARs
containing a <code class="language-plaintext highlighter-rouge">struts-plugin.xml</code> file at the root of the archive. To create a plugin, all we need to do is build
a JAR and put the expected <code class="language-plaintext highlighter-rouge">struts-plugin.xml</code> at the root.</p>
<p>To get started, let’s create a class that implements our IMyPlugin interface.</p>
<p><strong>MyPlugin.java</strong></p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">example.impl</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">example.IMyPlugin</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">MyPlugin</span> <span class="kd">implements</span> <span class="nc">IMyPlugin</span> <span class="o">{</span>
<span class="kd">public</span> <span class="nc">String</span> <span class="nf">saySomething</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="s">"We don't need no education"</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Internally, the framework utilizes a number of JavaBeans. We can use the <code class="language-plaintext highlighter-rouge">bean</code> element to add our own JavaBeans
to the set managed by the framework.</p>
<p><strong>struts-default.xml</strong></p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;?xml version="1.0" encoding="UTF-8" ?&gt;</span>
<span class="cp">&lt;!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd"&gt;</span>
<span class="nt">&lt;struts&gt;</span>
<span class="nt">&lt;bean</span> <span class="na">type=</span><span class="s">"example.IMyInterface"</span> <span class="na">class=</span><span class="s">"example.impl.MyPlugin"</span> <span class="na">name=</span><span class="s">"myPlugin"</span><span class="nt">/&gt;</span>
<span class="nt">&lt;/struts&gt;</span>
</code></pre></div></div>
<p>Finally, to install the plugin, drop the JAR file under WEB-INF/lib.</p>
<h2 id="the-action">The Action</h2>
<p>The JavaBeans configured by <code class="language-plaintext highlighter-rouge">bean</code> elements can be retrieved via a Container provided by XWork 2. We obtain
a reference to the Container by using the <code class="language-plaintext highlighter-rouge">@Inject</code> notation. (This notation is part of the Guice framework that
XWork and Struts use under the covers.) The framework predefines a Container object, and the <code class="language-plaintext highlighter-rouge">@Inject</code> annotation tells
the framework to set its Container object to the Action property.</p>
<p>We might want to supply a number of JavaBeans to the application this way. In the Action, we will obtain a reference
to the entire set of JavaBeans that might have been plugged in. Then, we can scroll through the set, displaying each
JavaBean’s message.</p>
<p><strong>MyAction.java</strong></p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">example.actions</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">example.IMyPlugin</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">MyAction</span> <span class="kd">extends</span> <span class="nc">ActionSupport</span> <span class="o">{</span>
<span class="nc">Set</span><span class="o">&lt;</span><span class="nc">IMyPlugin</span><span class="o">&gt;</span> <span class="n">plugins</span><span class="o">;</span>
<span class="nd">@Inject</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">setContainer</span><span class="o">(</span><span class="nc">Container</span> <span class="n">container</span><span class="o">)</span> <span class="o">{</span>
<span class="nc">Set</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;</span> <span class="n">names</span> <span class="o">=</span> <span class="n">container</span><span class="o">.</span><span class="na">getInstanceNames</span><span class="o">(</span><span class="nc">IMyPlugin</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
<span class="n">plugins</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">HashSet</span><span class="o">&lt;</span><span class="nc">IMyPlugin</span><span class="o">&gt;();</span>
<span class="k">for</span> <span class="o">(</span><span class="nc">String</span> <span class="n">name</span> <span class="o">:</span> <span class="n">names</span><span class="o">)</span> <span class="o">{</span>
<span class="n">plugins</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">container</span><span class="o">.</span><span class="na">getInstance</span><span class="o">(</span><span class="nc">IMyPlugin</span><span class="o">.</span><span class="na">class</span><span class="o">,</span> <span class="n">name</span><span class="o">));</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="nc">Set</span><span class="o">&lt;</span><span class="nc">IMyPlugin</span><span class="o">&gt;</span> <span class="nf">getPlugins</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="k">this</span><span class="o">.</span><span class="na">plugins</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>As seen by the Action class code, it’s important to define a unique interface for any beans that we plugin, so that
we can identify our beans later.</p>
<p>In the same way that we plugged in this JavaBean, we could also plugin and configure Action classes, Interceptors,
Results, or any other JAR-able resource that an application might utilize.</p>
<h2 id="the-jsp">The JSP</h2>
<p>Let’s do something with those plugins:</p>
<p><strong>Page.jsp</strong></p>
<div class="language-jsp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;s:iterator </span><span class="na">id=</span><span class="s">"plugin"</span><span class="na"> value=</span><span class="s">"plugins"</span><span class="nt">&gt;</span>
<span class="nt">&lt;s:property </span><span class="na">value=</span><span class="s">"#plugin.saySomething()"</span><span class="nt">/&gt;</span>
<span class="nt">&lt;/s:iterator&gt;</span>
</code></pre></div></div>
</section>
</article>
<footer class="container">
<div class="col-md-12">
Copyright &copy; 2000-2022 <a href="https://www.apache.org/">The Apache Software Foundation</a>.
Apache Struts, Struts, Apache, the Apache feather logo, and the Apache Struts project logos are
trademarks of The Apache Software Foundation. All Rights Reserved.
</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>