web-integration.xml 54.3 KB
Newer Older
1
<?xml version="1.0" encoding="UTF-8"?>
2
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
3 4
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<chapter id="web-integration">
5 6 7 8
  <title>Integrating with other web frameworks</title>

  <section id="intro">
    <title>Introduction</title>
9 10 11 12 13 14 15

    <para>This chapter details Spring's integration with third party web
    frameworks such as <ulink
    url="http://java.sun.com/javaee/javaserverfaces/">JSF</ulink>, <ulink
    url="http://struts.apache.org/">Struts</ulink>, <ulink
    url="http://www.opensymphony.com/webwork/">WebWork</ulink>, and <ulink
    url="http://tapestry.apache.org/">Tapestry</ulink>.</para>
16 17

    <!-- insert some content about Spring Web Flow here -->
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56

    <xi:include href="swf-sidebar.xml"
                xmlns:xi="http://www.w3.org/2001/XInclude" />

    <para>One of the core value propositions of the Spring Framework is that
    of enabling <emphasis>choice</emphasis>. In a general sense, Spring does
    not force one to use or buy into any particular architecture, technology,
    or methodology (although it certainly recommends some over others). This
    freedom to pick and choose the architecture, technology, or methodology
    that is most relevant to a developer and his or her development team is
    arguably most evident in the web area, where Spring provides its own web
    framework (<link linkend="mvc">Spring MVC</link>), while at the same time
    providing integration with a number of popular third party web frameworks.
    This allows one to continue to leverage any and all of the skills one may
    have acquired in a particular web framework such as Struts, while at the
    same time being able to enjoy the benefits afforded by Spring in other
    areas such as data access, declarative transaction management, and
    flexible configuration and application assembly.</para>

    <para>Having dispensed with the woolly sales patter (c.f. the previous
    paragraph), the remainder of this chapter will concentrate upon the meaty
    details of integrating your favourite web framework with Spring. One thing
    that is often commented upon by developers coming to Java from other
    languages is the seeming super-abundance of web frameworks available in
    Java... there are indeed a great number of web frameworks in the Java
    space; in fact there are far too many to cover with any semblance of
    detail in a single chapter. This chapter thus picks four of the more
    popular web frameworks in Java, starting with the Spring configuration
    that is common to all of the supported web frameworks, and then detailing
    the specific integration options for each supported web framework.</para>

    <para><emphasis> Please note that this chapter does not attempt to explain
    how to use any of the supported web frameworks. For example, if you want
    to use Struts for the presentation layer of your web application, the
    assumption is that you are already familiar with Struts. If you need
    further details about any of the supported web frameworks themselves,
    please do consult the section entitled <xref
    linkend="web-integration-resources" /> at the end of this chapter.
    </emphasis></para>
