Friday, July 8, 2011

Struts2, Tiles2, Spring, Hibernate, JQuery Plugin - Tabbed Panel

Struts2, Tiles2, Spring, Hibernate, JQuery Plugin - Tabbed Panel

Disclaimer:
All the software applications that appear below represent trademarks and they are the property of their respective owners.
Use and implement my notes in this article at your own risk.

Discussion here:
1. General Considerations;
2. Prerequisites;
3. Tabbed Panel jQuery plugin component;
4. Dynamically add tabs to Tabbed Panel;
5. Struts 2 Actions;
6. DAOs;
7. Annotated Hibernate entities;

1. General Considerations:
So, I decided to put another sample based on the same basis as the previous 5 ones (see the March 2011 blog archive).
This sample is purposed to show how to use the Tabbed Panel component of jQuery plugin and to dynamically add tabs in order to access the countries and cities of a zone.
The sample will be presented somehow in a "top-down" fashion, ie. starting from View, then Controller, then Model components of the MVC web app architecture.


NOTE: if you don't know for example what is "blog" database, or other aspects, then you should read the "Struts2, Tiles2, Spring, Hibernate, JQuery plugin - General Considerations", which is used as a basis for this sample.

2. Prerequisites:
Make sure you have read the "Struts2, Tiles2, Spring, Hibernate, JQuery plugin - General Considerations", and installed and configured all that is written there;

3. Tabbed Panel jQuery plugin component:
The Tabbed Panel if contained in jQuery plugin which is represented by struts2-jquery-plugin-2.5.0.jar, so you have to install it on your local app path.
In order to use the Tabbed Panel from jQuery plugin, you will have to include the following line in your JSP file:
<<
<%@ taglib prefix="sj" uri="/struts-jquery-tags"%>
>>
Thus, you made the jQuery plugin available to be used in your JSP file; it contains also the Tabbed Panel component.

A Tabbed Panel may be written like this:
<<
    <sj:tabbedpanel id="tabs">
        <sj:tab id="tab1" target="div1" label="tab no 1"/>
        <sj:tab id="tab2" target="div2" label="tab no 2"/>
        <sj:tab id="tab3" target="div3" label="tab no 3"/>
        <div id="div1">
        ...
        </div>
        <div id="div2">
        ...
        </div>
        <div id="div3">
        ...
        </div>
    </sj:tabbedpanel>
>>
So, you have a tabbedpanel identified by "tabs" which contains 3 tabs, each tab with the title "tab no x" and the tab content specified in the associated DIV HTML element (div1, div2, div3). The functioning is somehow like this: when the user clicks on a tab, then all the DIVs are hidden and only the associated DIV is displayed.

4. Dynamically add tabs to Tabbed Panel
We assume we have a generated select HTML element that contains the list of zones; then, we have a tabbedpanel container in which we'll dinamically render the tabs containing countries and their cities.
The JSP code is:
<<
            <s:select id="selectZones" list="selectZones" listKey="cod" listValue="name" />
            <tr><td colspan="2"><sj:tabbedpanel id="tabbedCountries"></sj:tabbedpanel></td></tr>
