diff --git a/src/asciidoc/index.adoc b/src/asciidoc/index.adoc index 885664150d..f3b029a2f4 100644 --- a/src/asciidoc/index.adoc +++ b/src/asciidoc/index.adoc @@ -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 `` element, add an -`` element and a `` element within it. The value of -the variable resolver should reference Spring's `DelegatingVariableResolver`; for -example: - -[source,xml,indent=0] -[subs="verbatim,quotes"] ----- - - - org.springframework.web.jsf.DelegatingVariableResolver - - en - en - es - - messages - - ----- - -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"] ----- - - userList - com.whatever.jsf.UserList - request - - userManager - #{userManager} - - ----- - - - -[[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"] ----- - - - org.springframework.web.jsf.SpringBeanVariableResolver - ... - - ----- - - +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 -`action`, 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"] ----- - ----- - -The location of the context configuration files can be customized using the ' -`contextConfigLocation`' property. - -[source,xml,indent=0] -[subs="verbatim,quotes"] ----- - - - ----- - -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 - ``. - -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"] ----- - ----- - -You must define that Action's bean with the "/users" name in __action-servlet.xml__: - -[source,xml,indent=0] -[subs="verbatim,quotes"] ----- - ----- - - -[[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 - element. These lines follow the element. - -[source,xml,indent=0] -[subs="verbatim,quotes"] ----- - - - ----- - -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"] ----- - - ----- - -If you're using Struts' __modules__ feature, your bean names must contain the module -prefix. For example, an action defined as `` with module prefix -"admin" requires a bean name with ``. - -[NOTE] -==== -If you are using Tiles in your Struts application, you must configure your -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"] ----- - - - - ----- - -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"] ----- - ----- - - - -[[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 <>, 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"] ----- - - - - - - - - - - - - - - - - - - - - - - - - *=PROPAGATION_REQUIRED - - - - - - - - - - - - - - *=PROPAGATION_REQUIRED - - - ----- - -Inside the Tapestry application, the above bean definitions need to be -<>, 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-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: - - - - ----- - - -[[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"] ----- - - global.appContext.getBean("userService") - - - global.appContext.getBean("authenticationService") - ----- - -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"] ----- - - **** - - - - - - - - - global.appContext.getBean("userService") - - - global.appContext.getBean("authenticationService") - - - - - - - - - - - - - - - - - - - - - - - ----- - - -[[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. - *

- * 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 -<>. 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"] ----- - - - - - - - - - - - - - - - - - ----- - -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"] ----- - - ----- - -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