57 58 59 60
  </section>

  <section id="web-integration-common">
    <title>Common configuration</title>
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83

    <para>Before diving into the integration specifics of each supported web
    framework, let us first take a look at the Spring configuration that
    <emphasis>not</emphasis> specific to any one web framework. (This section
    is equally applicable to Spring's own web framework, Spring MVC.)</para>

    <para>One of the concepts (for want of a better word) espoused by
    (Spring's) lightweight application model is that of a layered
    architecture. Remember that in a 'classic' layered architecture, the web
    layer is but one of many layers... it serves as one of the entry points
    into a server side application, and it delegates to service objects
    (facades) defined in a service layer to satisfy business specific (and
    presentation-technology agnostic) use cases. In Spring, these service
    objects, any other business-specific objects, data access objects, etc.
    exist in a distinct 'business context', which contains
    <emphasis>no</emphasis> web or presentation layer objects (presentation
    objects such as Spring MVC controllers are typically configured in a
    distinct 'presentation context'). This section details how one configures
    a Spring container (a <classname>WebApplicationContext</classname>) that
    contains all of the 'business beans' in one's application.</para>

    <para>Onto specifics... all that one need do is to declare a <ulink
    url="http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/web/context/ContextLoaderListener.html"><classname>ContextLoaderListener</classname></ulink>
T
Thomas Risberg 已提交
84
    in the standard Java EE servlet <literal>web.xml</literal> file of one's web
85 86 87 88 89 90 91 92 93 94
    application, and add a <literal>contextConfigLocation</literal>
    &lt;context-param/&gt; section (in the same file) that defines which set
    of Spring XML cpnfiguration files to load.</para>

    <para>Find below the &lt;listener/&gt; configuration:</para>

    <programlisting language="xml">&lt;listener&gt;
  &lt;listener-class&gt;org.springframework.web.context.ContextLoaderListener&lt;/listener-class&gt;
&lt;/listener&gt;</programlisting>

95
    <note>
96 97 98 99 100 101
      <para>Listeners were added to the Servlet API in version 2.3; listener
      startup order was finally clarified in Servlet 2.4. If you have a
      Servlet 2.3 container, you can use the <ulink
      url="http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/web/context/ContextLoaderServlet.html"><classname>ContextLoaderServlet</classname></ulink>
      to achieve the same functionality in a 100% portable fashion (with
      respect to startup order).</para>
102
    </note>
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148

    <para>Find below the &lt;context-param/&gt; configuration:</para>

    <programlisting language="xml">&lt;context-param&gt;
  &lt;param-name&gt;contextConfigLocation&lt;/param-name&gt;
  &lt;param-value&gt;/WEB-INF/applicationContext*.xml&lt;/param-value&gt;
&lt;/context-param&gt;</programlisting>

    <para>If you don't specify the <literal>contextConfigLocation</literal>
    context parameter, the <classname>ContextLoaderListener</classname> will
    look for a file called <literal>/WEB-INF/applicationContext.xml</literal>
    to load. Once the context files are loaded, Spring creates a <ulink
    url="http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/web/context/WebApplicationContext.html"><classname>WebApplicationContext</classname></ulink>
    object based on the bean definitions and stores it in the
    <interface>ServletContext</interface> of one's web application.</para>

    <para>All Java web frameworks are built on top of the Servlet API, and so
    one can use the following code snippet to get access to this 'business
    context' <interface>ApplicationContext</interface> created by the
    <classname>ContextLoaderListener</classname>.</para>

    <programlisting language="java">WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(servletContext);</programlisting>

    <para>The <ulink
    url="http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/web/context/support/WebApplicationContextUtils.html"><classname>WebApplicationContextUtils</classname></ulink>
    class is for convenience, so you don't have to remember the name of the
    <interface>ServletContext</interface> attribute. Its
    <emphasis>getWebApplicationContext()</emphasis> method will return
    <literal>null</literal> if an object doesn't exist under the
    <literal>WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE</literal>
    key. Rather than risk getting <classname>NullPointerExceptions</classname>
    in your application, it's better to use the
    <literal>getRequiredWebApplicationContext()</literal> method. This method
    throws an exception when the <interface>ApplicationContext</interface> is
    missing.</para>

    <para>Once you have a reference to the
    <classname>WebApplicationContext</classname>, you can retrieve beans by
    their name or type. Most developers retrieve beans by name, then cast them
    to one of their implemented interfaces.</para>

    <para>Fortunately, most of the frameworks in this section have simpler
    ways of looking up beans. Not only do they make it easy to get beans from
    a Spring container, but they also allow you to use dependency injection on
    their controllers. Each web framework section has more detail on its
    specific integration strategies.</para>
149 150 151 152
  </section>

  <section id="jsf">
    <title>JavaServer Faces 1.1 and 1.2</title>
153 154 155 156 157 158 159 160 161 162 163 164 165

    <para>JavaServer Faces (JSF) is the JCP's standard component-based,
    event-driven web user interface framework. As of Java EE 5, it is an
    official part of the Java EE umbrella.</para>

    <para>For a popular JSF runtime as well as for popular JSF component
    libraries, check out the <ulink url="http://myfaces.apache.org/">Apache
    MyFaces project</ulink>. The MyFaces project also provides common JSF
    extensions such as <ulink
    url="http://myfaces.apache.org/orchestra/">MyFaces Orchestra</ulink>: a
    Spring-based JSF extension that provides rich conversation scope
    support.</para>

166
    <note>
167 168 169 170 171 172
      <para>Spring Web Flow 2.0 provides rich JSF support through its newly
      established Spring Faces module, both for JSF-centric usage (as
      described in this section) and for Spring-centric usage (using JSF views
      within a Spring MVC dispatcher). Check out the <ulink
      url="http://www.springframework.org/webflow">Spring Web Flow
      website</ulink> for details!</para>
173
    </note>
174 175 176 177 178 179

    <para>The key element in Spring's JSF integration is the JSF 1.1
    <classname>VariableResolver</classname> mechanism. On JSF 1.2, Spring
    supports the <classname>ELResolver</classname> mechanism as a
    next-generation version of JSF EL integration.</para>

180 181
    <section id="jsf-delegatingvariableresolver">
      <title>DelegatingVariableResolver (JSF 1.1/1.2)</title>
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226

      <para>The easiest way to integrate one's Spring middle-tier with one's
      JSF web layer is to use the <ulink
      url="http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/web/jsf/DelegatingVariableResolver.html">
      <classname>DelegatingVariableResolver</classname></ulink> class. To
      configure this variable resolver in one's application, one will need to
      edit one's <emphasis>faces-context.xml</emphasis> file. After the
      opening <literal>&lt;faces-config/&gt;</literal> element, add an
      <literal>&lt;application/&gt;</literal> element and a
      <literal>&lt;variable-resolver/&gt;</literal> element within it. The
      value of the variable resolver should reference Spring's
      <classname>DelegatingVariableResolver</classname>; for example:</para>

      <programlisting language="xml">&lt;faces-config&gt;
  &lt;application&gt;
    &lt;variable-resolver&gt;org.springframework.web.jsf.DelegatingVariableResolver&lt;/variable-resolver&gt;
    &lt;locale-config&gt;
      &lt;default-locale&gt;en&lt;/default-locale&gt;
      &lt;supported-locale&gt;en&lt;/supported-locale&gt;
      &lt;supported-locale&gt;es&lt;/supported-locale&gt;
    &lt;/locale-config&gt;
    &lt;message-bundle&gt;messages&lt;/message-bundle&gt;
  &lt;/application&gt;
&lt;/faces-config&gt;</programlisting>

      <para>The <classname>DelegatingVariableResolver</classname> will first
      delegate value lookups to the default resolver of the underlying JSF
      implementation, and then to Spring's 'business context'
      <classname>WebApplicationContext</classname>. This allows one to easily
      inject dependencies into one's JSF-managed beans.</para>

      <para>Managed beans are defined in one's
      <literal>faces-config.xml</literal> file. Find below an example where
      <literal>#{userManager}</literal> is a bean that is retrieved from the
      Spring 'business context'.</para>

      <programlisting language="xml">&lt;managed-bean&gt;
  &lt;managed-bean-name&gt;userList&lt;/managed-bean-name&gt;
  &lt;managed-bean-class&gt;com.whatever.jsf.UserList&lt;/managed-bean-class&gt;
  &lt;managed-bean-scope&gt;request&lt;/managed-bean-scope&gt;
  &lt;managed-property&gt;
    &lt;property-name&gt;userManager&lt;/property-name&gt;
    &lt;value&gt;#{userManager}&lt;/value&gt;
  &lt;/managed-property&gt;
&lt;/managed-bean&gt;</programlisting>
227
    </section>
228

229 230
    <section id="jsf-springbeanvariableresolver">
      <title>SpringBeanVariableResolver (JSF 1.1/1.2)</title>
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247

      <para><classname>SpringBeanVariableResolver</classname> is a variant of
      <classname>DelegatingVariableResolver</classname>. It delegates to the
      Spring's 'business context' <classname>WebApplicationContext</classname>
      <emphasis>first</emphasis>, then to the default resolver of the
      underlying JSF implementation. This is useful in particular when using
      request/session-scoped beans with special Spring resolution rules, e.g.
      Spring <interfacename>FactoryBean</interfacename>
      implementations.</para>

      <para>Configuration-wise, simply define
      <classname>SpringBeanVariableResolver</classname> in your
      <emphasis>faces-context.xml</emphasis> file:</para>

      <programlisting language="xml">&lt;faces-config&gt;
  &lt;application&gt;
    &lt;variable-resolver&gt;org.springframework.web.jsf.SpringBeanVariableResolver&lt;/variable-resolver&gt;
248
    ...
249 250
  &lt;/application&gt;
&lt;/faces-config&gt;</programlisting>
251
    </section>
252

253 254
    <section id="jsf-springbeanfaceselresolver">
      <title>SpringBeanFacesELResolver (JSF 1.2+)</title>
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270

      <para><classname>SpringBeanFacesELResolver</classname> is a JSF 1.2
      compliant <classname>ELResolver</classname> implementation, integrating
      with the standard Unified EL as used by JSF 1.2 and JSP 2.1. Like
      <classname>SpringBeanVariableResolver</classname>, it delegates to the
      Spring's 'business context' <classname>WebApplicationContext</classname>
      <emphasis>first</emphasis>, then to the default resolver of the
      underlying JSF implementation.</para>

      <para>Configuration-wise, simply define
      <classname>SpringBeanFacesELResolver</classname> in your JSF 1.2
      <emphasis>faces-context.xml</emphasis> file:</para>

      <programlisting language="xml">&lt;faces-config&gt;
  &lt;application&gt;
    &lt;el-resolver&gt;org.springframework.web.jsf.el.SpringBeanFacesELResolver&lt;/el-resolver&gt;
271
    ...
272 273
  &lt;/application&gt;
&lt;/faces-config&gt;</programlisting>
274
    </section>
275

276 277
    <section id="jsf-facescontextutils">
      <title>FacesContextUtils</title>
278 279 280 281 282 283 284 285 286 287 288 289

      <para>A custom <interfacename>VariableResolver</interfacename> works
      well when mapping one's properties to beans in
      <emphasis>faces-config.xml</emphasis>, but at times one may need to grab
      a bean explicitly. The <ulink
      url="http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/web/jsf/FacesContextUtils.html">
      <classname>FacesContextUtils</classname></ulink> class makes this easy.
      It is similar to <classname>WebApplicationContextUtils</classname>,
      except that it takes a <classname>FacesContext</classname> parameter
      rather than a <interface>ServletContext</interface> parameter.</para>

      <programlisting language="java">ApplicationContext ctx = FacesContextUtils.getWebApplicationContext(FacesContext.getCurrentInstance());</programlisting>
290 291 292 293 294
    </section>
  </section>

  <section id="struts">
    <title>Apache Struts 1.x and 2.x</title>
295 296 297 298 299 300 301 302 303 304 305 306

    <para><ulink url="http://struts.apache.org">Struts</ulink> is the
    <emphasis>de facto</emphasis> web framework for Java applications, mainly
    because it was one of the first to be released (June 2001). Invented by
    Craig McClanahan, Struts is an open source project hosted by the Apache
    Software Foundation. At the time, it greatly simplified the JSP/Servlet
    programming paradigm and won over many developers who were using
    proprietary frameworks. It simplified the programming model, it was open
    source (and thus free as in beer), and it had a large community, which
    allowed the project to grow and become popular among Java web
    developers.</para>

307
    <note>
308 309 310 311 312 313 314 315 316 317
      <para><emphasis>The following section discusses Struts 1 a.k.a. "Struts
      Classic".</emphasis></para>

      <para>Struts 2 is effectively a different product - a successor of
      WebWork 2.2 (as discussed in <xref linkend="webwork" />), carrying the
      Struts brand now. Check out the Struts 2 <ulink
      url="http://struts.apache.org/2.x/docs/spring-plugin.html">Spring
      Plugin</ulink> for the built-in Spring integration shipped with Struts
      2. In general, Struts 2 is closer to WebWork 2.2 than to Struts 1 in
      terms of its Spring integration implications.</para>
318
    </note>
319 320 321 322

    <para>To integrate your Struts 1.x application with Spring, you have two
    options:</para>

323 324
    <itemizedlist>
      <listitem>
325 326 327
        <para>Configure Spring to manage your Actions as beans, using the
        <classname>ContextLoaderPlugin</classname>, and set their dependencies
        in a Spring context file.</para>
328
      </listitem>
329

330
      <listitem>
331 332 333
        <para>Subclass Spring's <classname>ActionSupport</classname> classes
        and grab your Spring-managed beans explicitly using a
        <emphasis>getWebApplicationContext()</emphasis> method.</para>
334 335
      </listitem>
    </itemizedlist>
336

337 338
    <section id="struts-contextloaderplugin">
      <title>ContextLoaderPlugin</title>
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379

      <para>The <ulink
      url="http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/web/struts/ContextLoaderPlugIn.html"><classname>ContextLoaderPlugin</classname></ulink>
      is a Struts 1.1+ plug-in that loads a Spring context file for the Struts
      <classname>ActionServlet</classname>. This context refers to the root
      <classname>WebApplicationContext</classname> (loaded by the
      <classname>ContextLoaderListener</classname>) as its parent. The default
      name of the context file is the name of the mapped servlet, plus
      <emphasis>-servlet.xml</emphasis>. If
      <classname>ActionServlet</classname> is defined in web.xml as
      <literal>&lt;servlet-name&gt;action&lt;/servlet-name&gt;</literal>, the
      default is <emphasis>/WEB-INF/action-servlet.xml</emphasis>.</para>

      <para>To configure this plug-in, add the following XML to the plug-ins
      section near the bottom of your <emphasis>struts-config.xml</emphasis>
      file:</para>

      <programlisting language="xml">&lt;plug-in className="org.springframework.web.struts.ContextLoaderPlugIn"/&gt;</programlisting>

      <para>The location of the context configuration files can be customized
      using the '<literal>contextConfigLocation</literal>' property.</para>

      <programlisting language="xml">&lt;plug-in className="org.springframework.web.struts.ContextLoaderPlugIn"&gt;
  &lt;set-property property="contextConfigLocation"
      value="/WEB-INF/action-servlet.xml,/WEB-INF/applicationContext.xml"/&gt;
&lt;/plug-in&gt;</programlisting>

      <para>It is possible to use this plugin to load all your context files,
      which can be useful when using testing tools like StrutsTestCase.
      StrutsTestCase's <classname>MockStrutsTestCase</classname> won't
      initialize Listeners on startup so putting all your context files in the
      plugin is a workaround. (A <ulink
      url="http://sourceforge.net/tracker/index.php?func=detail&amp;aid=1088866&amp;group_id=39190&amp;atid=424562">
      bug has been filed</ulink> for this issue, but has been closed as 'Wont
      Fix').</para>

      <para>After configuring this plug-in in
      <emphasis>struts-config.xml</emphasis>, you can configure your
      <classname>Action</classname> to be managed by Spring. Spring (1.1.3+)
      provides two ways to do this:</para>

380 381
      <itemizedlist>
        <listitem>
382 383 384
          <para>Override Struts' default
          <classname>RequestProcessor</classname> with Spring's
          <classname>DelegatingRequestProcessor</classname>.</para>
385
        </listitem>
386

387
        <listitem>
388 389 390
          <para>Use the <classname>DelegatingActionProxy</classname> class in
          the <literal>type</literal> attribute of your
          <literal>&lt;action-mapping&gt;</literal>.</para>
391 392
        </listitem>
      </itemizedlist>
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407

      <para>Both of these methods allow you to manage your Actions and their
      dependencies in the <emphasis>action-servlet.xml</emphasis> file. The
      bridge between the Action in <emphasis>struts-config.xml</emphasis> and
      <emphasis>action-servlet.xml</emphasis> is built with the
      action-mapping's "path" and the bean's "name". If you have the following
      in your <emphasis>struts-config.xml</emphasis> file:</para>

      <programlisting language="xml">&lt;action path="/users" .../&gt;</programlisting>

      <para>You must define that Action's bean with the "/users" name in
      <emphasis>action-servlet.xml</emphasis>:</para>

      <programlisting language="xml">&lt;bean name="/users" .../&gt;</programlisting>

408 409
      <section id="struts-delegatingrequestprocessor">
        <title>DelegatingRequestProcessor</title>
410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436

        <para>To configure the <ulink
        url="http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/web/struts/DelegatingRequestProcessor.html">
        <literal>DelegatingRequestProcessor</literal></ulink> in your
        <emphasis>struts-config.xml</emphasis> file, override the
        "processorClass" property in the &lt;controller&gt; element. These
        lines follow the &lt;action-mapping&gt; element.</para>

        <programlisting language="xml">&lt;controller&gt;
  &lt;set-property property="processorClass"
      value="org.springframework.web.struts.DelegatingRequestProcessor"/&gt;
&lt;/controller&gt;</programlisting>

        <para>After adding this setting, your Action will automatically be
        looked up in Spring's context file, no matter what the type. In fact,
        you don't even need to specify a type. Both of the following snippets
        will work:</para>

        <programlisting language="xml">&lt;action path="/user" type="com.whatever.struts.UserAction"/&gt;
&lt;action path="/user"/&gt;</programlisting>

        <para>If you're using Struts' <emphasis>modules</emphasis> feature,
        your bean names must contain the module prefix. For example, an action
        defined as <literal>&lt;action path="/user"/&gt;</literal> with module
        prefix "admin" requires a bean name with <literal>&lt;bean
        name="/admin/user"/&gt;</literal>.</para>

437
        <note>
438 439 440 441
          <para>If you are using Tiles in your Struts application, you must
          configure your &lt;controller&gt; with the <ulink
          url="http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/web/struts/DelegatingTilesRequestProcessor.html"><classname>DelegatingTilesRequestProcessor</classname></ulink>
          instead.</para>
442 443
        </note>
      </section>
444

445 446
      <section id="struts-delegatingactionproxy">
        <title>DelegatingActionProxy</title>
447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476

        <para>If you have a custom <classname>RequestProcessor</classname> and
        can't use the <classname>DelegatingRequestProcessor</classname> or
        <classname>DelegatingTilesRequestProcessor</classname> approaches, you
        can use the <ulink
        url="http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/web/struts/DelegatingActionProxy.html">
        <classname>DelegatingActionProxy</classname></ulink> as the type in
        your action-mapping.</para>

        <programlisting language="xml">&lt;action path="/user" type="org.springframework.web.struts.DelegatingActionProxy"
    name="userForm" scope="request" validate="false" parameter="method"&gt;
  &lt;forward name="list" path="/userList.jsp"/&gt;
  &lt;forward name="edit" path="/userForm.jsp"/&gt;
&lt;/action&gt;</programlisting>

        <para>The bean definition in <emphasis>action-servlet.xml</emphasis>
        remains the same, whether you use a custom
        <literal>RequestProcessor</literal> or the
        <classname>DelegatingActionProxy</classname>.</para>

        <para>If you define your <classname>Action</classname> in a context
        file, the full feature set of Spring's bean container will be
        available for it: dependency injection as well as the option to
        instantiate a new <classname>Action</classname> instance for each
        request. To activate the latter, add
        <emphasis>scope="prototype"</emphasis> to your Action's bean
        definition.</para>

        <programlisting language="xml">&lt;bean name="/user" scope="prototype" autowire="byName"
    class="org.example.web.UserAction"/&gt;</programlisting>
477 478
      </section>
    </section>
479

480 481
    <section id="struts-actionsupport">
      <title>ActionSupport Classes</title>
482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498

      <para>As previously mentioned, you can retrieve the
      <classname>WebApplicationContext</classname> from the
      <interface>ServletContext</interface> using the
      <classname>WebApplicationContextUtils</classname> class. An easier way
      is to extend Spring's <classname>Action</classname> classes for Struts.
      For example, instead of subclassing Struts'
      <classname>Action</classname> class, you can subclass Spring's <ulink
      url="http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/web/struts/ActionSupport.html">
      <classname>ActionSupport</classname></ulink> class.</para>

      <para>The <classname>ActionSupport</classname> class provides additional
      convenience methods, like
      <emphasis>getWebApplicationContext()</emphasis>. Below is an example of
      how you might use this in an Action:</para>

      <programlisting language="java">public class UserAction extends DispatchActionSupport {
499 500 501 502 503 504 505 506 507 508 509 510 511

    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");
    }
512 513 514 515 516 517 518 519 520
}</programlisting>

      <para>Spring includes subclasses for all of the standard Struts Actions
      - the Spring versions merely have <emphasis>Support</emphasis> appended
      to the name: <itemizedlist spacing="compact">
          <listitem>
            <para><ulink
            url="http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/web/struts/ActionSupport.html"><classname>ActionSupport</classname></ulink>,</para>
          </listitem>
521

522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539
          <listitem>
            <para><ulink
            url="http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/web/struts/DispatchActionSupport.html"><literal>DispatchActionSupport</literal></ulink>,</para>
          </listitem>

          <listitem>
            <para><ulink
            url="http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/web/struts/LookupDispatchActionSupport.html"><literal>LookupDispatchActionSupport</literal></ulink>
            and</para>
          </listitem>

          <listitem>
            <para><ulink
            url="http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/web/struts/MappingDispatchActionSupport.html"><literal>MappingDispatchActionSupport</literal></ulink>.</para>
          </listitem>
        </itemizedlist></para>

      <para>The recommended strategy is to use the approach that best suits
540 541 542 543
      your project. Subclassing makes your code more readable, and you know
      exactly how your dependencies are resolved. However, using the
      <classname>ContextLoaderPlugin</classname> allow you to easily add new
      dependencies in your context XML file. Either way, Spring provides some
544
      nice options for integrating the two frameworks.</para>
545 546 547 548 549
    </section>
  </section>

  <section id="webwork">
    <title>WebWork 2.x</title>
550 551 552 553

    <para>From the <ulink url="http://www.opensymphony.com/webwork/">WebWork
    homepage</ulink>...</para>

554
    <quote>
555 556 557 558 559 560
      <emphasis>WebWork is a Java web-application development framework. It is
      built specifically with developer productivity and code simplicity in
      mind, providing robust support for building reusable UI templates, such
      as form controls, UI themes, internationalization, dynamic form
      parameter mapping to JavaBeans, robust client and server side
      validation, and much more.</emphasis>
561
    </quote>
562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597

    <para>WebWork is (in the opinion of this author) a very clean, elegant web
    framework. Its architecture and key concepts are not only very easy to
    understand, it has a rich tag library, nicely decoupled validation, and it
    is (again, in the opinion of this author) quite easy to be productive in
    next to no time at all (the documentation and tutorials are pretty good
    too).</para>

    <para>One of the key enablers in WebWork's technology stack is <ulink
    url="http://www.opensymphony.com/webwork/wikidocs/IoC%20Overview.html">an
    IoC container</ulink> to manage Webwork Actions, handle the "wiring" of
    business objects, etc. Prior to WebWork version 2.2, WebWork used its own
    proprietary IoC container (and provided integration points so that one
    could integrate an IoC container such as Springs into the mix). However,
    as of WebWork version 2.2, the default IoC container that is used within
    WebWork <emphasis>is</emphasis> Spring. This is obviously great news if
    one is a Spring developer, because it means that one is immediately
    familiar with the basics of IoC configuration, idioms and suchlike within
    WebWork.</para>

    <para>Now in the interests of adhering to the DRY (Dont Repeat Yourself)
    principle, it would be foolish to writeup the Spring-WebWork integration
    in light of the fact that the WebWork team have already written such a
    writeup. Please do consult the <ulink
    url="http://www.opensymphony.com/webwork/wikidocs/Spring.html">Spring-WebWork
    integration page</ulink> on the <ulink
    url="http://wiki.opensymphony.com/display/WW/WebWork">WebWork wiki</ulink>
    for the full lowdown.</para>

    <para>Note that the Spring-WebWork integration code was developed (and
    continues to be maintained and improved) by the WebWork developers
    themselves, so in the first instance please do refer to the WebWork site
    and forums if you are having issues with the integration. Do feel free to
    post comments and queries regarding the Spring-WebWork integration on the
    <ulink url="http://forum.springframework.org/forumdisplay.php?f=25">Spring
    support forums</ulink> too.</para>
598 599 600 601
  </section>

  <section id="tapestry">
    <title>Tapestry 3.x and 4.x</title>
602 603 604 605

    <para>From the <ulink url="http://tapestry.apache.org/">Tapestry
    homepage</ulink>...</para>

606
    <quote>
607 608 609 610
      <emphasis>Tapestry is an open-source framework for creating dynamic,
      robust, highly scalable web applications in Java. Tapestry complements
      and builds upon the standard Java Servlet API, and so it works in any
      servlet container or application server.</emphasis>
611
    </quote>
612 613

    <para>While Spring has its own <link linkend="mvc">powerful web
T
Thomas Risberg 已提交
614
    layer</link>, there are a number of unique advantages to building a Java EE
615 616 617 618 619
    application using a combination of Tapestry for the web user interface and
    the Spring container for the lower layers. This section of the web
    integration chapter attempts to detail a few best practices for combining
    these two frameworks.</para>

T
Thomas Risberg 已提交
620
    <para>A <emphasis>typical</emphasis> layered Java EE application built with
621 622 623 624 625 626 627
    Tapestry and Spring will consist of a top user interface (UI) layer built
    with Tapestry, and a number of lower layers, all wired together by one or
    more Spring containers. Tapestry's own reference documentation contains
    the following snippet of best practice advice. (Text that the author of
    this Spring section has added is contained within <literal>[]</literal>
    brackets.)</para>

628
    <quote>
629 630 631 632 633 634
      <emphasis>A very succesful design pattern in Tapestry is to keep pages
      and components very simple, and <emphasis
      role="bold">delegate</emphasis> as much logic as possible out to
      HiveMind [or Spring, or whatever] services. Listener methods should
      ideally do little more than marshall together the correct information
      and pass it over to a service.</emphasis>
635
    </quote>
636 637 638 639 640 641 642 643 644 645 646 647 648 649 650

    <para>The key question then is... how does one supply Tapestry pages with
    collaborating services? The answer, ideally, is that one would want to
    dependency inject those services directly into one's Tapestry pages. In
    Tapestry, one can effect this dependency injection by a variety of
    means... This section is only going to enumerate the dependency injection
    means afforded by Spring. The real beauty of the rest of this
    Spring-Tapestry integration is that the elegant and flexible design of
    Tapestry itself makes doing this dependency injection of Spring-managed
    beans a cinch. (Another nice thing is that this Spring-Tapestry
    integration code was written - and continues to be maintained - by the
    Tapestry creator <ulink url="http://howardlewisship.com/blog/">Howard M.
    Lewis Ship</ulink>, so hats off to him for what is really some silky
    smooth integration).</para>

651 652
    <section id="tapestry-di">
      <title>Injecting Spring-managed beans</title>
653 654 655 656 657 658 659 660 661 662 663

      <para>Assume we have the following simple Spring container definition
      (in the ubiquitous XML format):</para>

      <programlisting language="xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       <emphasis role="bold">xmlns:jee="http://www.springframework.org/schema/jee"</emphasis>
       xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
<emphasis role="bold">http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd"</emphasis>&gt;
664
 
665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693
&lt;beans&gt;
    &lt;!-- the DataSource --&gt;
    &lt;jee:jndi-lookup id="dataSource" jndi-name="java:DefaultDS"/&gt;

    &lt;bean id="hibSessionFactory" 
          class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"&gt;
        &lt;property name="dataSource" ref="dataSource"/&gt;
    &lt;/bean&gt;

    &lt;bean id="transactionManager" 
          class="org.springframework.transaction.jta.JtaTransactionManager"/&gt;

    &lt;bean id="mapper" 
          class="com.whatever.dataaccess.mapper.hibernate.MapperImpl"&gt;
        &lt;property name="sessionFactory" ref="hibSessionFactory"/&gt;
    &lt;/bean&gt;

    &lt;!-- (transactional) AuthenticationService --&gt;
    &lt;bean id="authenticationService" 
          class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"&gt;
        &lt;property name="transactionManager" ref="transactionManager"/&gt;
        &lt;property name="target"&gt;
            &lt;bean class="com.whatever.services.service.user.AuthenticationServiceImpl"&gt;
                &lt;property name="mapper" ref="mapper"/&gt;
            &lt;/bean&gt;
        &lt;/property&gt;
        &lt;property name="proxyInterfacesOnly" value="true"/&gt;
        &lt;property name="transactionAttributes"&gt;
            &lt;value&gt;
694
                *=PROPAGATION_REQUIRED
695 696 697
            &lt;/value&gt;
        &lt;/property&gt;
    &lt;/bean&gt;  
698
 
699 700 701 702 703 704 705 706 707 708 709 710
    &lt;!-- (transactional) UserService --&gt;
    &lt;bean id="userService" 
          class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"&gt;
        &lt;property name="transactionManager" ref="transactionManager"/&gt;
        &lt;property name="target"&gt;
             &lt;bean class="com.whatever.services.service.user.UserServiceImpl"&gt;
                 &lt;property name="mapper" ref="mapper"/&gt;
             &lt;/bean&gt;
        &lt;/property&gt;
        &lt;property name="proxyInterfacesOnly" value="true"/&gt;
        &lt;property name="transactionAttributes"&gt;
            &lt;value&gt;
711
                *=PROPAGATION_REQUIRED
712 713 714
            &lt;/value&gt;
        &lt;/property&gt;
    &lt;/bean&gt;  
715
 
716 717 718 719 720 721 722 723 724 725 726 727 728 729 730
 &lt;/beans&gt;</programlisting>

      <para>Inside the Tapestry application, the above bean definitions need
      to be <link linkend="web-integration-common">loaded into a Spring
      container</link>, and any relevant Tapestry pages need to be supplied
      (injected) with the <literal>authenticationService</literal> and
      <literal>userService</literal> beans, which implement the
      <interfacename>AuthenticationService</interfacename> and
      <interfacename>UserService</interfacename> interfaces,
      respectively.</para>

      <para>At this point, the application context is available to a web
      application by calling Spring's static utility function
      <literal>WebApplicationContextUtils.getApplicationContext(servletContext)</literal>,
      where servletContext is the standard
T
Thomas Risberg 已提交
731
      <interface>ServletContext</interface> from the Java EE Servlet
732 733 734 735 736
      specification. As such, one simple mechanism for a page to get an
      instance of the <interfacename>UserService</interfacename>, for example,
      would be with code such as:</para>

      <programlisting language="java">WebApplicationContext appContext = WebApplicationContextUtils.getApplicationContext(
737 738
    getRequestCycle().getRequestContext().getServlet().getServletContext());
UserService userService = (UserService) appContext.getBean("userService");
739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754
<lineannotation>... some code which uses UserService</lineannotation></programlisting>

      <para>This mechanism does work... having said that, it can be made a lot
      less verbose by encapsulating most of the functionality in a method in
      the base class for the page or component. However, in some respects it
      goes against the IoC principle; ideally you would like the page to not
      have to ask the context for a specific bean by name, and in fact, the
      page would ideally not know about the context at all.</para>

      <para>Luckily, there is a mechanism to allow this. We rely upon the fact
      that Tapestry already has a mechanism to declaratively add properties to
      a page, and it is in fact the preferred approach to manage all
      properties on a page in this declarative fashion, so that Tapestry can
      properly manage their lifecycle as part of the page and component
      lifecycle.</para>

755
      <note>
756 757 758
        <para>This next section is applicable to Tapestry 3.x. If you are
        using Tapestry version 4.x, please consult the section entitled <xref
        linkend="tapestry-4-style-di" />.</para>
759
      </note>
760

761 762
      <section id="tapestry-pre4-style-di">
        <title>Dependency Injecting Spring Beans into Tapestry pages</title>
763 764 765 766 767 768 769 770 771 772 773 774 775 776 777

        <para>First we need to make the
        <interface>ApplicationContext</interface> available to the Tapestry
        page or Component without having to have the
        <interface>ServletContext</interface>; this is because at the stage in
        the page's/component's lifecycle when we need to access the
        <interface>ApplicationContext</interface>, the
        <interface>ServletContext</interface> won't be easily available to the
        page, so we can't use
        <literal>WebApplicationContextUtils.getApplicationContext(servletContext)</literal>
        directly. One way is by defining a custom version of the Tapestry
        <interfacename>IEngine</interfacename> which exposes this for
        us:</para>

        <programlisting language="java">package com.whatever.web.xportal;
778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800

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);
        }
    }
