1090 lines
54 KiB
XML
1090 lines
54 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
|
<chapter id="web-integration">
|
|
<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 -->
|
|
&swf-sidebar;
|
|
|
|
<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><![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><![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><![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><![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><![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><![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><![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><![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><![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><![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><![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><![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><![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><![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><![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><![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><![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><![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><![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><![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><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><![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><![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><![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><![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><![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><![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><![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>
|