>>
, where selectZones is the list of zones rendered by the default action and "tabbedCountries" is the tabbedpanel container.
Then, to render the tabs into "tabbedCountries", we'll use a jQuery function, named "populateTabs()" to which we send the value of the "selectZones" select:
<<
function populateTabs(selectZoneValue) {
    for (var k = $('#tabbedCountries').tabs('length') - 1; k >=0 ; k--)
        $('#tabbedCountries').tabs("remove", k);
    $.ajax({
        url: "selectCountries.action?code_zone=" + selectZoneValue,
        type: "POST",
        async: false,
        dataType: "json",
        success: function(data){
            var tab_counter = 2;
            for (var i = 0; i < data.length; i++) {
                var $tabs = $('#tabbedCountries').tabs({
                    tabTemplate: '<li><a href="#tabs_'+tab_counter+'">'+data[i].value+'</a></li>',
                    add: function(event, ui) {
                        var contentCity = "";
                        $.ajax({
                            url: "selectCities.action?code_country=" + data[i].key,
                            type: "POST",
                            async: false,
                            dataType: "json",
                            success: function(dataj){
                                for (var j = 0; j < dataj.length; j++)
                                    contentCity += dataj[j].value + "<br />";
                            }
                        });
                        $(ui.panel).append('<p>'+contentCity+'</p>');
                    }
                });
                var tab_title = data[i].key + ":"+data[i].value || 'Tab '+tab_counter;
                $tabs.tabs('add', '#tabs_'+tab_counter, tab_title);
                tab_counter++;
            }
         }
    });
}
>>
This function is called twice when jQuery loads:
- first, when the "selectZones" select is rendered, to initially render the tabbedpanel with initial selected value of "selectZones" select;
- second, at "selectZones" select change event;
<<
$(function() {
    $("#selectZones").unbind("change");
    populateTabs($("#selectZones").val());
    $("#selectZones").change(function() {
        populateTabs($(this).val());
    })
});
>>
Some things regarding the populateTabs() function:
- initially, it is assured that the "tabbedCountries" tabbedpanel container does not contain any tabs; this is important as the appending of a new country tab to be made at its associated zone;
- we call by AJAX an action that will return the countries list as a JSON object and we render a "tabTemplate" that contains the tab title and the link to the associated DIV content;
- on the "add" method of tabbedCountries.tabs we call another AJAX that will render the cities of the associated country;
- finally, we append the country and its cities to the tab associated DIV content;
- we increment the tab_counter, first initialized to 2, which is used internally by "tabbedCountries" to keep the index of tabs;

5. Struts 2 Actions:
There are 2 types of actions here:
- one that HTML renders the zones list;
- two that JSON renders the countires and cities list; these two are the same, so, I'll put the code just for the countries;
For the zones it is used the "blog-struts" package
<<
@ParentPackage("blog-struts")
>>
This will use the Struts "blog-struts" package defined in struts.xml, ie. it will extend the "struts-default" package of the Struts2; the "struts-default" package may also render tiles type results.

To populate the zones, you will use this code:
<<
@Override
    @Action(value="/index", results={
        @Result(name="success", location="page.selectzones", type="tiles"),
        @Result(name="input", location="page.empty", type="tiles")
    })
    public String execute() {
        try {
            setSelectZones((List<Zone>)zoneDAO.findAll());
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return ActionSupport.INPUT;
        }
        return ActionSupport.SUCCESS;
    }
>>

Here:
"selectZones" is the Java List which will be rendered by the "selectZones" select HTML element;
"zoneDAO" is the DAO (see "6. DAOs") managed by Spring over the Hibernate entities which contains methods as "findAll()" in order to obtain zones data from database;

Also, you may observe here annotations to define the "success" and "input" results (overcomes) of the action.

In the same action you should define getters and setters for "List<Zone> displayZoneList".


For the countries it is used the "blog-json" package
<<
@ParentPackage("blog-json")
>>
This will use the Struts "blog-json" package defined in struts.xml, ie. it will extend the "json-default" package of the Struts2; "json-default" uses "struts2-json-plugin-2.2.1.jar" to render a JSON result.