801 802 803 804 805 806 807 808 809 810 811
}</programlisting>

        <para>This engine class places the Spring Application Context as an
        attribute called "appContext" in this Tapestry app's 'Global' object.
        Make sure to register the fact that this special IEngine instance
        should be used for this Tapestry application, with an entry in the
        Tapestry application definition file. For example:</para>

        <programlisting language="xml"><lineannotation>file: xportal.application:</lineannotation>
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!DOCTYPE application PUBLIC 
812
    "-//Apache Software Foundation//Tapestry Specification 3.0//EN" 
813 814
    "http://jakarta.apache.org/tapestry/dtd/Tapestry_3_0.dtd"&gt;
&lt;application
815
    name="Whatever xPortal"
816 817
    engine-class="com.whatever.web.xportal.MyEngine"&gt;
&lt;/application&gt;</programlisting>
818
      </section>
819

820 821
      <section id="tapestry-componentdefs">
        <title>Component definition files</title>
822 823 824 825 826 827 828 829

        <para>Now in our page or component definition file (*.page or *.jwc),
        we simply add property-specification elements to grab the beans we
        need out of the <interfacename>ApplicationContext</interfacename>, and
        create page or component properties for them. For example:</para>

        <programlisting language="xml">    &lt;property-specification name="userService"
                            type="com.whatever.services.service.user.UserService"&gt;
