Source

xwork / docs / wikidocs / Components.html

<html>
    <head>
        <title>XWork : Components</title>
	    <link rel="stylesheet" href="styles/site.css" type="text/css" />
        <META http-equiv="Content-Type" content="text/html; charset=UTF-8">	    
    </head>

    <body>
	    <table class="pagecontent" border="0" cellpadding="0" cellspacing="0" width="100%" bgcolor="#ffffff">
		    <tr>
			    <td valign="top" class="pagebody">
				    <div class="pageheader">
					    <span class="pagetitle">
                            XWork : Components
                                                    </span>
				    </div>
				    <div class="pagesubheading">
					    This page last changed on Jun 25, 2004 by <font color="#0050B2">unkyaku</font>.
				    </div>

				    <p class="paragraph"><h2 style="margin: 4px 0px 4px 0px;" class="heading2"><a name="Components-Overview"> Overview</a></h2></p><a href="XWork.html" title="XWork">XWork</a> provides the ComponentManager interface (and a corresponding implementation in the DefaultComponentManager class) to allow a design pattern known as <b class="strong">Inversion of Control</b> (or <b class="strong">IoC</b> for short) to be applied to your actions or other arbitrary objects. In a nutshell, the IoC pattern allows a parent object (in this case XWork&#039;s ComponentManager instance) to control a client object (usually an action, but it could be any object that implements the appropriate <em class="emphasis">enabler</em>).<p class="paragraph">You may also want to look at <a href="http://wiki.opensymphony.com//display/WW/Components" title="Components">WW:Components</a> to see how WW2 uses XWork&#039;s IoC architecture.</p><h2 class="heading2"><a name="Components-WhyIoC%3F"> Why IoC?</a></h2><p class="paragraph">So why is IoC useful? It means that you can develop components (generally services of some sort) in a top-down fashion, without the need to build a registry class that the client must then call to obtain the component instance.</p>Traditionally when implementing services you are probably used to following steps similar to these:
<ol>
<li> Write the component (eg an ExchangeRateService)</li>
<li> Write the client class (eg an XWork action)</li>
<li> Write a registry class that holds the component object (eg Registry)</li>
<li> Write code that gives the component object to the registry (eg Registry.registerService(new MyExchangeRateService()))</li>
<li> Use the registry to obtain the service from your client class (eg ExchangeRateService ers = Registry.getExchangeRateService())</li>
<li> Make calls to the component from the client class (eg String baseCurrencyCode = ers.getBaseCurrency())</li>
</ol><br/>
Using IoC, the process is reduced to the following:
<ol>
<li> Write the component class (eg an ExchangeRateService)</li>
<li> Register the component class with XWork (eg componentManager.addEnabler(MyExchangeRateService, ExchangeRateAware))</li>
<li> Write the client class, making sure it implements the enabling interface (eg an XWork action that implements ExchangeRateAware)</li>
<li> Access the component instance directly from your client action (eg String baseCurencyCode = ers.getBaseCurrency())</li>
</ol><br/>
XWork takes care of passing components through to enabled action classes or other components.<p class="paragraph">Some other benefits that IoC can provide include:</p><ul class="star">
<li> A component describes itself. When you instantiate a component, you can easily determine what dependencies it requires without looking at the source or using trial and error.</li>
<li> Dependencies can be discovered easily using reflection. This has many benefits ranging from diagram generation to runtime optimization (by determining in advance which components will be needed to fufill a request and preparing them asyncronously, for example).</li>
<li> Avoids the super-uber-mega-factory pattern where all the components of the app are held together by a single class that is directly tied back to other domain specific classes, making it hard to &#039;just use that one class&#039;.</li>
<li> Adheres to Law of Demeter. Some people think this is silly, but in practise I&#039;ve found it works much better. Each class is coupled to only what it actually uses (and it should never use too much) and no more. This encourages smaller responsibility specific classes which leads to cleaner design.</li>
<li> Allows context to be isolated and explicitly passed around. ThreadLocals may be ok in a web-app, but they aren&#039;t well suited for high concurrency async applications (such as message driven applications).</li>
</ul>
<h2 class="heading2"><a name="Components-Configurationxwork.xml"> Configuration - xwork.xml</a></h2><p class="paragraph">The ComponentInterceptor class is used to apply the IoC pattern to XWork actions (ie, to supply components to actions). The ComponentInterceptor should be declared in the &lt;interceptors&gt; block of xwork.xml as follows:</p><div class="code"><div class="codeContent">
<pre><span class="xml&#45;tag">&lt;interceptor name=<span class="xml&#45;quote">&quot;component&quot;</span>&#10;        class=<span class="xml&#45;quote">&quot;com.opensymphony.xwork.interceptor.component.ComponentInterceptor&quot;</span>/&gt;</span></pre>
</div></div><p class="paragraph">You should ensure that any actions that are to be supplied with components have this interceptor applied. (See <a href="Interceptors.html" title="Interceptors">XW:Interceptors</a> for information on how to apply interceptors to actions.)</p>If you want to apply IoC to objects other than actions or other components, you will need to use the ComponentManager object directly.<p class="paragraph"><h2 class="heading2"><a name="Components-WritingComponentClasses"> Writing Component Classes</a></h2></p>The actual component class can be virtually anything you like. The only constraints on it are that it must be a concrete class with a default constructor so that XWork can create instances of it as required. Optionally, a component may implement the Initializable and/or Disposable interfaces so it will receive lifecycle events just after it is created or before it is destroyed. Simply:<br/>

<div class="code"><div class="codeContent">
<pre><span class="java&#45;keyword">public</span> class MyComponent <span class="java&#45;keyword">implements</span> Intializable, Disposable &#123;&#10;    <span class="java&#45;keyword">public</span> void init () &#123;&#10;         <span class="java&#45;comment">//<span class="java&#45;keyword">do</span> initialization here</span>&#10;    &#125;&#10;&#10;    <span class="java&#45;keyword">public</span> void dispose() &#123;&#10;         <span class="java&#45;comment">//<span class="java&#45;keyword">do</span> any clean up necessary before garbage collection of <span class="java&#45;keyword">this</span> component</span>&#10;    &#125;&#10;&#125;</pre>
</div></div><p class="paragraph"><h2 class="heading2"><a name="Components-ComponentDependencies"> Component Dependencies</a></h2></p>One feature that is not immediately obvious is that it is possible for components to depend on other components. For example if the ExchangeRateService described above depended on a Configuration component, XWork will pass the Configuration component through to the ExchangeRateService instance after ExchangeRateService is instantiated. Note that XWork automatically takes care of initializing the components in the correct order, so if A is an action or component that depends on B and C, and B depends on C and if A, B, and C have not been previously instantiated, the ComponentManager will in the following order:<br/>

<ol>
<li> Instantiate C and call it&#039;s init() method if it implements Initializable.</li>
<li> Instantiate B, then using the enabler method, set C to be used by B</li>
<li> Call B&#039;s init() method, if it implements Intitializable.</li>
<li> Set B using B&#039;s enabler method to be used by A.</li>
</ol><br/>
And so on and so forth. Of course, if there are instances of B or C that would be reused in this case, those instances would be passed using the enabler method rather than a new instance.<p class="paragraph"><h2 class="heading2"><a name="Components-WritingEnablers"> Writing Enablers</a></h2></p>An enabler should consist of just a single method that accepts a single parameter. The parameter class should either be the component class that is to be enabled, or one of the component&#039;s superclasses. XWork does not care what the name of the enabler&#039;s method is.<p class="paragraph">Here is an example of what the ExchangeRateAware enabler might look like:</p><div class="code"><div class="codeContent">
<pre><span class="java&#45;keyword">public</span>&#160;<span class="java&#45;keyword">interface</span> ExchangeRateAware &#123;&#10;    <span class="java&#45;keyword">public</span> void setExchangeRateService(ExchangeRateService exchangeRateService);&#10;&#125;</pre>
</div></div><p class="paragraph">Note that typically an enabler would be an interface, however there is nothing to prevent you from using a class instead if you so choose.</p><h2 class="heading2"><a name="Components-Writing%26quot%3BEnableraware%26quot%3BActions"> Writing &quot;Enabler-aware&quot; Actions</a></h2><p class="paragraph">All an action needs to do is implement the relevant enabler interface. XWork will then call the action&#039;s enabler method just prior to the action&#039;s execution. As a simple example:</p><div class="code"><div class="codeContent">
<pre><span class="java&#45;keyword">public</span> class MyAction <span class="java&#45;keyword">extends</span> ActionSupport <span class="java&#45;keyword">implements</span> ExchangeRateAware &#123;&#10;    ExchangeRateService ers;&#10;    &#10;    <span class="java&#45;keyword">public</span> void setExchangeRateService(ExchangeRateService exchangeRateService) &#123;&#10;        ers = exchangeRateService;&#10;    &#125;&#10;    &#10;    <span class="java&#45;keyword">public</span>&#160;<span class="java&#45;object">String</span> execute() <span class="java&#45;keyword">throws</span> Exception &#123;&#10;        <span class="java&#45;object">System</span>.out.println(<span class="java&#45;quote">&quot;The base currency is &quot;</span> &#43; ers.getBaseCurrency());&#10;    &#125;&#10;&#125;</pre>
</div></div><p class="paragraph">If you have an object that is not an action or another component, you must explictly tell XWork to supply any enabled components to your object by calling componentManager.initializeObject(enabledObject);</p><h2 class="heading2"><a name="Components-Usinganexternalreferenceresolver"> Using an external reference resolver</a></h2><p class="paragraph">You can also use an external reference resolver in XWork, i.e., references that will be resolved not by XWork itself. One such example is using an external resolver to integrate XWork with the <a href="http://www.springframework.org" title="Visit page outside Confluence">Spring Framework</a></p>You just need to write an external reference resolver and then tell XWork to use it in the package declaration:
<div class="code"><div class="codeContent">
<pre><span class="xml&#45;tag">&lt;package&#10;    name=<span class="xml&#45;quote">&quot;default&quot;</span>&#10;    externalReferenceResolver=<span class="xml&#45;quote">&quot;com.atlassian.xwork.ext.SpringServletContextReferenceResolver&quot;</span>&gt;</span></pre>
</div></div><br/>
Now, to use external references you do something like this:
<div class="code"><div class="codeContent">
<pre><span class="xml&#45;tag">&lt;external&#45;ref name=<span class="xml&#45;quote">&quot;foo&quot;</span>&gt;</span>Foo<span class="xml&#45;tag">&lt;/external&#45;ref&gt;</span></pre>
</div></div><br/>
Where the name attribute is the setter method name and Foo is the reference to lookup.<p class="paragraph">For more details and sample code about this integration, take a look at the javadocs to the com.opensymphony.xwork.config.ExternalReferenceResolver class (unfortunately unavailable online) and at <a href="http://jira.opensymphony.com/secure/ViewIssue.jspa?key=XW-122" title="Visit page outside Confluence">XW-122</a></p>-Chris

				    
                    			    </td>
		    </tr>
	    </table>
	    <table border="0" cellpadding="0" cellspacing="0" width="100%">
			<tr>
				<td height="12" background="border/border_bottom.gif"><img src="border/spacer.gif" width="1" height="1" border="0"/></td>
			</tr>
		    <tr>
			    <td align="center"><font color="grey">Document generated by Confluence on Dec 12, 2004 12:35</font></td>
		    </tr>
	    </table>
    </body>
</html>