Clean MVC reference documentation
Remove reference to removed deprecations and generally cleanup the MVC documentation. Cleanup Struts
This commit is contained in:
parent
d6b4d92eed
commit
0a52c86559
|
@ -32846,9 +32846,8 @@ applications using Spring. The following describes in a broad way how to do this
|
|||
[NOTE]
|
||||
====
|
||||
This section focuses on Spring's support for Tiles v3 in the
|
||||
`org.springframework.web.servlet.view.tiles3` package as well as Tiles v2 in the
|
||||
`org.springframework.web.servlet.view.tiles2` package. Tiles v1 (a.k.a. "Struts Tiles")
|
||||
is no longer supported by Spring.
|
||||
`org.springframework.web.servlet.view.tiles3` package. Tiles v1 (a.k.a. "Struts Tiles")
|
||||
as well as Tiles v2 are no longer supported by Spring.
|
||||
====
|
||||
|
||||
|
||||
|
@ -34384,12 +34383,13 @@ SWF allows you to capture logical page flows as self-contained modules that are
|
|||
in different situations, and as such is ideal for building web application modules that
|
||||
guide the user through controlled navigations that drive business processes.
|
||||
|
||||
For more information about SWF, consult the Spring Web Flow website.
|
||||
For more information about SWF, consult the
|
||||
http://projects.spring.io/spring-webflow/[Spring Web Flow website].
|
||||
****
|
||||
|
||||
This chapter details Spring's integration with third party web frameworks such as
|
||||
http://java.sun.com/javaee/javaserverfaces/[JSF], http://struts.apache.org/[Struts], and
|
||||
http://tapestry.apache.org/[Tapestry].
|
||||
http://www.oracle.com/technetwork/java/javaee/javaserverfaces-139869.html[JSF],
|
||||
http://struts.apache.org/[Struts], and http://tapestry.apache.org/[Tapestry].
|
||||
|
||||
One of the core value propositions of the Spring Framework is that of enabling
|
||||
__choice__. In a general sense, Spring does not force one to use or buy into any
|
||||
|
@ -34512,7 +34512,7 @@ has more detail on its specific integration strategies.
|
|||
|
||||
|
||||
[[jsf]]
|
||||
=== JavaServer Faces 1.1 and 1.2
|
||||
=== JavaServer Faces 1.2
|
||||
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.
|
||||
|
||||
|
@ -34526,89 +34526,10 @@ a Spring-based JSF extension that provides rich conversation scope support.
|
|||
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
|
||||
http://www.springframework.org/webflow[Spring Web Flow website] for details!
|
||||
http://projects.spring.io/spring-webflow[Spring Web Flow website] for details!
|
||||
====
|
||||
|
||||
The key element in Spring's JSF integration is the JSF 1.1 `VariableResolver` mechanism.
|
||||
On JSF 1.2, Spring supports the `ELResolver` mechanism as a next-generation version of
|
||||
JSF EL integration.
|
||||
|
||||
|
||||
|
||||
[[jsf-delegatingvariableresolver]]
|
||||
==== DelegatingVariableResolver (JSF 1.1/1.2)
|
||||
The easiest way to integrate one's Spring middle-tier with one's JSF web layer is to use
|
||||
the
|
||||
{javadoc-baseurl}/org/springframework/web/jsf/DelegatingVariableResolver.html[`DelegatingVariableResolver`]
|
||||
class. To configure this variable resolver in one's application, one will need to edit
|
||||
one's __faces-context.xml__ file. After the opening `<faces-config/>` element, add an
|
||||
`<application/>` element and a `<variable-resolver/>` element within it. The value of
|
||||
the variable resolver should reference Spring's `DelegatingVariableResolver`; for
|
||||
example:
|
||||
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
<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>
|
||||
----
|
||||
|
||||
The `DelegatingVariableResolver` will first delegate value lookups to the default
|
||||
resolver of the underlying JSF implementation and then to Spring's 'business context'
|
||||
`WebApplicationContext`. This allows one to easily inject dependencies into one's
|
||||
JSF-managed beans.
|
||||
|
||||
Managed beans are defined in one's `faces-config.xml` file. Find below an example where
|
||||
`#{userManager}` is a bean that is retrieved from the Spring 'business context'.
|
||||
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
<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>
|
||||
----
|
||||
|
||||
|
||||
|
||||
[[jsf-springbeanvariableresolver]]
|
||||
==== SpringBeanVariableResolver (JSF 1.1/1.2)
|
||||
`SpringBeanVariableResolver` is a variant of `DelegatingVariableResolver`. It delegates
|
||||
to the Spring's 'business context' `WebApplicationContext` __first__ 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
|
||||
`FactoryBean` implementations.
|
||||
|
||||
Configuration-wise, simply define `SpringBeanVariableResolver` in your
|
||||
__faces-context.xml__ file:
|
||||
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
<faces-config>
|
||||
<application>
|
||||
<variable-resolver>org.springframework.web.jsf.SpringBeanVariableResolver</variable-resolver>
|
||||
...
|
||||
</application>
|
||||
</faces-config>
|
||||
----
|
||||
|
||||
|
||||
The key element in Spring's JSF integration is the JSF `ELResolver` mechanism.
|
||||
|
||||
[[jsf-springbeanfaceselresolver]]
|
||||
==== SpringBeanFacesELResolver (JSF 1.2+)
|
||||
|
@ -34633,7 +34554,6 @@ __faces-context.xml__ file:
|
|||
----
|
||||
|
||||
|
||||
|
||||
[[jsf-facescontextutils]]
|
||||
==== FacesContextUtils
|
||||
A custom `VariableResolver` works well when mapping one's properties to beans
|
||||
|
@ -34650,704 +34570,35 @@ takes a `FacesContext` parameter rather than a `ServletContext` parameter.
|
|||
|
||||
|
||||
|
||||
|
||||
[[struts]]
|
||||
=== Apache Struts 1.x and 2.x
|
||||
http://struts.apache.org[Struts] used to be the __de facto__ web framework for Java
|
||||
applications, mainly because it was one of the first to be released (June 2001). It has
|
||||
now been renamed to __Struts 1__ (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.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
__The following section discusses Struts 1 a.k.a. "Struts Classic".__
|
||||
|
||||
Struts 2 is effectively a different product, carrying the Struts brand now. Check out the
|
||||
Struts 2 http://struts.apache.org/2.x/docs/spring-plugin.html[Spring Plugin] for the
|
||||
built-in Spring integration shipped with Struts 2.
|
||||
====
|
||||
|
||||
To integrate your Struts 1.x application with Spring, you have two options:
|
||||
|
||||
* Configure Spring to manage your Actions as beans, using the `ContextLoaderPlugin`, and
|
||||
set their dependencies in a Spring context file.
|
||||
* Subclass Spring's `ActionSupport` classes and grab your Spring-managed beans
|
||||
explicitly using a __getWebApplicationContext()__ method.
|
||||
|
||||
|
||||
|
||||
[[struts-contextloaderplugin]]
|
||||
==== ContextLoaderPlugin
|
||||
The
|
||||
{javadoc-baseurl}/org/springframework/web/struts/ContextLoaderPlugIn.html[`ContextLoaderPlugin`]
|
||||
is a Struts 1.1+ plug-in that loads a Spring context file for the Struts
|
||||
`ActionServlet`. This context refers to the root `WebApplicationContext` (loaded by the
|
||||
`ContextLoaderListener`) as its parent. The default name of the context file is the name
|
||||
of the mapped servlet, plus __-servlet.xml__. If `ActionServlet` is defined in web.xml as
|
||||
`<servlet-name>action</servlet-name>`, the default is __/WEB-INF/action-servlet.xml__.
|
||||
|
||||
To configure this plug-in, add the following XML to the plug-ins section near the bottom
|
||||
of your __struts-config.xml__ file:
|
||||
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
<plug-in className="org.springframework.web.struts.ContextLoaderPlugIn"/>
|
||||
----
|
||||
|
||||
The location of the context configuration files can be customized using the '
|
||||
`contextConfigLocation`' property.
|
||||
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
<plug-in className="org.springframework.web.struts.ContextLoaderPlugIn">
|
||||
<set-property property="contextConfigLocation"
|
||||
value="/WEB-INF/action-servlet.xml,/WEB-INF/applicationContext.xml"/>
|
||||
</plug-in>
|
||||
----
|
||||
|
||||
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 `MockStrutsTestCase`
|
||||
won't initialize Listeners on startup so putting all your context files in the plugin is
|
||||
a workaround. (A
|
||||
http://sourceforge.net/tracker/index.php?func=detail&aid=1088866&group_id=39190&atid=424562[bug
|
||||
has been filed] for this issue, but has been closed as 'Wont Fix').
|
||||
|
||||
After configuring this plug-in in __struts-config.xml__, you can configure your `Action`
|
||||
to be managed by Spring. Spring (1.1.3+) provides two ways to do this:
|
||||
|
||||
* Override Struts' default `RequestProcessor` with Spring's `DelegatingRequestProcessor`.
|
||||
* Use the `DelegatingActionProxy` class in the `type` attribute of your
|
||||
`<action-mapping>`.
|
||||
|
||||
Both of these methods allow you to manage your Actions and their dependencies in the
|
||||
__action-servlet.xml__ file. The bridge between the Action in __struts-config.xml__ and
|
||||
__action-servlet.xml__ is built with the action-mapping's "path" and the bean's "name".
|
||||
If you have the following in your __struts-config.xml__ file:
|
||||
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
<action path="/users" .../>
|
||||
----
|
||||
|
||||
You must define that Action's bean with the "/users" name in __action-servlet.xml__:
|
||||
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
<bean name="/users" .../>
|
||||
----
|
||||
|
||||
|
||||
[[struts-delegatingrequestprocessor]]
|
||||
===== DelegatingRequestProcessor
|
||||
To configure the
|
||||
{javadoc-baseurl}/org/springframework/web/struts/DelegatingRequestProcessor.html[`DelegatingRequestProcessor`]
|
||||
in your __struts-config.xml__ file, override the "processorClass" property in the
|
||||
<controller> element. These lines follow the <action-mapping> element.
|
||||
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
<controller>
|
||||
<set-property property="processorClass"
|
||||
value="org.springframework.web.struts.DelegatingRequestProcessor"/>
|
||||
</controller>
|
||||
----
|
||||
|
||||
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:
|
||||
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
<action path="/user" type="com.whatever.struts.UserAction"/>
|
||||
<action path="/user"/>
|
||||
----
|
||||
|
||||
If you're using Struts' __modules__ feature, your bean names must contain the module
|
||||
prefix. For example, an action defined as `<action path="/user"/>` with module prefix
|
||||
"admin" requires a bean name with `<bean name="/admin/user"/>`.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
If you are using Tiles in your Struts application, you must configure your <controller>
|
||||
with the
|
||||
{javadoc-baseurl}/org/springframework/web/struts/DelegatingTilesRequestProcessor.html[`DelegatingTilesRequestProcessor`]
|
||||
instead.
|
||||
====
|
||||
|
||||
|
||||
[[struts-delegatingactionproxy]]
|
||||
===== DelegatingActionProxy
|
||||
If you have a custom `RequestProcessor` and can't use the `DelegatingRequestProcessor`
|
||||
or `DelegatingTilesRequestProcessor` approaches, you can use the
|
||||
{javadoc-baseurl}/org/springframework/web/struts/DelegatingActionProxy.html[`DelegatingActionProxy`]
|
||||
as the type in your action-mapping.
|
||||
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
<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>
|
||||
----
|
||||
|
||||
The bean definition in __action-servlet.xml__ remains the same, whether you use a custom
|
||||
`RequestProcessor` or the `DelegatingActionProxy`.
|
||||
|
||||
If you define your `Action` 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 `Action` instance for each request. To activate the latter,
|
||||
add __scope="prototype"__ to your Action's bean definition.
|
||||
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
<bean name="/user" scope="prototype" autowire="byName"
|
||||
class="org.example.web.UserAction"/>
|
||||
----
|
||||
|
||||
|
||||
|
||||
[[struts-actionsupport]]
|
||||
==== ActionSupport Classes
|
||||
As previously mentioned, you can retrieve the `WebApplicationContext` from the
|
||||
`ServletContext` using the `WebApplicationContextUtils` class. An easier way is to
|
||||
extend Spring's `Action` classes for Struts. For example, instead of subclassing Struts'
|
||||
`Action` class, you can subclass Spring's
|
||||
{javadoc-baseurl}/org/springframework/web/struts/ActionSupport.html[`ActionSupport`]
|
||||
class.
|
||||
|
||||
The `ActionSupport` class provides additional convenience methods,
|
||||
like __getWebApplicationContext()__. Below is an example of how you might use this in an
|
||||
Action:
|
||||
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
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");
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Spring includes subclasses for all of the standard Struts Actions - the Spring versions
|
||||
merely have __Support__ appended to the name:
|
||||
|
||||
* {javadoc-baseurl}/org/springframework/web/struts/ActionSupport.html[`ActionSupport`],
|
||||
* {javadoc-baseurl}/org/springframework/web/struts/DispatchActionSupport.html[`DispatchActionSupport`],
|
||||
* {javadoc-baseurl}/org/springframework/web/struts/LookupDispatchActionSupport.html[`LookupDispatchActionSupport`]
|
||||
and
|
||||
* {javadoc-baseurl}/org/springframework/web/struts/MappingDispatchActionSupport.html[`MappingDispatchActionSupport`].
|
||||
|
||||
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 `ContextLoaderPlugin` allows you to easily add new
|
||||
dependencies in your context XML file. Either way, Spring provides some nice options for
|
||||
integrating with Struts.
|
||||
=== Apache Struts 2.x
|
||||
Invented by Craig McClanahan, http://struts.apache.org[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.
|
||||
|
||||
Check out the Struts
|
||||
https://struts.apache.org/release/2.3.x/docs/spring-plugin.html[Spring Plugin] for the
|
||||
built-in Spring integration shipped with Struts.
|
||||
|
||||
|
||||
|
||||
[[tapestry]]
|
||||
=== Tapestry 3.x and 4.x
|
||||
=== Tapestry 5.x
|
||||
From the http://tapestry.apache.org/[Tapestry homepage]:
|
||||
|
||||
"__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.__"
|
||||
Tapestry is a "__Component oriented framework for creating dynamic, robust,
|
||||
highly scalable web applications in Java.__"
|
||||
|
||||
While Spring has its own <<mvc,powerful web layer>>, 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.
|
||||
|
||||
A __typical__ 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 `[]` brackets.)
|
||||
|
||||
"__A very successful design pattern in Tapestry is to keep pages and components very
|
||||
simple, and __delegate__ 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.__"
|
||||
|
||||
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 http://howardlewisship.com/blog/[Howard M. Lewis Ship], so hats off to
|
||||
him for what is really some silky smooth integration).
|
||||
|
||||
|
||||
|
||||
[[tapestry-di]]
|
||||
==== Injecting Spring-managed beans
|
||||
Assume we have the following simple Spring container definition (in the ubiquitous XML
|
||||
format):
|
||||
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
**xmlns:jee="http://www.springframework.org/schema/jee"**
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
**http://www.springframework.org/schema/jee
|
||||
http://www.springframework.org/schema/jee/spring-jee.xsd**">
|
||||
|
||||
<!-- 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>
|
||||
----
|
||||
|
||||
Inside the Tapestry application, the above bean definitions need to be
|
||||
<<web-integration-common,loaded into a Spring container>>, and any relevant Tapestry
|
||||
pages need to be supplied (injected) with the `authenticationService` and `userService`
|
||||
beans, which implement the `AuthenticationService` and `UserService` interfaces,
|
||||
respectively.
|
||||
|
||||
At this point, the application context is available to a web application by calling
|
||||
Spring's static utility function
|
||||
`WebApplicationContextUtils.getApplicationContext(servletContext)`, where servletContext
|
||||
is the standard `ServletContext` from the Java EE Servlet specification. As such, one
|
||||
simple mechanism for a page to get an instance of the `UserService`, for example, would
|
||||
be with code such as:
|
||||
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
WebApplicationContext appContext = WebApplicationContextUtils.getApplicationContext(
|
||||
getRequestCycle().getRequestContext().getServlet().getServletContext());
|
||||
UserService userService = (UserService) appContext.getBean("userService");
|
||||
// ... some code which uses UserService
|
||||
----
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
This next section is applicable to Tapestry 3.x. If you are using Tapestry version 4.x,
|
||||
please consult the section entitled <<tapestry-4-style-di>>.
|
||||
====
|
||||
|
||||
|
||||
[[tapestry-pre4-style-di]]
|
||||
===== Dependency Injecting Spring Beans into Tapestry pages
|
||||
First we need to make the `ApplicationContext` available to the Tapestry page or
|
||||
Component without having to have the `ServletContext`; this is because at the stage in
|
||||
the page's/component's lifecycle when we need to access the `ApplicationContext`, the
|
||||
`ServletContext` won't be easily available to the page, so we can't use
|
||||
`WebApplicationContextUtils.getApplicationContext(servletContext)` directly. One way is
|
||||
by defining a custom version of the Tapestry `IEngine` which exposes this for us:
|
||||
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
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:
|
||||
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
file: xportal.application:
|
||||
<?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>
|
||||
----
|
||||
|
||||
|
||||
[[tapestry-componentdefs]]
|
||||
===== Component definition files
|
||||
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
|
||||
`ApplicationContext`, and create page or component properties for them. For example:
|
||||
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
<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>
|
||||
----
|
||||
|
||||
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:
|
||||
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
<?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>
|
||||
----
|
||||
|
||||
|
||||
[[tapestry-getters]]
|
||||
===== Adding abstract accessors
|
||||
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).
|
||||
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
// our UserService implementation; will come from page definition
|
||||
public abstract UserService getUserService();
|
||||
// our AuthenticationService implementation; will come from page definition
|
||||
public abstract AuthenticationService getAuthenticationService();
|
||||
----
|
||||
|
||||
For the sake of completeness, the entire Java class, for a login page in this example,
|
||||
might look like this:
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
|
||||
[[tapestry-4-style-di]]
|
||||
===== Dependency Injecting Spring Beans into Tapestry pages - Tapestry 4.x style
|
||||
Effecting the dependency injection of Spring-managed beans into Tapestry pages in
|
||||
Tapestry version 4.x is __so__ much simpler. All that is needed is a single
|
||||
http://howardlewisship.com/tapestry-javaforge/tapestry-spring/[add-on library], 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 `WEB-INF/lib`).
|
||||
|
||||
You will then need to create and expose the Spring container using the
|
||||
<<web-integration-common,method detailed previously>>. You can then inject
|
||||
Spring-managed beans into Tapestry very easily; if we are using Java 5, consider the
|
||||
`Login` page from above: we simply need to annotate the appropriate getter methods in
|
||||
order to dependency inject the Spring-managed `userService` and `authenticationService`
|
||||
objects (lots of the class definition has been elided for clarity).
|
||||
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
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();
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
We are almost done. All that remains is the HiveMind configuration that exposes the
|
||||
Spring container stored in the `ServletContext` as a HiveMind service; for example:
|
||||
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
<?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>
|
||||
----
|
||||
|
||||
If you are using Java 5 (and thus have access to annotations), then that really is it.
|
||||
|
||||
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 `.page` or `.jwc` file for the `Login`
|
||||
page (or component):
|
||||
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
<inject property="userService" object="spring:userService"/>
|
||||
<inject property="authenticationService" object="spring:authenticationService"/>
|
||||
----
|
||||
|
||||
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.
|
||||
for the web user interface and the Spring container for the lower layers.
|
||||
|
||||
For more information, check out Tapestry's dedicated
|
||||
https://tapestry.apache.org/integrating-with-spring-framework.html[integration module for
|
||||
Spring].
|
||||
|
||||
|
||||
|
||||
|
@ -35356,7 +34607,7 @@ throughout the stack in this Tapestry application.
|
|||
Find below links to further resources about the various web frameworks described in this
|
||||
chapter.
|
||||
|
||||
* The http://java.sun.com/javaee/javaserverfaces/[JSF] homepage
|
||||
* The http://www.oracle.com/technetwork/java/javaee/javaserverfaces-139869.html[JSF] homepage
|
||||
* The http://struts.apache.org/[Struts] homepage
|
||||
* The http://tapestry.apache.org/[Tapestry] homepage
|
||||
|
||||
|
|
Loading…
Reference in New Issue