830
        global.appContext.getBean("userService")
831 832 833
    &lt;/property-specification&gt;
    &lt;property-specification name="authenticationService"
                            type="com.whatever.services.service.user.AuthenticationService"&gt;
834
        global.appContext.getBean("authenticationService")
835 836 837 838 839 840 841 842
    &lt;/property-specification&gt;</programlisting>

        <para>The OGNL expression inside the property-specification specifies
        the initial value for the property, as a bean obtained from the
        context. The entire page definition might look like this:</para>

        <programlisting language="xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!DOCTYPE page-specification PUBLIC 
843
    "-//Apache Software Foundation//Tapestry Specification 3.0//EN" 
844
    "http://jakarta.apache.org/tapestry/dtd/Tapestry_3_0.dtd"&gt;
845
     
846
&lt;page-specification class="com.whatever.web.xportal.pages.Login"&gt;
847
 
848 849 850 851 852 853
    &lt;property-specification name="username" type="java.lang.String"/&gt;
    &lt;property-specification name="password" type="java.lang.String"/&gt;
    &lt;property-specification name="error" type="java.lang.String"/&gt;
    &lt;property-specification name="callback" type="org.apache.tapestry.callback.ICallback" persistent="yes"/&gt;
    &lt;property-specification name="userService"
                            type="com.whatever.services.service.user.UserService"&gt;
