Monday, April 19, 2010

Struts 2 - Spring integration

An introduction and web application example

Recently, There has been a lot of noise about Dependency Injection and Inversion of Control based frameworks, most of this due to this article of Uncle Bob.
I consider article of Martin Fowler about above-mentioned pattern somewhat like a "manifest" of Inversion Control\Dependency Injection, so I invite you to accurately read it.
But now, what about real, pragmatic, occupational impact of this technology on a Java developer?
What I can see is a broad diffusion of Spring framework, that is a great tool in my opinion, but a giant apart universe, too. Spring gives to team of developers the ability to decoupling entire project in separate parts, each one independent from each other and several sub-teams can work singly, without or with very poor knowledge about work of collegues on other parts.
Typically, each team work on a single level of a classic three-tiers architecture:
  • Presentation layer
  • Business Logic layer
  • Physical layer

But this rule is not written anywhere. Spring framework gives great support on integration and decoupling of project shares, but it has a price! 
Gigantic overall configuration work (expecially if you integrate more frameworks\technologies), giant and multiple xml\properties files writing..well, in my humble opinion.. it seems a pachyderm to manage. Furthermore, code injected via xml is often difficult to debug. I am very sceptical that it is the panacea of all ills, the jolly to be used in every circumstance.
However, Spring helps very much in some cases, I never dream for a moment to criticize this wonderful product and the amazing work performed by community of developers.
My questions are: It is really helpful to you? Do you really save time and resources using that? How big your project is to justify that configuration and xml-read working?

For most project I worked on, Spring would involve to many resource and time to be spent and my response to above questions is No for all three  cases, BUT I am a very curious person and I like challenges; further more it is so required on market, that, sooner or later, I will massively use in professional environment. As they say on official documentation preface: "Spring is modular, allowing you to use only those parts that you need". Moreover I am very bewitched from Spring's Transaction Manager, that I think, in general, is a survival module for every web application project.


Anyway, this is not an article on Spring, neither on Dependency Injection. I am going to show how to integrate two useful frameworks: Struts 2 and Spring.
I assume that the reader knows both technologies, one is a MVC Framework (most used for web development), the other is a "lightweight solution" to develop java enterprise applications.
Googling I found several workarounds and some of that they works only with specific version of above frameworks.
I tried out almost all the most published solutions to take this task and I found this one, that I'm going to explain in this article, the more efficient and easy to implement.
I know, a service locator can be used for this purpose instead of dependency injection or, better, we can do all the work within Spring framework, using Web Flow, but I think Struts 2 is a great tool for developing presentation tier of web applications and I prefer to use it over Web Flow.

Let's go into practical side!

Wait wait!! ..marginal note: I switched to MyEclipse IDE since two months, I never loved Eclipse as IDE, too many configuration settings, not intuitive user interaction, very mnemonic use required, everywhere you click-right, a big menu will be open.. But MyEclipse rocks! Explosed deployment and plugins (dependency) management are features useful enough to motivate my emphasis on this tool.

Steps to be followed to configure your project for Spring and Struts2 support are simple in MyEclipse:
    just Right-click on project --> MyEclipse --> then first add Struts Capability, then Spring Capability. 
Remember to select version 2.1 of Struts core libraries and spring plugin (servlet default specification will be 2.5) and for Spring add 3.0 Core libraries and Web libraries specify the path where to put applicationContext.xml file: WEB-INF (but you can always manually move in future).

On other IDEs, I suppose there are other ways to add these supports to main project, the result, however, will be exactly this:



Fig. 1 Libraries included

and at the end of the file, first of tag </webapp> closing, write:

This snippet of code sets a listener to use Spring as Struts action manager.

<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class> </listener>

so web.xml file will look like this:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee   http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <filter>
        <filter-name>struts2</filter-name>
        <filter-class>
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <listener> <listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class> </listener> </web-app>

Now, let's create a Struts action; in our example it is used to retrieve some information from a service  on business layer and push it in a jsp.
Again, I am going to show only how to integrate two frameworks, so:

 - Jsp will be very ugly;
 - Project will not be extended to physical layer (no database use and interaction);

Tutorials on these missing features will be submitted in the near future (I am writing several articles on Hibernate and JQuery).

