You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1092 lines
55 KiB
1092 lines
55 KiB
<?xml version="1.0" encoding="UTF-8"?> |
|
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" |
|
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd"> |
|
|
|
<chapter id="web-integration" xmlns:xi="http://www.w3.org/2001/XInclude"> |
|
<title>Integrating with other web frameworks</title> |
|
|
|
<section id="intro"> |
|
<title>Introduction</title> |
|
<para> |
|
This chapter details Spring's integration with third party web frameworks |
|
such as <ulink url="http://java.sun.com/javaee/javaserverfaces/">JSF</ulink>, |
|
<ulink url="http://struts.apache.org/">Struts</ulink>, |
|
<ulink url="http://www.opensymphony.com/webwork/">WebWork</ulink>, and |
|
<ulink url="http://tapestry.apache.org/">Tapestry</ulink>. |
|
</para> |
|
|
|
<!-- insert some content about Spring Web Flow here --> |
|
<xi:include href="swf-sidebar.xml"/> |
|
|
|
<para> |
|
One of the core value propositions of the Spring Framework is that of |
|
enabling <emphasis>choice</emphasis>. In a general sense, Spring does not |
|
force one to use or buy into any particular architecture, technology, or |
|
methodology (although it certainly recommends some over others). This freedom |
|
to pick and choose the architecture, technology, or methodology that is most |
|
relevant to a developer and his or her development team is arguably most evident |
|
in the web area, where Spring provides its own web framework |
|
(<link linkend="mvc">Spring MVC</link>), while at the same time providing integration |
|
with a number of popular third party web frameworks. This allows one to continue |
|
to leverage any and all of the skills one may have acquired in a |
|
particular web framework such as Struts, while at the same time being able to |
|
enjoy the benefits afforded by Spring in other areas such as data access, |
|
declarative transaction management, and flexible configuration and application |
|
assembly. |
|
</para> |
|
<para> |
|
Having dispensed with the woolly sales patter (c.f. the previous paragraph), |
|
the remainder of this chapter will concentrate upon the meaty details of |
|
integrating your favourite web framework with Spring. One thing that is often |
|
commented upon by developers coming to Java from other languages is the seeming |
|
super-abundance of web frameworks available in Java... there are indeed a great |
|
number of web frameworks in the Java space; in fact there are far too many to |
|
cover with any semblance of detail in a single chapter. This chapter thus picks |
|
four of the more popular web frameworks in Java, starting with the Spring |
|
configuration that is common to all of the supported web frameworks, and then |
|
detailing the specific integration options for each supported web framework. |
|
</para> |
|
<para> |
|
<emphasis> |
|
Please note that this chapter does not attempt to explain how to use any |
|
of the supported web frameworks. For example, if you want to use Struts for |
|
the presentation layer of your web application, the assumption is that you |
|
are already familiar with Struts. If you need further details about any of |
|
the supported web frameworks themselves, please do consult the section |
|
entitled <xref linkend="web-integration-resources" /> at the end of this chapter. |
|
</emphasis> |
|
</para> |
|
</section> |
|
|
|
<section id="web-integration-common"> |
|
<title>Common configuration</title> |
|
<para> |
|
Before diving into the integration specifics of each supported web framework, let |
|
us first take a look at the Spring configuration that <emphasis>not</emphasis> |
|
specific to any one web framework. (This section is equally applicable to Spring's |
|
own web framework, Spring MVC.) |
|
</para> |
|
<para> |
|
One of the concepts (for want of a better word) espoused by (Spring's) lightweight |
|
application model is that of a layered architecture. Remember that in a 'classic' |
|
layered architecture, the web layer is but one of many layers... it serves as one |
|
of the entry points into a server side application, and it delegates to service |
|
objects (facades) defined in a service layer to satisfy business specific (and |
|
presentation-technology agnostic) use cases. In Spring, these service objects, |
|
any other business-specific objects, data access objects, etc. exist in a |
|
distinct 'business context', which contains <emphasis>no</emphasis> web or |
|
presentation layer objects (presentation objects such as Spring MVC controllers |
|
are typically configured in a distinct 'presentation context'). This section |
|
details how one configures a Spring container (a |
|
<classname>WebApplicationContext</classname>) that contains all of the |
|
'business beans' in one's application. |
|
</para> |
|
<para> |
|
Onto specifics... all that one need do is to declare a |
|
<ulink url="http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/web/context/ContextLoaderListener.html"><classname>ContextLoaderListener</classname></ulink> |
|
in the standard J2EE servlet <literal>web.xml</literal> file of one's web application, |
|
and add a <literal>contextConfigLocation</literal> <context-param/> section |
|
(in the same file) that defines which set of Spring XML cpnfiguration files to load. |
|
</para> |
|
<para> |
|
Find below the <listener/> configuration: |
|
</para> |
|
<programlisting language="xml"><![CDATA[<listener> |
|
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> |
|
</listener>]]></programlisting> |
|
<note> |
|
<para> |
|
Listeners were added to the Servlet API in version 2.3; listener startup order was |
|
finally clarified in Servlet 2.4. If you have a Servlet 2.3 container, you can use the |
|
<ulink url="http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/web/context/ContextLoaderServlet.html"><classname>ContextLoaderServlet</classname></ulink> |
|
to achieve the same functionality in a 100% portable fashion (with respect to startup order). |
|
</para> |
|
</note> |
|
<para> |
|
Find below the <context-param/> configuration: |
|
</para> |
|
<programlisting language="xml"><![CDATA[<context-param> |
|
<param-name>contextConfigLocation</param-name> |
|
<param-value>/WEB-INF/applicationContext*.xml</param-value> |
|
</context-param>]]></programlisting> |
|
<para> |
|
If you don't specify the <literal>contextConfigLocation</literal> |
|
context parameter, the <classname>ContextLoaderListener</classname> will look |
|
for a file called <literal>/WEB-INF/applicationContext.xml</literal> to load. |
|
Once the context files are loaded, Spring creates a |
|
<ulink url="http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/web/context/WebApplicationContext.html"><classname>WebApplicationContext</classname></ulink> |
|
object based on the bean definitions and stores it in the |
|
<interface>ServletContext</interface> of one's web application. |
|
</para> |
|
<para> |
|
All Java web frameworks are built on top of the Servlet API, and so one can |
|
use the following code snippet to get access to this 'business context' |
|
<interface>ApplicationContext</interface> created by the |
|
<classname>ContextLoaderListener</classname>. |
|
</para> |
|
<programlisting language="java"><![CDATA[WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(servletContext);]]></programlisting> |
|
<para> |
|
The <ulink url="http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/web/context/support/WebApplicationContextUtils.html"><classname>WebApplicationContextUtils</classname></ulink> |
|
class is for convenience, so you don't have to remember the name of the |
|
<interface>ServletContext</interface> attribute. Its <emphasis>getWebApplicationContext()</emphasis> |
|
method will return <literal>null</literal> if an object doesn't exist under the |
|
<literal>WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE</literal> key. Rather |
|
than risk getting <classname>NullPointerExceptions</classname> in your application, it's |
|
better to use the <literal>getRequiredWebApplicationContext()</literal> method. This |
|
method throws an exception when the <interface>ApplicationContext</interface> is missing. |
|
</para> |
|
<para> |
|
Once you have a reference to the <classname>WebApplicationContext</classname>, |
|
you can retrieve beans by their name or type. Most developers retrieve beans |
|
by name, then cast them to one of their implemented interfaces. |
|
</para> |
|
<para> |
|
Fortunately, most of the frameworks in this section have simpler ways of looking up |
|
beans. Not only do they make it easy to get beans from a Spring container, but they |
|
also allow you to use dependency injection on their controllers. Each web framework |
|
section has more detail on its specific integration strategies. |
|
</para> |
|
</section> |
|
|
|
<section id="jsf"> |
|
<title>JavaServer Faces 1.1 and 1.2</title> |
|
<para> |
|
JavaServer Faces (JSF) is the JCP's standard component-based, event-driven |
|
web user interface framework. As of Java EE 5, it is an official part of |
|
the Java EE umbrella. |
|
</para> |
|
<para> |
|
For a popular JSF runtime as well as for popular JSF component libraries, check |
|
out the <ulink url="http://myfaces.apache.org/">Apache MyFaces project</ulink>. |
|
The MyFaces project also provides common JSF extensions such as |
|
<ulink url="http://myfaces.apache.org/orchestra/">MyFaces Orchestra</ulink>: |
|
a Spring-based JSF extension that provides rich conversation scope support. |
|
</para> |
|
<note> |
|
<para> |
|
Spring Web Flow 2.0 provides rich JSF support through its newly |
|
established Spring Faces module, both for JSF-centric usage |
|
(as described in this section) and for Spring-centric usage |
|
(using JSF views within a Spring MVC dispatcher). Check out the |
|
<ulink url="http://www.springframework.org/webflow">Spring Web Flow website</ulink> |
|
for details! |
|
</para> |
|
</note> |
|
<para> |
|
The key element in Spring's JSF integration is the JSF 1.1 |
|
<classname>VariableResolver</classname> mechanism. On JSF 1.2, |
|
Spring supports the <classname>ELResolver</classname> mechanism |
|
as a next-generation version of JSF EL integration. |
|
</para> |
|
<section id="jsf-delegatingvariableresolver"> |
|
<title>DelegatingVariableResolver (JSF 1.1/1.2)</title> |
|
<para> |
|
The easiest way to integrate one's Spring middle-tier with one's |
|
JSF web layer is to use the |
|
<ulink url="http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/web/jsf/DelegatingVariableResolver.html"> |
|
<classname>DelegatingVariableResolver</classname></ulink> class. To configure |
|
this variable resolver in one's application, one will need to edit one's |
|
<emphasis>faces-context.xml</emphasis> file. After the opening |
|
<literal><faces-config/></literal> element, add an <literal><application/></literal> |
|
element and a <literal><variable-resolver/></literal> element within it. |
|
The value of the variable resolver should reference Spring's |
|
<classname>DelegatingVariableResolver</classname>; for example:</para> |
|
<programlisting language="xml"><![CDATA[<faces-config> |
|
<application> |
|
<variable-resolver>org.springframework.web.jsf.DelegatingVariableResolver</variable-resolver> |
|
<locale-config> |
|
<default-locale>en</default-locale> |
|
<supported-locale>en</supported-locale> |
|
<supported-locale>es</supported-locale> |
|
</locale-config> |
|
<message-bundle>messages</message-bundle> |
|
</application> |
|
</faces-config>]]></programlisting> |
|
<para> |
|
The <classname>DelegatingVariableResolver</classname> will first delegate value |
|
lookups to the default resolver of the underlying JSF implementation, and |
|
then to Spring's 'business context' <classname>WebApplicationContext</classname>. |
|
This allows one to easily inject dependencies into one's JSF-managed beans. |
|
</para> |
|
<para> |
|
Managed beans are defined in one's <literal>faces-config.xml</literal> |
|
file. Find below an example where <literal>#{userManager}</literal> is a bean |
|
that is retrieved from the Spring 'business context'. |
|
</para> |
|
<programlisting language="xml"><![CDATA[<managed-bean> |
|
<managed-bean-name>userList</managed-bean-name> |
|
<managed-bean-class>com.whatever.jsf.UserList</managed-bean-class> |
|
<managed-bean-scope>request</managed-bean-scope> |
|
<managed-property> |
|
<property-name>userManager</property-name> |
|
<value>#{userManager}</value> |
|
</managed-property> |
|
</managed-bean>]]></programlisting> |
|
</section> |
|
<section id="jsf-springbeanvariableresolver"> |
|
<title>SpringBeanVariableResolver (JSF 1.1/1.2)</title> |
|
<para> |
|
<classname>SpringBeanVariableResolver</classname> is a variant of |
|
<classname>DelegatingVariableResolver</classname>. It delegates to the |
|
Spring's 'business context' <classname>WebApplicationContext</classname> |
|
<emphasis>first</emphasis>, then to the default resolver of the |
|
underlying JSF implementation. This is useful in particular when |
|
using request/session-scoped beans with special Spring resolution rules, |
|
e.g. Spring <interfacename>FactoryBean</interfacename> implementations. |
|
</para> |
|
<para> |
|
Configuration-wise, simply define <classname>SpringBeanVariableResolver</classname> |
|
in your <emphasis>faces-context.xml</emphasis> file: |
|
</para> |
|
<programlisting language="xml"><![CDATA[<faces-config> |
|
<application> |
|
<variable-resolver>org.springframework.web.jsf.SpringBeanVariableResolver</variable-resolver> |
|
... |
|
</application> |
|
</faces-config>]]></programlisting> |
|
</section> |
|
<section id="jsf-springbeanfaceselresolver"> |
|
<title>SpringBeanFacesELResolver (JSF 1.2+)</title> |
|
<para> |
|
<classname>SpringBeanFacesELResolver</classname> is a JSF 1.2 compliant |
|
<classname>ELResolver</classname> implementation, integrating with |
|
the standard Unified EL as used by JSF 1.2 and JSP 2.1. Like |
|
<classname>SpringBeanVariableResolver</classname>, it delegates to the |
|
Spring's 'business context' <classname>WebApplicationContext</classname> |
|
<emphasis>first</emphasis>, then to the default resolver of the |
|
underlying JSF implementation. |
|
</para> |
|
<para> |
|
Configuration-wise, simply define <classname>SpringBeanFacesELResolver</classname> |
|
in your JSF 1.2 <emphasis>faces-context.xml</emphasis> file: |
|
</para> |
|
<programlisting language="xml"><![CDATA[<faces-config> |
|
<application> |
|
<el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver> |
|
... |
|
</application> |
|
</faces-config>]]></programlisting> |
|
</section> |
|
<section id="jsf-facescontextutils"> |
|
<title>FacesContextUtils</title> |
|
<para> |
|
A custom <interfacename>VariableResolver</interfacename> works well when mapping |
|
one's properties to beans in <emphasis>faces-config.xml</emphasis>, but at times |
|
one may need to grab a bean explicitly. The |
|
<ulink url="http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/web/jsf/FacesContextUtils.html"> |
|
<classname>FacesContextUtils</classname></ulink> class makes this easy. It is |
|
similar to <classname>WebApplicationContextUtils</classname>, except that it |
|
takes a <classname>FacesContext</classname> parameter rather than a |
|
<interface>ServletContext</interface> parameter. |
|
</para> |
|
<programlisting language="java"><![CDATA[ApplicationContext ctx = FacesContextUtils.getWebApplicationContext(FacesContext.getCurrentInstance());]]></programlisting> |
|
</section> |
|
</section> |
|
|
|
<section id="struts"> |
|
<title>Apache Struts 1.x and 2.x</title> |
|
<para> |
|
<ulink url="http://struts.apache.org">Struts</ulink> is the |
|
<emphasis>de facto</emphasis> web framework for Java applications, mainly |
|
because it was one of the first to be released (June 2001). Invented by |
|
Craig McClanahan, Struts is an open source project hosted by the Apache |
|
Software Foundation. At the time, it greatly simplified the JSP/Servlet |
|
programming paradigm and won over many developers who were using |
|
proprietary frameworks. It simplified the programming model, it was open |
|
source (and thus free as in beer), and it had a large community, which allowed |
|
the project to grow and become popular among Java web developers. |
|
</para> |
|
<note> |
|
<para> |
|
<emphasis>The following section discusses Struts 1 a.k.a. "Struts Classic".</emphasis> |
|
</para> |
|
<para> |
|
Struts 2 is effectively a different product - a successor of |
|
WebWork 2.2 (as discussed in <xref linkend="webwork"/>), |
|
carrying the Struts brand now. Check out the Struts 2 |
|
<ulink url="http://struts.apache.org/2.x/docs/spring-plugin.html">Spring Plugin</ulink> |
|
for the built-in Spring integration shipped with Struts 2. |
|
In general, Struts 2 is closer to WebWork 2.2 than to Struts 1 |
|
in terms of its Spring integration implications. |
|
</para> |
|
</note> |
|
<para> |
|
To integrate your Struts 1.x application with Spring, you have two options: |
|
</para> |
|
<itemizedlist> |
|
<listitem> |
|
<para> |
|
Configure Spring to manage your Actions as beans, using the |
|
<classname>ContextLoaderPlugin</classname>, and set their |
|
dependencies in a Spring context file. |
|
</para> |
|
</listitem> |
|
<listitem> |
|
<para> |
|
Subclass Spring's <classname>ActionSupport</classname> |
|
classes and grab your Spring-managed beans explicitly using |
|
a <emphasis>getWebApplicationContext()</emphasis> method. |
|
</para> |
|
</listitem> |
|
</itemizedlist> |
|
<section id="struts-contextloaderplugin"> |
|
<title>ContextLoaderPlugin</title> |
|
<para> |
|
The <ulink url="http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/web/struts/ContextLoaderPlugIn.html"><classname>ContextLoaderPlugin</classname></ulink> |
|
is a Struts 1.1+ plug-in that loads a Spring context file for the Struts |
|
<classname>ActionServlet</classname>. This context refers to the root |
|
<classname>WebApplicationContext</classname> (loaded by the |
|
<classname>ContextLoaderListener</classname>) as its parent. The default |
|
name of the context file is the name of the mapped servlet, plus |
|
<emphasis>-servlet.xml</emphasis>. If <classname>ActionServlet</classname> |
|
is defined in web.xml as |
|
<literal><servlet-name>action</servlet-name></literal>, the |
|
default is <emphasis>/WEB-INF/action-servlet.xml</emphasis>. |
|
</para> |
|
<para> |
|
To configure this plug-in, add the following XML to the plug-ins section near |
|
the bottom of your <emphasis>struts-config.xml</emphasis> file: |
|
</para> |
|
<programlisting language="xml"><![CDATA[<plug-in className="org.springframework.web.struts.ContextLoaderPlugIn"/>]]></programlisting> |
|
<para> |
|
The location of the context configuration files can be customized using the |
|
'<literal>contextConfigLocation</literal>' property. |
|
</para> |
|
<programlisting language="xml"><![CDATA[<plug-in className="org.springframework.web.struts.ContextLoaderPlugIn"> |
|
<set-property property="contextConfigLocation" |
|
value="/WEB-INF/action-servlet.xml,/WEB-INF/applicationContext.xml"/> |
|
</plug-in>]]></programlisting> |
|
<para> |
|
It is possible to use this plugin to load all your context files, which can be |
|
useful when using testing tools like StrutsTestCase. StrutsTestCase's |
|
<classname>MockStrutsTestCase</classname> won't initialize Listeners on startup |
|
so putting all your context files in the plugin is a workaround. (A |
|
<ulink url="http://sourceforge.net/tracker/index.php?func=detail&aid=1088866&group_id=39190&atid=424562"> |
|
bug has been filed</ulink> for this issue, but has been closed as 'Wont Fix'). |
|
</para> |
|
<para> |
|
After configuring this plug-in in <emphasis>struts-config.xml</emphasis>, you can |
|
configure your <classname>Action</classname> to be managed by Spring. Spring (1.1.3+) |
|
provides two ways to do this: |
|
</para> |
|
<itemizedlist> |
|
<listitem> |
|
<para> |
|
Override Struts' default <classname>RequestProcessor</classname> |
|
with Spring's <classname>DelegatingRequestProcessor</classname>. |
|
</para> |
|
</listitem> |
|
<listitem> |
|
<para> |
|
Use the <classname>DelegatingActionProxy</classname> class |
|
in the <literal>type</literal> attribute of your |
|
<literal><action-mapping></literal>. |
|
</para> |
|
</listitem> |
|
</itemizedlist> |
|
<para> |
|
Both of these methods allow you to manage your Actions and |
|
their dependencies in the <emphasis>action-servlet.xml</emphasis> file. |
|
The bridge between the Action in <emphasis>struts-config.xml</emphasis> |
|
and <emphasis>action-servlet.xml</emphasis> is built with the |
|
action-mapping's "path" and the bean's "name". If you have the |
|
following in your <emphasis>struts-config.xml</emphasis> file: |
|
</para> |
|
<programlisting language="xml"><![CDATA[<action path="/users" .../>]]></programlisting> |
|
<para> |
|
You must define that Action's bean with the "/users" name in |
|
<emphasis>action-servlet.xml</emphasis>: |
|
</para> |
|
<programlisting language="xml"><![CDATA[<bean name="/users" .../>]]></programlisting> |
|
<section id="struts-delegatingrequestprocessor"> |
|
<title>DelegatingRequestProcessor</title> |
|
<para> |
|
To configure the <ulink url="http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/web/struts/DelegatingRequestProcessor.html"> |
|
<literal>DelegatingRequestProcessor</literal></ulink> in your |
|
<emphasis>struts-config.xml</emphasis> file, override the "processorClass" |
|
property in the <controller> element. These lines follow the |
|
<action-mapping> element. |
|
</para> |
|
<programlisting language="xml"><![CDATA[<controller> |
|
<set-property property="processorClass" |
|
value="org.springframework.web.struts.DelegatingRequestProcessor"/> |
|
</controller>]]></programlisting> |
|
<para> |
|
After adding this setting, your Action will automatically be |
|
looked up in Spring's context file, no matter what the type. In fact, |
|
you don't even need to specify a type. Both of the following snippets |
|
will work: |
|
</para> |
|
<programlisting language="xml"><![CDATA[<action path="/user" type="com.whatever.struts.UserAction"/> |
|
<action path="/user"/>]]></programlisting> |
|
<para> |
|
If you're using Struts' <emphasis>modules</emphasis> feature, |
|
your bean names must contain the module prefix. For example, an action |
|
defined as <literal><action path="/user"/></literal> with module |
|
prefix "admin" requires a bean name with |
|
<literal><bean name="/admin/user"/></literal>. |
|
</para> |
|
<note> |
|
<para> |
|
If you are using Tiles in your Struts application, you must configure your |
|
<controller> with the |
|
<ulink url="http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/web/struts/DelegatingTilesRequestProcessor.html"><classname>DelegatingTilesRequestProcessor</classname></ulink> |
|
instead. |
|
</para> |
|
</note> |
|
</section> |
|
<section id="struts-delegatingactionproxy"> |
|
<title>DelegatingActionProxy</title> |
|
<para> |
|
If you have a custom <classname>RequestProcessor</classname> and |
|
can't use the <classname>DelegatingRequestProcessor</classname> or |
|
<classname>DelegatingTilesRequestProcessor</classname> approaches, you can |
|
use the <ulink url="http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/web/struts/DelegatingActionProxy.html"> |
|
<classname>DelegatingActionProxy</classname></ulink> as the type in your |
|
action-mapping. |
|
</para> |
|
<programlisting language="xml"><![CDATA[<action path="/user" type="org.springframework.web.struts.DelegatingActionProxy" |
|
name="userForm" scope="request" validate="false" parameter="method"> |
|
<forward name="list" path="/userList.jsp"/> |
|
<forward name="edit" path="/userForm.jsp"/> |
|
</action>]]></programlisting> |
|
<para> |
|
The bean definition in <emphasis>action-servlet.xml</emphasis> |
|
remains the same, whether you use a custom <literal>RequestProcessor</literal> |
|
or the <classname>DelegatingActionProxy</classname>. |
|
</para> |
|
<para> |
|
If you define your <classname>Action</classname> in a context file, the |
|
full feature set of Spring's bean container will be available for it: |
|
dependency injection as well as the option to instantiate a new |
|
<classname>Action</classname> instance for each request. To activate the latter, |
|
add <emphasis>scope="prototype"</emphasis> to your Action's bean definition. |
|
</para> |
|
<programlisting language="xml"><![CDATA[<bean name="/user" scope="prototype" autowire="byName" |
|
class="org.example.web.UserAction"/>]]></programlisting> |
|
</section> |
|
</section> |
|
<section id="struts-actionsupport"> |
|
<title>ActionSupport Classes</title> |
|
<para> |
|
As previously mentioned, you can retrieve the |
|
<classname>WebApplicationContext</classname> from the <interface>ServletContext</interface> |
|
using the <classname>WebApplicationContextUtils</classname> class. An |
|
easier way is to extend Spring's <classname>Action</classname> classes for |
|
Struts. For example, instead of subclassing Struts' |
|
<classname>Action</classname> class, you can subclass Spring's |
|
<ulink url="http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/web/struts/ActionSupport.html"> |
|
<classname>ActionSupport</classname></ulink> class. |
|
</para> |
|
<para> |
|
The <classname>ActionSupport</classname> class provides additional |
|
convenience methods, like <emphasis>getWebApplicationContext()</emphasis>. |
|
Below is an example of how you might use this in an Action: |
|
</para> |
|
<programlisting language="java"><![CDATA[public class UserAction extends DispatchActionSupport { |
|
|
|
public ActionForward execute(ActionMapping mapping, |
|
ActionForm form, |
|
HttpServletRequest request, |
|
HttpServletResponse response) throws Exception { |
|
if (log.isDebugEnabled()) { |
|
log.debug("entering 'delete' method..."); |
|
} |
|
WebApplicationContext ctx = getWebApplicationContext(); |
|
UserManager mgr = (UserManager) ctx.getBean("userManager"); |
|
// talk to manager for business logic |
|
return mapping.findForward("success"); |
|
} |
|
}]]></programlisting> |
|
<para> |
|
Spring includes subclasses for all of the standard Struts Actions |
|
- the Spring versions merely have <emphasis>Support</emphasis> appended to the name: |
|
<itemizedlist spacing="compact"> |
|
<listitem><para><ulink url="http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/web/struts/ActionSupport.html"><classname>ActionSupport</classname></ulink>,</para></listitem> |
|
<listitem><para><ulink url="http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/web/struts/DispatchActionSupport.html"><literal>DispatchActionSupport</literal></ulink>,</para></listitem> |
|
<listitem><para><ulink url="http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/web/struts/LookupDispatchActionSupport.html"><literal>LookupDispatchActionSupport</literal></ulink> and</para></listitem> |
|
<listitem><para><ulink url="http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/web/struts/MappingDispatchActionSupport.html"><literal>MappingDispatchActionSupport</literal></ulink>.</para></listitem> |
|
</itemizedlist> |
|
</para> |
|
|
|
<para> |
|
The recommended strategy is to use the approach that best suits |
|
your project. Subclassing makes your code more readable, and you know |
|
exactly how your dependencies are resolved. However, using the |
|
<classname>ContextLoaderPlugin</classname> allow you to easily add new |
|
dependencies in your context XML file. Either way, Spring provides some |
|
nice options for integrating the two frameworks. |
|
</para> |
|
</section> |
|
</section> |
|
|
|
<section id="webwork"> |
|
<title>WebWork 2.x</title> |
|
<para> |
|
From the <ulink url="http://www.opensymphony.com/webwork/">WebWork homepage</ulink>... |
|
</para> |
|
<quote> |
|
<emphasis> |
|
WebWork is a Java web-application development framework. It is built |
|
specifically with developer productivity and code simplicity in mind, |
|
providing robust support for building reusable UI templates, such as form |
|
controls, UI themes, internationalization, dynamic form parameter mapping |
|
to JavaBeans, robust client and server side validation, and much more. |
|
</emphasis> |
|
</quote> |
|
<para> |
|
WebWork is (in the opinion of this author) a very clean, elegant web framework. |
|
Its architecture and key concepts are not only very easy to understand, it has |
|
a rich tag library, nicely decoupled validation, and it is (again, in the opinion |
|
of this author) quite easy to be productive in next to no time at all (the |
|
documentation and tutorials are pretty good too). |
|
</para> |
|
<para> |
|
One of the key enablers in WebWork's technology stack is |
|
<ulink url="http://www.opensymphony.com/webwork/wikidocs/IoC%20Overview.html">an IoC container</ulink> |
|
to manage Webwork Actions, handle the "wiring" of business objects, etc. |
|
Prior to WebWork version 2.2, WebWork used its own proprietary IoC container |
|
(and provided integration points so that one could integrate an IoC container |
|
such as Springs into the mix). However, as of WebWork version 2.2, the default |
|
IoC container that is used within WebWork <emphasis>is</emphasis> Spring. This |
|
is obviously great news if one is a Spring developer, because it means that one |
|
is immediately familiar with the basics of IoC configuration, idioms and suchlike |
|
within WebWork. |
|
</para> |
|
<para> |
|
Now in the interests of adhering to the DRY (Dont Repeat Yourself) principle, it |
|
would be foolish to writeup the Spring-WebWork integration in light of the fact that |
|
the WebWork team have already written such a writeup. Please do consult the |
|
<ulink url="http://www.opensymphony.com/webwork/wikidocs/Spring.html">Spring-WebWork integration page</ulink> |
|
on the |
|
<ulink url="http://wiki.opensymphony.com/display/WW/WebWork">WebWork wiki</ulink> |
|
for the full lowdown. |
|
</para> |
|
<para> |
|
Note that the Spring-WebWork integration code was developed (and continues |
|
to be maintained and improved) by the WebWork developers themselves, so in the |
|
first instance please do refer to the WebWork site and forums if you are having |
|
issues with the integration. Do feel free to post comments and queries regarding |
|
the Spring-WebWork integration on the |
|
<ulink url="http://forum.springframework.org/forumdisplay.php?f=25">Spring support forums</ulink> |
|
too. |
|
</para> |
|
</section> |
|
|
|
<section id="tapestry"> |
|
<title>Tapestry 3.x and 4.x</title> |
|
<para> |
|
From the <ulink url="http://tapestry.apache.org/">Tapestry homepage</ulink>... |
|
</para> |
|
<quote> |
|
<emphasis> |
|
Tapestry is an open-source framework for creating dynamic, robust, highly |
|
scalable web applications in Java. Tapestry complements and builds upon |
|
the standard Java Servlet API, and so it works in any servlet container or |
|
application server. |
|
</emphasis> |
|
</quote> |
|
<para> |
|
While Spring has its own <link linkend="mvc">powerful web layer</link>, there |
|
are a number of unique advantages to building a J2EE application using a |
|
combination of Tapestry for the web user interface and the Spring container |
|
for the lower layers. This section of the web integration chapter attempts |
|
to detail a few best practices for combining these two frameworks. |
|
</para> |
|
<para> |
|
A <emphasis>typical</emphasis> layered J2EE application built with Tapestry |
|
and Spring will consist of a top user interface (UI) layer built with Tapestry, |
|
and a number of lower layers, all wired together by one or more Spring containers. |
|
Tapestry's own reference documentation contains the following snippet of best |
|
practice advice. (Text that the author of this Spring section has added is |
|
contained within <literal>[]</literal> brackets.) |
|
</para> |
|
<quote> |
|
<emphasis> |
|
A very succesful design pattern in Tapestry is to keep pages and components |
|
very simple, and <emphasis role="bold">delegate</emphasis> as much logic as |
|
possible out to HiveMind [or Spring, or whatever] services. Listener methods |
|
should ideally do little more than marshall together the correct information |
|
and pass it over to a service. |
|
</emphasis> |
|
</quote> |
|
<para> |
|
The key question then is... how does one supply Tapestry pages with collaborating |
|
services? The answer, ideally, is that one would want to dependency inject those |
|
services directly into one's Tapestry pages. In Tapestry, one can effect this |
|
dependency injection by a variety of means... |
|
This section is only going to enumerate the dependency injection means afforded |
|
by Spring. The real beauty of the rest of this Spring-Tapestry integration is that |
|
the elegant and flexible design of Tapestry itself makes doing this dependency |
|
injection of Spring-managed beans a cinch. (Another nice thing is that this |
|
Spring-Tapestry integration code was written - and continues to be maintained - |
|
by the Tapestry creator |
|
<ulink url="http://howardlewisship.com/blog/">Howard M. Lewis Ship</ulink>, |
|
so hats off to him for what is really some silky smooth integration). |
|
</para> |
|
<section id="tapestry-di"> |
|
<title>Injecting Spring-managed beans</title> |
|
<para> |
|
Assume we have the following simple Spring container definition (in the |
|
ubiquitous XML format): |
|
</para> |
|
<programlisting language="xml"><![CDATA[<?xml version="1.0" encoding="UTF-8"?> |
|
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" |
|
"http://www.springframework.org/dtd/spring-beans-2.0.dtd"> |
|
|
|
<beans> |
|
<!-- the DataSource --> |
|
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"> |
|
<property name="jndiName" value="java:DefaultDS"/> |
|
</bean> |
|
|
|
<bean id="hibSessionFactory" |
|
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> |
|
<property name="dataSource" ref="dataSource"/> |
|
</bean> |
|
|
|
<bean id="transactionManager" |
|
class="org.springframework.transaction.jta.JtaTransactionManager"/> |
|
|
|
<bean id="mapper" |
|
class="com.whatever.dataaccess.mapper.hibernate.MapperImpl"> |
|
<property name="sessionFactory" ref="hibSessionFactory"/> |
|
</bean> |
|
|
|
<!-- (transactional) AuthenticationService --> |
|
<bean id="authenticationService" |
|
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> |
|
<property name="transactionManager" ref="transactionManager"/> |
|
<property name="target"> |
|
<bean class="com.whatever.services.service.user.AuthenticationServiceImpl"> |
|
<property name="mapper" ref="mapper"/> |
|
</bean> |
|
</property> |
|
<property name="proxyInterfacesOnly" value="true"/> |
|
<property name="transactionAttributes"> |
|
<value> |
|
*=PROPAGATION_REQUIRED |
|
</value> |
|
</property> |
|
</bean> |
|
|
|
<!-- (transactional) UserService --> |
|
<bean id="userService" |
|
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> |
|
<property name="transactionManager" ref="transactionManager"/> |
|
<property name="target"> |
|
<bean class="com.whatever.services.service.user.UserServiceImpl"> |
|
<property name="mapper" ref="mapper"/> |
|
</bean> |
|
</property> |
|
<property name="proxyInterfacesOnly" value="true"/> |
|
<property name="transactionAttributes"> |
|
<value> |
|
*=PROPAGATION_REQUIRED |
|
</value> |
|
</property> |
|
</bean> |
|
|
|
</beans>]]></programlisting> |
|
<para> |
|
Inside the Tapestry application, the above bean definitions need to |
|
be <link linkend="web-integration-common">loaded into a Spring container</link>, |
|
and any relevant Tapestry pages need to be supplied (injected) with the |
|
<literal>authenticationService</literal> and |
|
<literal>userService</literal> beans, which implement the |
|
<interfacename>AuthenticationService</interfacename> and |
|
<interfacename>UserService</interfacename> interfaces, respectively. |
|
</para> |
|
<para> |
|
At this point, the application context is available to a web |
|
application by calling Spring's static utility function |
|
<literal>WebApplicationContextUtils.getApplicationContext(servletContext)</literal>, |
|
where servletContext is the standard <interface>ServletContext</interface> |
|
from the J2EE Servlet specification. As such, one simple mechanism for |
|
a page to get an instance of the <interfacename>UserService</interfacename>, |
|
for example, would be with code such as: |
|
</para> |
|
<programlisting language="java"><![CDATA[WebApplicationContext appContext = WebApplicationContextUtils.getApplicationContext( |
|
getRequestCycle().getRequestContext().getServlet().getServletContext()); |
|
UserService userService = (UserService) appContext.getBean("userService"); |
|
]]><lineannotation>... some code which uses UserService</lineannotation></programlisting> |
|
<para> |
|
This mechanism does work... having said that, it can be made a |
|
lot less verbose by encapsulating most of the functionality in a |
|
method in the base class for the page or component. However, in |
|
some respects it goes against the IoC principle; ideally you would like the page to |
|
not have to ask the context for a specific bean by name, and in |
|
fact, the page would ideally not know about the context at all. |
|
</para> |
|
<para> |
|
Luckily, there is a mechanism to allow this. We rely upon the |
|
fact that Tapestry already has a mechanism to declaratively add |
|
properties to a page, and it is in fact the preferred approach to |
|
manage all properties on a page in this declarative fashion, so that |
|
Tapestry can properly manage their lifecycle as part of the page and |
|
component lifecycle. |
|
</para> |
|
<note> |
|
<para> |
|
This next section is applicable to Tapestry 3.x. |
|
If you are using Tapestry version 4.x, please consult the section |
|
entitled <xref linkend="tapestry-4-style-di"/>. |
|
</para> |
|
</note> |
|
<section id="tapestry-pre4-style-di"> |
|
<title>Dependency Injecting Spring Beans into Tapestry pages</title> |
|
<para> |
|
First we need to make the <interface>ApplicationContext</interface> |
|
available to the Tapestry page or Component without having to have the |
|
<interface>ServletContext</interface>; this is because at the stage in the |
|
page's/component's lifecycle when we need to access the |
|
<interface>ApplicationContext</interface>, the |
|
<interface>ServletContext</interface> won't be easily available to the |
|
page, so we can't use |
|
<literal>WebApplicationContextUtils.getApplicationContext(servletContext)</literal> |
|
directly. One way is by defining a custom version of the Tapestry |
|
<interfacename>IEngine</interfacename> which exposes this for us: |
|
</para> |
|
<programlisting language="java"><![CDATA[package com.whatever.web.xportal; |
|
|
|
import ... |
|
|
|
public class MyEngine extends org.apache.tapestry.engine.BaseEngine { |
|
|
|
public static final String APPLICATION_CONTEXT_KEY = "appContext"; |
|
|
|
/** |
|
* @see org.apache.tapestry.engine.AbstractEngine#setupForRequest(org.apache.tapestry.request.RequestContext) |
|
*/ |
|
protected void setupForRequest(RequestContext context) { |
|
super.setupForRequest(context); |
|
|
|
// insert ApplicationContext in global, if not there |
|
Map global = (Map) getGlobal(); |
|
ApplicationContext ac = (ApplicationContext) global.get(APPLICATION_CONTEXT_KEY); |
|
if (ac == null) { |
|
ac = WebApplicationContextUtils.getWebApplicationContext( |
|
context.getServlet().getServletContext() |
|
); |
|
global.put(APPLICATION_CONTEXT_KEY, ac); |
|
} |
|
} |
|
}]]></programlisting> |
|
<para> |
|
This engine class places the Spring Application Context as |
|
an attribute called "appContext" in this Tapestry app's 'Global' |
|
object. Make sure to register the fact that this special IEngine |
|
instance should be used for this Tapestry application, with an entry |
|
in the Tapestry application definition file. For example: |
|
</para> |
|
<programlisting language="xml"><lineannotation>file: xportal.application:</lineannotation><![CDATA[ |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<!DOCTYPE application PUBLIC |
|
"-//Apache Software Foundation//Tapestry Specification 3.0//EN" |
|
"http://jakarta.apache.org/tapestry/dtd/Tapestry_3_0.dtd"> |
|
<application |
|
name="Whatever xPortal" |
|
engine-class="com.whatever.web.xportal.MyEngine"> |
|
</application>]]></programlisting> |
|
</section> |
|
<section id="tapestry-componentdefs"> |
|
<title>Component definition files</title> |
|
<para> |
|
Now in our page or component definition file (*.page or *.jwc), |
|
we simply add property-specification elements to grab the beans we |
|
need out of the <interfacename>ApplicationContext</interfacename>, |
|
and create page or component properties for them. For example: |
|
</para> |
|
<programlisting language="xml"><![CDATA[ <property-specification name="userService" |
|
type="com.whatever.services.service.user.UserService"> |
|
global.appContext.getBean("userService") |
|
</property-specification> |
|
<property-specification name="authenticationService" |
|
type="com.whatever.services.service.user.AuthenticationService"> |
|
global.appContext.getBean("authenticationService") |
|
</property-specification>]]></programlisting> |
|
<para> |
|
The OGNL expression inside the property-specification specifies the |
|
initial value for the property, as a bean obtained from the context. |
|
The entire page definition might look like this: |
|
</para> |
|
<programlisting language="xml"><![CDATA[<?xml version="1.0" encoding="UTF-8"?> |
|
<!DOCTYPE page-specification PUBLIC |
|
"-//Apache Software Foundation//Tapestry Specification 3.0//EN" |
|
"http://jakarta.apache.org/tapestry/dtd/Tapestry_3_0.dtd"> |
|
|
|
<page-specification class="com.whatever.web.xportal.pages.Login"> |
|
|
|
<property-specification name="username" type="java.lang.String"/> |
|
<property-specification name="password" type="java.lang.String"/> |
|
<property-specification name="error" type="java.lang.String"/> |
|
<property-specification name="callback" type="org.apache.tapestry.callback.ICallback" persistent="yes"/> |
|
<property-specification name="userService" |
|
type="com.whatever.services.service.user.UserService"> |
|
global.appContext.getBean("userService") |
|
</property-specification> |
|
<property-specification name="authenticationService" |
|
type="com.whatever.services.service.user.AuthenticationService"> |
|
global.appContext.getBean("authenticationService") |
|
</property-specification> |
|
|
|
<bean name="delegate" class="com.whatever.web.xportal.PortalValidationDelegate"/> |
|
|
|
<bean name="validator" class="org.apache.tapestry.valid.StringValidator" lifecycle="page"> |
|
<set-property name="required" expression="true"/> |
|
<set-property name="clientScriptingEnabled" expression="true"/> |
|
</bean> |
|
|
|
<component id="inputUsername" type="ValidField"> |
|
<static-binding name="displayName" value="Username"/> |
|
<binding name="value" expression="username"/> |
|
<binding name="validator" expression="beans.validator"/> |
|
</component> |
|
|
|
<component id="inputPassword" type="ValidField"> |
|
<binding name="value" expression="password"/> |
|
<binding name="validator" expression="beans.validator"/> |
|
<static-binding name="displayName" value="Password"/> |
|
<binding name="hidden" expression="true"/> |
|
</component> |
|
|
|
</page-specification>]]></programlisting> |
|
</section> |
|
<section id="tapestry-getters"> |
|
<title>Adding abstract accessors</title> |
|
<para> |
|
Now in the Java class definition for the page or component |
|
itself, all we need to do is add an abstract getter method |
|
for the properties we have defined (in order to be able to |
|
access the properties). |
|
</para> |
|
<programlisting language="java"><![CDATA[// our UserService implementation; will come from page definition |
|
public abstract UserService getUserService(); |
|
// our AuthenticationService implementation; will come from page definition |
|
public abstract AuthenticationService getAuthenticationService();]]></programlisting> |
|
<para> |
|
For the sake of completeness, the entire Java class, for a |
|
login page in this example, might look like this: |
|
</para> |
|
<programlisting language="java"><![CDATA[package com.whatever.web.xportal.pages; |
|
|
|
/** |
|
* Allows the user to login, by providing username and password. |
|
* After successfully logging in, a cookie is placed on the client browser |
|
* that provides the default username for future logins (the cookie |
|
* persists for a week). |
|
*/ |
|
public abstract class Login extends BasePage implements ErrorProperty, PageRenderListener { |
|
|
|
/** the key under which the authenticated user object is stored in the visit as */ |
|
public static final String USER_KEY = "user"; |
|
|
|
/** The name of the cookie that identifies a user **/ |
|
private static final String COOKIE_NAME = Login.class.getName() + ".username"; |
|
private final static int ONE_WEEK = 7 * 24 * 60 * 60; |
|
|
|
public abstract String getUsername(); |
|
public abstract void setUsername(String username); |
|
|
|
public abstract String getPassword(); |
|
public abstract void setPassword(String password); |
|
|
|
public abstract ICallback getCallback(); |
|
public abstract void setCallback(ICallback value); |
|
|
|
public abstract UserService getUserService(); |
|
public abstract AuthenticationService getAuthenticationService(); |
|
|
|
protected IValidationDelegate getValidationDelegate() { |
|
return (IValidationDelegate) getBeans().getBean("delegate"); |
|
} |
|
|
|
protected void setErrorField(String componentId, String message) { |
|
IFormComponent field = (IFormComponent) getComponent(componentId); |
|
IValidationDelegate delegate = getValidationDelegate(); |
|
delegate.setFormComponent(field); |
|
delegate.record(new ValidatorException(message)); |
|
} |
|
|
|
/** |
|
* Attempts to login. |
|
* <p> |
|
* If the user name is not known, or the password is invalid, then an error |
|
* message is displayed. |
|
**/ |
|
public void attemptLogin(IRequestCycle cycle) { |
|
|
|
String password = getPassword(); |
|
|
|
// Do a little extra work to clear out the password. |
|
setPassword(null); |
|
IValidationDelegate delegate = getValidationDelegate(); |
|
|
|
delegate.setFormComponent((IFormComponent) getComponent("inputPassword")); |
|
delegate.recordFieldInputValue(null); |
|
|
|
// An error, from a validation field, may already have occurred. |
|
if (delegate.getHasErrors()) { |
|
return; |
|
} |
|
|
|
try { |
|
User user = getAuthenticationService().login(getUsername(), getPassword()); |
|
loginUser(user, cycle); |
|
} |
|
catch (FailedLoginException ex) { |
|
this.setError("Login failed: " + ex.getMessage()); |
|
return; |
|
} |
|
} |
|
|
|
/** |
|
* Sets up the {@link User} as the logged in user, creates |
|
* a cookie for their username (for subsequent logins), |
|
* and redirects to the appropriate page, or |
|
* a specified page). |
|
**/ |
|
public void loginUser(User user, IRequestCycle cycle) { |
|
|
|
String username = user.getUsername(); |
|
|
|
// Get the visit object; this will likely force the |
|
// creation of the visit object and an HttpSession |
|
Map visit = (Map) getVisit(); |
|
visit.put(USER_KEY, user); |
|
|
|
// After logging in, go to the MyLibrary page, unless otherwise specified |
|
ICallback callback = getCallback(); |
|
|
|
if (callback == null) { |
|
cycle.activate("Home"); |
|
} |
|
else { |
|
callback.performCallback(cycle); |
|
} |
|
|
|
IEngine engine = getEngine(); |
|
Cookie cookie = new Cookie(COOKIE_NAME, username); |
|
cookie.setPath(engine.getServletPath()); |
|
cookie.setMaxAge(ONE_WEEK); |
|
|
|
// Record the user's username in a cookie |
|
cycle.getRequestContext().addCookie(cookie); |
|
engine.forgetPage(getPageName()); |
|
} |
|
|
|
public void pageBeginRender(PageEvent event) { |
|
if (getUsername() == null) { |
|
setUsername(getRequestCycle().getRequestContext().getCookieValue(COOKIE_NAME)); |
|
} |
|
} |
|
}]]></programlisting> |
|
</section> |
|
<section id="tapestry-4-style-di"> |
|
<title>Dependency Injecting Spring Beans into Tapestry pages - Tapestry 4.x style</title> |
|
<para> |
|
Effecting the dependency injection of Spring-managed beans into Tapestry |
|
pages in Tapestry version 4.x is <emphasis>so</emphasis> much simpler. |
|
All that is needed is a single |
|
<ulink url="http://howardlewisship.com/tapestry-javaforge/tapestry-spring/">add-on library</ulink>, |
|
and some (small) amount of (essentially boilerplate) configuration. |
|
Simply package and deploy this library with the (any of the) other |
|
libraries required by your web application (typically in |
|
<literal>WEB-INF/lib</literal>). |
|
</para> |
|
<para> |
|
You will then need to create and expose the Spring container using the |
|
<link linkend="web-integration-common">method detailed previously</link>. |
|
You can then inject Spring-managed beans into Tapestry very easily; if |
|
we are using Java 5, consider the <classname>Login</classname> page from above: |
|
we simply need to annotate the appropriate getter methods |
|
in order to dependency inject the Spring-managed <literal>userService</literal> |
|
and <literal>authenticationService</literal> objects (lots of the class |
|
definition has been elided for clarity)... |
|
</para> |
|
<programlisting language="java"><![CDATA[package com.whatever.web.xportal.pages; |
|
|
|
public abstract class Login extends BasePage implements ErrorProperty, PageRenderListener { |
|
|
|
@InjectObject("spring:userService") |
|
public abstract UserService getUserService(); |
|
|
|
@InjectObject("spring:authenticationService") |
|
public abstract AuthenticationService getAuthenticationService(); |
|
|
|
}]]></programlisting> |
|
<para> |
|
We are almost done... all that remains is the HiveMind configuration that exposes the |
|
Spring container stored in the <interfacename>ServletContext</interfacename> as a |
|
HiveMind service; for example:</para> |
|
<programlisting language="xml"><![CDATA[<?xml version="1.0"?> |
|
<module id="com.javaforge.tapestry.spring" version="0.1.1"> |
|
|
|
<service-point id="SpringApplicationInitializer" |
|
interface="org.apache.tapestry.services.ApplicationInitializer" |
|
visibility="private"> |
|
<invoke-factory> |
|
<construct class="com.javaforge.tapestry.spring.SpringApplicationInitializer"> |
|
<set-object property="beanFactoryHolder" |
|
value="service:hivemind.lib.DefaultSpringBeanFactoryHolder" /> |
|
</construct> |
|
</invoke-factory> |
|
</service-point> |
|
|
|
<!-- Hook the Spring setup into the overall application initialization. --> |
|
<contribution |
|
configuration-id="tapestry.init.ApplicationInitializers"> |
|
<command id="spring-context" |
|
object="service:SpringApplicationInitializer" /> |
|
</contribution> |
|
|
|
</module>]]></programlisting> |
|
<para> |
|
If you are using Java 5 (and thus have access to annotations), then |
|
that really is it. |
|
</para> |
|
<para> |
|
If you are not using Java 5, then one obviously doesn't annotate one's |
|
Tapestry page classes with annotations; instead, one simply uses |
|
good old fashioned XML to declare the dependency injection; for example, |
|
inside the <literal>.page</literal> or <literal>.jwc</literal> file |
|
for the <classname>Login</classname> page (or component): |
|
</para> |
|
<programlisting language="xml"><![CDATA[<inject property="userService" object="spring:userService"/> |
|
<inject property="authenticationService" object="spring:authenticationService"/>]]></programlisting> |
|
</section> |
|
</section> |
|
<para> |
|
In this example, we've managed to allow service beans defined in |
|
a Spring container to be provided to the Tapestry page in a declarative |
|
fashion. The page class does not know where the service implementations |
|
are coming from, and in fact it is easy to slip in another implementation, |
|
for example, during testing. This inversion of control is one of the |
|
prime goals and benefits of the Spring Framework, and we have managed |
|
to extend it all the way up the J2EE stack in this Tapestry application. |
|
</para> |
|
</section> |
|
|
|
<section id="web-integration-resources"> |
|
<title>Further Resources</title> |
|
<para> |
|
Find below links to further resources about the various web frameworks |
|
described in this chapter. |
|
</para> |
|
<itemizedlist> |
|
<listitem> |
|
<para>The <ulink url="http://java.sun.com/javaee/javaserverfaces/">JSF</ulink> homepage</para> |
|
</listitem> |
|
<listitem> |
|
<para>The <ulink url="http://struts.apache.org/">Struts</ulink> homepage</para> |
|
</listitem> |
|
<listitem> |
|
<para>The <ulink url="http://www.opensymphony.com/webwork/">WebWork</ulink> homepage</para> |
|
</listitem> |
|
<listitem> |
|
<para>The <ulink url="http://tapestry.apache.org/">Tapestry</ulink> homepage</para> |
|
</listitem> |
|
</itemizedlist> |
|
</section> |
|
|
|
</chapter>
|
|
|