854
        global.appContext.getBean("userService")
855 856 857
    &lt;/property-specification&gt;
    &lt;property-specification name="authenticationService"
                            type="com.whatever.services.service.user.AuthenticationService"&gt;
858
        global.appContext.getBean("authenticationService")
859
    &lt;/property-specification&gt;
860
   
861
    &lt;bean name="delegate" class="com.whatever.web.xportal.PortalValidationDelegate"/&gt;
862
 
863 864 865 866
    &lt;bean name="validator" class="org.apache.tapestry.valid.StringValidator" lifecycle="page"&gt;
        &lt;set-property name="required" expression="true"/&gt;
        &lt;set-property name="clientScriptingEnabled" expression="true"/&gt;
    &lt;/bean&gt;
867
 
868 869 870 871 872
    &lt;component id="inputUsername" type="ValidField"&gt;
        &lt;static-binding name="displayName" value="Username"/&gt;
        &lt;binding name="value" expression="username"/&gt;
        &lt;binding name="validator" expression="beans.validator"/&gt;
    &lt;/component&gt;
873
   
874 875 876 877 878 879
    &lt;component id="inputPassword" type="ValidField"&gt;
        &lt;binding name="value" expression="password"/&gt;
       &lt;binding name="validator" expression="beans.validator"/&gt;
       &lt;static-binding name="displayName" value="Password"/&gt;
       &lt;binding name="hidden" expression="true"/&gt;
    &lt;/component&gt;
