1132 lines
54 KiB
XML
1132 lines
54 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
|
|
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
|
|
<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 -->
|
|
|
|
<xi:include href="swf-sidebar.xml"
|
|
xmlns:xi="http://www.w3.org/2001/XInclude" />
|
|
|
|
<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 favorite 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>
|
|
|
|
<note>
|
|
<para>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 <xref linkend="web-integration-resources" /> at the end
|
|
of this chapter.
|
|
</para>
|
|
</note>
|
|
|
|
</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 is
|
|
<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>On to specifics: all that one need do is to declare a <ulink
|
|
url="http://static.springframework.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/context/ContextLoaderListener.html"><classname>ContextLoaderListener</classname></ulink>
|
|
in the standard Java EE 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 configuration files to load.</para>
|
|
|
|
<para>Find below the <listener/> configuration:</para>
|
|
|
|
<programlisting language="xml"><listener>
|
|
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
|
|
</listener></programlisting>
|
|
|
|
<para>Find below the <context-param/> configuration:</para>
|
|
|
|
<programlisting language="xml"><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/3.0.x/javadoc-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 the 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">WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(servletContext);</programlisting>
|
|
|
|
<para>The <ulink
|
|
url="http://static.springframework.org/spring/docs/3.0.x/javadoc-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 and 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/3.0.x/javadoc-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"><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"><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> and 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"><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"><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/3.0.x/javadoc-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">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> used to be the
|
|
<emphasis>de facto</emphasis> web framework for Java applications, mainly
|
|
because it was one of the first to be released (June 2001). It has now been renamed to <emphasis>Struts 1</emphasis>
|
|
(as opposed to Struts 2). Many applications still use it.
|
|
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/3.0.x/javadoc-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"><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"><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"><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"><bean name="/users" .../></programlisting>
|
|
|
|
<section id="struts-delegatingrequestprocessor">
|
|
<title>DelegatingRequestProcessor</title>
|
|
|
|
<para>To configure the <ulink
|
|
url="http://static.springframework.org/spring/docs/3.0.x/javadoc-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"><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"><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/3.0.x/javadoc-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/3.0.x/javadoc-api/org/springframework/web/struts/DelegatingActionProxy.html">
|
|
<classname>DelegatingActionProxy</classname></ulink> as the type in
|
|
your action-mapping.</para>
|
|
|
|
<programlisting language="xml"><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"><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/3.0.x/javadoc-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">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/3.0.x/javadoc-api/org/springframework/web/struts/ActionSupport.html"><classname>ActionSupport</classname></ulink>,</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><ulink
|
|
url="http://static.springframework.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/struts/DispatchActionSupport.html"><literal>DispatchActionSupport</literal></ulink>,</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><ulink
|
|
url="http://static.springframework.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/struts/LookupDispatchActionSupport.html"><literal>LookupDispatchActionSupport</literal></ulink>
|
|
and</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><ulink
|
|
url="http://static.springframework.org/spring/docs/3.0.x/javadoc-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. In contrast, using the
|
|
<classname>ContextLoaderPlugin</classname> allows you to easily add new
|
|
dependencies in your context XML file. Either way, Spring provides some
|
|
nice options for integrating with Struts.</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>
|
|
|
|
<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>
|
|
|
|
<para>Web work's architecture and concepts are easy to
|
|
understand, and the framework also has an extensive tag library as well as
|
|
nicely decoupled validation.</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 Spring's 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 (Don't Repeat Yourself)
|
|
principle, it would be foolish to document the Spring-WebWork integration
|
|
in light of the fact that the WebWork team have already written such a
|
|
writeup. Please 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 please refer first to the WebWork site and forums if you are
|
|
having issues with the integration. But 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>
|
|
|
|
<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>
|
|
|
|
<para>While Spring has its own <link linkend="mvc">powerful web
|
|
layer</link>, there are a number of unique advantages to building an
|
|
enterprise Java 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 enterprise Java 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>
|
|
|
|
<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 marshal together the correct information
|
|
and pass it over to a service.</emphasis>
|
|
</quote>
|
|
</para>
|
|
|
|
<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"><?xml version="1.0" encoding="UTF-8"?>
|
|
<beans xmlns="http://www.springframework.org/schema/beans"
|
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
<emphasis role="bold">xmlns:jee="http://www.springframework.org/schema/jee"</emphasis>
|
|
xsi:schemaLocation="
|
|
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
|
|
<emphasis role="bold">http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd"</emphasis>>
|
|
|
|
<beans>
|
|
<!-- the DataSource -->
|
|
<jee:jndi-lookup id="dataSource" jndi-name="java:DefaultDS"/>
|
|
|
|
<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 Java EE 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">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">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>
|
|
<?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"> <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"><?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">// 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">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">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"><?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"><inject property="userService" object="spring:userService"/>
|
|
<inject property="authenticationService" object="spring:authenticationService"/></programlisting>
|
|
|
|
<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 throughout the stack in this Tapestry application.</para>
|
|
</section>
|
|
</section>
|
|
|
|
</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>
|