To populate the countries, you will use this code:
<<
@Override
    @Action(value="/selectCountries", results={
        @Result(name="success", type="json", params={"root", "selectCountryList"}),
        @Result(name="input", location="/jsp/empty.jsp")
    })
    public String execute() {
        try {
            if (code_zone != null && code_zone.trim().length() > 1) {
                    List<Country> listCountries = (List<Country>)countryDAO.findByProperty("zone.cod", getCode_zone());
                    selectCountryList = new ArrayList<KeyValuePairBean>();
                    for (Country country : listCountries) {
                        selectCountryList.add(new KeyValuePairBean(country.getCod(), country.getName()));
                    }
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return ActionSupport.INPUT;
        }
        return ActionSupport.SUCCESS;
    }
>>

Here:
"code_zone" is a String to which is passed the selected value of the "selectZones" select HTML element;
"selectCountryList" is the Java List which will be rendered on the respective tab of the "tabbedCountries" tabbedpanel;
"countryDAO" is the DAO (see "6. DAOs") managed by Spring over the Hibernate entities which contains methods as "findByProperty()" in order to obtain countries data from database;

Also, you may observe here annotations to define the "success" and "input" results (overcomes) of the action.

In the same action you should define getters and setters for "List<Country> selectCountryList" and for the "code_zone" String.

NOTE: to see how the Struts 2 Actions are accessed, and how their results are rendered, checkout also the "struts.xml" and "tiles.xml" files.

6. DAOs
DAOs, namely Data Access Objects, are used by Spring to obtain data from database via Hibernate.
DAOS are part of the business layer of a database web app.
A DAO class extends in this case the "HibernateDaoSupport" and contains methods to access the database via annotated Hibernate entities.
Any DAO should be described as a bean in the "applicationContext.xml" file in order to be seen by the sessionFactory; for example, for zones:
<<
    <bean id="ZoneDAO" class="blog.hibernate.dao.ZoneDAO">
        <property name="sessionFactory">
            <ref bean="sessionFactory" />
        </property>
    </bean>
>>

From Java point of view, a DAO should use a session manager, in this case, "HibernateDaoSupport" which is a Spring class that manages Hibernate sessions over annotated entities.

NOTE: a method that uses Hibernate session to get data from database uses HQL (Hibernate Query Language); to understand the HQL syntax you should google for HQL documentation.

7. Annotated Hibernate entities
An annotated entity is a POJO class that describes a database table by means of annotations.
It represents the Model from the MVC architecture.
For example, for the database table "zone", it was defined the Zone.java entity class; to refer table "zone" from database "blog", annotations are placed at the top of the class definition:
<<
@Entity
@Table(name = "zone", catalog = "blog")
>>
, while for each of table "zone" fields, there are specified on their value getters: primary key, foreign key, column name, field length, aso, if they exist.
Here are some examples of annotations, taken from Zone.java:
<<
    @Id
    @GeneratedValue
    @Column(name = "cod", unique = true, nullable = false, length = 4)
>>
<<
    @Column(name = "name", nullable = false, length = 45)
>>
<<
    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "zone")
>>

NOTE: if MyEclipse is used as IDE, you may generate both DAO and entity for each table by using Hibernate Reverse Engineering perspective.

10 comments:

  1. very nice tutorial,
    Can you please send me the complete code?
    thank you in advance (email: softcons99@yahoo.com)
    Kwame

    ReplyDelete
    Replies
    1. pls send me the code too...(dilawarkhanshadab@gmail.com)

      Delete
    2. will you pls send me the code..its very imp fr me.

      Delete
  2. hi Can you plz send me the code its very urgent plz send to this mail id raviteja.jayavarapu@gmail.com
    thank you....

    ReplyDelete
  3. Hi Can you send me the above code its very urgent plz send to this mail id raviteja.jayavarapu@gmail.com
    thank you ..

    ReplyDelete
  4. Hi Can you send me the above code its very urgent plz send to this mail id raviteja.jayavarapu@gmail.com
    thank you ..

    ReplyDelete
  5. Hi Can you send me the above code its very urgent plz send to this mail id raviteja.jayavarapu@gmail.com
    thank you ..

    ReplyDelete
  6. Hi all,
    I know there were guys that wanted the code and probably I couldn't send them the code in useful time. First of all I want to apologize to all these guys.
    Second, due to latest activity, I really have no time to maintain this blog, so I want you guys understand I CANNOT SEND THE CODE ANYMORE.
    In this case, you have 2 solutions:
    1) the main aspects for each sample are already posted on respective topics; practically, if you read the "general considerations" and the related topics, you will not need the code; you just need to configure a new app and paste there the code from the topics;
    2) if you really want the code, then ALL THE GUYS THAT PUT THEIR EMAILS ON THE COMMENTS ABOVE HAVE RECEIVED THE CODE; so, if I cannot respond, then maybe they can ;) in fact, it is a distributable code, isn't it ? ;)
    I hope you guys understand the above, thanx, vsorinel.

    ReplyDelete