880
 
881
&lt;/page-specification&gt;</programlisting>
882
      </section>
883

884 885
      <section id="tapestry-getters">
        <title>Adding abstract accessors</title>
886 887 888 889 890 891 892

        <para>Now in the Java class definition for the page or component
        itself, all we need to do is add an abstract getter method for the
        properties we have defined (in order to be able to access the
        properties).</para>

        <programlisting language="java">// our UserService implementation; will come from page definition
893 894
public abstract UserService getUserService();
// our AuthenticationService implementation; will come from page definition
895 896 897 898 899 900
public abstract AuthenticationService getAuthenticationService();</programlisting>

        <para>For the sake of completeness, the entire Java class, for a login
        page in this example, might look like this:</para>

        <programlisting language="java">package com.whatever.web.xportal.pages;
901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941
 
/**
 *  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. 
942
     * &lt;p&gt;
943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011
     *  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));
        }
    }
1012
}</programlisting>
1013
      </section>
1014

1015
      <section id="tapestry-4-style-di">
1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038
        <title>Dependency Injecting Spring Beans into Tapestry pages -
        Tapestry 4.x style</title>

        <para>Effecting the dependency injection of Spring-managed beans into
        Tapestry pages in Tapestry version 4.x is <emphasis>so</emphasis> much
        simpler. All that is needed is a single <ulink
        url="http://howardlewisship.com/tapestry-javaforge/tapestry-spring/">add-on
        library</ulink>, and some (small) amount of (essentially boilerplate)
        configuration. Simply package and deploy this library with the (any of
        the) other libraries required by your web application (typically in
        <literal>WEB-INF/lib</literal>).</para>

        <para>You will then need to create and expose the Spring container
        using the <link linkend="web-integration-common">method detailed
        previously</link>. You can then inject Spring-managed beans into
        Tapestry very easily; if we are using Java 5, consider the
        <classname>Login</classname> page from above: we simply need to
        annotate the appropriate getter methods in order to dependency inject
        the Spring-managed <literal>userService</literal> and
        <literal>authenticationService</literal> objects (lots of the class
        definition has been elided for clarity)...</para>

        <programlisting language="java">package com.whatever.web.xportal.pages;
1039 1040 1041 1042 1043 1044 1045 1046 1047

public abstract class Login extends BasePage implements ErrorProperty, PageRenderListener {
    
    @InjectObject("spring:userService")
    public abstract UserService getUserService();
    
    @InjectObject("spring:authenticationService")
    public abstract AuthenticationService getAuthenticationService();

1048 1049 1050 1051 1052 1053 1054 1055 1056
}</programlisting>

        <para>We are almost done... all that remains is the HiveMind
        configuration that exposes the Spring container stored in the
        <interfacename>ServletContext</interfacename> as a HiveMind service;
        for example:</para>

        <programlisting language="xml">&lt;?xml version="1.0"?&gt;
&lt;module id="com.javaforge.tapestry.spring" version="0.1.1"&gt;
1057

1058
    &lt;service-point id="SpringApplicationInitializer"
1059
        interface="org.apache.tapestry.services.ApplicationInitializer"
1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089
        visibility="private"&gt;
        &lt;invoke-factory&gt;
            &lt;construct class="com.javaforge.tapestry.spring.SpringApplicationInitializer"&gt;
                &lt;set-object property="beanFactoryHolder"
                    value="service:hivemind.lib.DefaultSpringBeanFactoryHolder" /&gt;
            &lt;/construct&gt;
        &lt;/invoke-factory&gt;
    &lt;/service-point&gt;

    &lt;!-- Hook the Spring setup into the overall application initialization. --&gt;
    &lt;contribution
        configuration-id="tapestry.init.ApplicationInitializers"&gt;
        &lt;command id="spring-context"
            object="service:SpringApplicationInitializer" /&gt;
    &lt;/contribution&gt;

&lt;/module&gt;</programlisting>

        <para>If you are using Java 5 (and thus have access to annotations),
        then that really is it.</para>

        <para>If you are not using Java 5, then one obviously doesn't annotate
        one's Tapestry page classes with annotations; instead, one simply uses
        good old fashioned XML to declare the dependency injection; for
        example, inside the <literal>.page</literal> or
        <literal>.jwc</literal> file for the <classname>Login</classname> page
        (or component):</para>

        <programlisting language="xml">&lt;inject property="userService" object="spring:userService"/&gt;
&lt;inject property="authenticationService" object="spring:authenticationService"/&gt;</programlisting>
1090 1091
      </section>
    </section>
1092 1093 1094 1095 1096 1097 1098

    <para>In this example, we've managed to allow service beans defined in a
    Spring container to be provided to the Tapestry page in a declarative
    fashion. The page class does not know where the service implementations
    are coming from, and in fact it is easy to slip in another implementation,
    for example, during testing. This inversion of control is one of the prime
    goals and benefits of the Spring Framework, and we have managed to extend
T
Thomas Risberg 已提交
1099
    it all the way up the Java EE stack in this Tapestry application.</para>
1100 1101 1102 1103
  </section>

  <section id="web-integration-resources">
    <title>Further Resources</title>
1104 1105 1106 1107

    <para>Find below links to further resources about the various web
    frameworks described in this chapter.</para>

1108 1109
    <itemizedlist>
      <listitem>
1110 1111 1112
        <para>The <ulink
        url="http://java.sun.com/javaee/javaserverfaces/">JSF</ulink>
        homepage</para>
1113
      </listitem>
1114

1115
      <listitem>
1116 1117
        <para>The <ulink url="http://struts.apache.org/">Struts</ulink>
        homepage</para>
1118
      </listitem>
1119

1120
      <listitem>
1121 1122 1123
        <para>The <ulink
        url="http://www.opensymphony.com/webwork/">WebWork</ulink>
        homepage</para>
1124
      </listitem>
1125

1126
      <listitem>
1127 1128
        <para>The <ulink url="http://tapestry.apache.org/">Tapestry</ulink>
        homepage</para>
1129 1130 1131 1132
      </listitem>
    </itemizedlist>
  </section>
</chapter>