Clean MVC reference documentation

Remove reference to removed deprecations and generally cleanup the MVC
documentation.
Cleanup Struts
This commit is contained in:
Brian Clozel 2013-12-11 11:29:38 -08:00 committed by Phillip Webb
parent d6b4d92eed
commit 0a52c86559
1 changed files with 27 additions and 776 deletions

View File

@ -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