Our application uses a Struts Action to call an interface of a concrete implementation of a service and then, through action, it whows results to a JSP.
Here the UML code:



Struts Action code:
package it.nickg.webapp.web.actions;

import it.nickg.webapp.services.HelloService;

import java.util.List;

import com.opensymphony.xwork2.ActionSupport;

public class HelloAction extends ActionSupport {

    private HelloService helloService;
    private List words;

    public List getWords() {
        return words;
    }

    public void setWords(List words) {
        this.words = words;
    }

    public HelloService getHelloService() {
        return helloService;
    }

    public void setHelloService(HelloService helloService) {
        this.helloService = helloService;
    }

    public String execute() throws Exception {

        words = helloService.getWords();
        return SUCCESS;
    }
}


Struts2 apply dependency injection through setters, so every variable that you need to use in jsp, must have a setter (and I add getter too).

HelloService Interface code:
package it.nickg.webapp.services;

import java.util.List;

public interface HelloService {

    public abstract List getWords();

    // mandatory setter method for Spring's dependency injection
    public abstract void setWords(List words);
}

HelloServiceImpl, real service implementation:

package it.nickg.webapp.services.impl;

import it.nickg.webapp.services.HelloService;

import java.io.Serializable;
import java.util.List;

public class HelloServiceImpl implements Serializable, HelloService {

    private List words;

    public List getWords() {
        return words;
    }

    public void setWords(List words) {
        this.words = words;
    }
}

Spring applies injection to setter, too: when you need to return some variable from a method in middle-tier, just declare it as private and define a setter, then in xml spring's configuration file define values to be setted. Spring inject values and presentation tier doesn't need to know real implementation of business object.
Now edit struts.xml file to map the action created:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN" "http://struts.apache.org/dtds/struts-2.1.dtd">
<struts>
<package name="default" extends="struts-default">
<action name="hello" class="helloAction">
<result type="success">/result.jsp</result>
</action>
</package>
</struts>

Crucial  note: what allows to estabilish the link between Struts and Spring is here, class parameter of action must be set as id of spring's bean calling that action.

Add this code to applicationContext.xml file
<?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:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <bean id="helloService" class="it.nickg.webapp.services.impl.HelloServiceImpl">
        <property name="words">
            <list>
                <value>Hello </value>
                <value>World! </value>
                <value>This is </value>
                <value>a Struts - Spring </value>
                <value>integration example</value>
            </list>
        </property>
    </bean>
    
    <bean id="helloAction" class="it.nickg.webapp.web.actions.HelloAction">
        <property name="helloService" ref="helloService"/>
    </bean>
</beans>

Now we write JSPs. Let's give to index.jsp the role to redirect to our action:


<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<c:redirect url="hellopage.action"/>


and write the "success.jsp" in order to display data returned from action:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
    String path = request.getContextPath();
    String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()
            + path + "/";
%>
<%@ taglib prefix="s" uri="/struts-tags"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
    <head>
        <base href="<%=basePath%>">

        <title>My JSP 'success.jsp' starting page</title>

        <meta http-equiv="pragma" content="no-cache">
        <meta http-equiv="cache-control" content="no-cache">
        <meta http-equiv="expires" content="0">
        <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
        <meta http-equiv="description" content="This is my page">
        <!--
    <link rel="stylesheet" type="text/css" href="styles.css">
    -->

    </head>

    <body>
        <h3>This is only a sample</h3>
        <br>
        
        <s:iterator value="words">
            <s:property/>
        </s:iterator>
    </body>
</html>
When you run the application, it goes to index.jsp, redirected to hellopage.action and from here to success.jsp that displays an output like this:
In the next article, I will start from this application and I will extend it to physical layer, adding a database and an ORM implementation.
Stay tuned!

Tuesday, April 13, 2010

Java is fatherless now..

It's been a long time since I wrote last post and I'm planning to publish a new article on interesting things in next days.
Unfortunately a sad announcement (among other bad news) echoes in Java community:
our father, the man who gives us this powerful and amazing "thing" named Java (as weel as providing us possibility to have a job), decides to resign from Oracle.
Here is the link to his brand new blog.
We all looking forward to his next steps.