Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
IBM Rational Team Concert 2 Essentials

You're reading from   IBM Rational Team Concert 2 Essentials Improve your team productivity with Integrated Process, Planning, and Collaboration using Team Concert Enterprise Edition

Arrow left icon
Product type Paperback
Published in Feb 2011
Publisher Packt
ISBN-13 9781849681605
Length 308 pages
Edition 1st Edition
Languages
Arrow right icon
Toc

Table of Contents (20) Chapters Close

IBM Rational Team Concert 2 Essentials
Credits
About the Authors
Acknowledgement
About the Reviewers
www.PacktPub.com
Preface
1. Beginning with IBM RTC FREE CHAPTER 2. Installing RTC and WebSphere 3. Setting up the Project 4. Team and Source Control 5. Team Collaboration and Work Items 6. Development Process and Release Planning 7. Build Management 8. Extending RTC Quick Reference Installing the Express-C Edition with the Tomcat Server The BookManager Application Architecture What's New in RTC v3.0 Index

Architecture


For our sample BookManager project, we chose a mix of well-understood, mature, and widely available open source technologies to implement a standard n-tiered, web-based application. This is a typical JEE app that has a Presentation layer made with JSPs, a Services layer made with Struts actions, and a Persistence layer that uses Hibernate as a front-end to a Derby database. This architecture is shown in the following figure:

Let's examine each of these logical tiers.

Presentation tier

The client is presented as a series of web pages, generated by JSP, using the Apache Struts 2.x framework. Struts is a very mature set of servlets and JSP tag libraries that provides a classic MVC (Model-View-Controller) pattern for web-based presentation tiers written in Java. The look and feel is enhanced through the use of basic CSS (Cascading Style Sheets). You can learn more about Struts from the project's home page at struts.apache.org.

Services tier

The services are written as Java servlets, the majority of which are implemented as Struts Actions. This provides the controller piece of the MVC implementation of Apache Struts. The work of validating input, persisting the data, and retrieving them is handled by these servlets. This approach allows us to deploy our application on any Sun-compliant servlet container; in our case, Apache Tomcat.

Many of the features such as exception handling, file uploading, lifecycle callbacks, and validation are provided by Interceptors (these are conceptually the same as servlet filters) or the JDK's Proxy class. They provide a way to supply pre-processing and post-processing around an action.

The Struts framework is completely configurable via XML files.

Persistence tier

The data is stored in a relational database (RDBMS) that is abstracted by the open source Hibernate 3.x framework. Hibernate provides a simple object-relational (OR) mapping construct that makes it easy to persist Java beans in a relational database, without having to write Structured Query Language (SQL) or hand-coded mapping logic. The mapping is stored in easily modifiable and distributable XML files, which are read by Hibernate. In turn, Hibernate wraps the complexity of all database activity, including connecting, communicating, and performing typical Create, Read, Update, and Delete (CRUD) functions.

Hibernate is an abstraction of Persistence, which still requires an implementation of some sort. For our project, we are using the Apache Derby embedded database. Derby is a lightweight, completely pure Java database that can be bundled with an application and distributed as part of the final software package, without need for a separate installation and setup. Likewise, it is instantiated and used at run-time and thus, does not require a separate process that must be managed independent of BookManager Application. While this is suitable for our purposes, the configuration we're using does not scale, and must be replaced either with a server-based Derby install or another RDBMS system (such as MySQL, Oracle, and so on). Fortunately, Hibernate makes it easy to switch; a few changes to one of the configuration files and the inclusion of the proper JDBC library (that is, the library that facilitates communication with the database from Java) is all that's needed to migrate the BookManager Application to a different database product.

Control flow

The Struts framework is the backbone of the BookManager Application and we use it to specify the flow of control based on user actions. These are configured in a struts.xml file that describes which JSPs work with which Actions and under what conditions. The following is the Struts configuration XML source code:

<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"  "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
  <package name="default" extends="struts-default">
    <interceptors>
      <interceptor name="checkAuthentication" class="client.interceptor.LoginInterceptor" />
      <interceptor-stack name="booklookDefaultStackNoAuth">
        <interceptor-ref name="createSession"/>
        <interceptor-ref name="defaultStack"/>
      </interceptor-stack>
    </interceptors>
      
    <default-interceptor-ref name="booklookDefaultStack" />
      
    <!-- This section provides a single routing point for any errors thrown by the server, as well as forcing the user back to the login page in the event he is not authenticated. -->  
    <global-results>
      <result name="error">/error.jsp</result>
        <result name="login">/login.jsp</result>
    </global-results>
    <global-exception-mappings>
      <exception-mapping exception="org.apache.struts.register.exceptions.SecurityBreachException" result="securityerror" />
      <exception-mapping exception="java.lang.Exception" result="error" />
    </global-exception-mappings>
 
    <!-- This section maps the individual servlets to the pages that should be displayed as a result.  Each servlet is an action that corresponds to an activity the user can perform. The login action is different from the others in that if an error occurs, we display the login page again, rather than an error page --> 
    <action name="login" class="client.action.LoginAction">
      <interceptor-ref name="booklookDefaultStackNoAuth"/>
      <result name="success">welcome.jsp</result>
      <result name="error">login.jsp</result>
    </action>
    <action name="addbookscreen"class="client.action.AddBookScreenAction">
      <result>addbook.jsp</result>
    </action>
    <action name="addbook" class="client.action.AddBookAction">
      <result name="success" type="redirectAction">listbooks</result>
    </action>
    <action name="listbooks" class="client.action.ListBooksAction">
      <result>listbooks.jsp</result>
    </action>
    <action name="editbookscreen"class="client.action.EditBookScreenAction">
      <result>editbook.jsp</result>
    </action>
    <action name="editbook" class="client.action.EditBookAction">
      <result name="success" type="redirectAction">listbooks</result>
    </action>
    <action name="deletebook" class="client.action.DeleteBookAction">
      <result name="success" type="redirectAction">listbooks</result>
    </action>
    <action name="logout" class="client.action.LogoutAction">
      <interceptor-ref name="booklookDefaultStackNoAuth"/>
    </action>
  </package>

</struts>

Interceptors

Struts 2 has an interceptor feature that allows a developer to process the workflow of any Struts request, prior to it being served to the user. We added a LoginInterceptor class to the existing chain of Struts interceptors (defined by the line defaultStack) to check if a USER object is present in the HTTP session prior to each Struts action being served. If it is the case, we assume the user has authenticated via the login page and continue the user onto their originally requested action. If not, we bypass the original user's action and force him to the login.jsp page to enter his credentials. These credentials are sent to a LoginAction in Struts, which builds a BookManagerUser object and queries the Derby database via Hibernate to see if the user's login and password are a match. If so, it creates a USER session variable with a BookplaneUser Javabean object in it. This variable contains not only the user's login and SHA-encrypted password but also his role. Any user with a role of admin will be permitted to add and update book information; all other user roles can only view the book list.

In the current BookManager Application, the struts.xml file is configured with interceptors, global results, and actions. It is typical to have several interceptors assigned per action. As you can imagine, having to configure every interceptor for each action would quickly become extremely unmanageable. For this reason, interceptors can be grouped into named stacks. In our case, we've created an interceptor stack named booklookDefaultStackNoAuth that combines the out of the box defaultStack and createSession interceptors, and attaches them to the login and logout actions. The following is the code for the LoginInterceptor:

package client.interceptor;

import java.util.Map;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;

@SuppressWarnings("serial")
public class LoginInterceptor extends AbstractInterceptor {

   public String intercept(ActionInvocation actionInvocation) throwsException {
      Map<String, Object> session =ActionContext.getContext().getSession();
      Object booklookUserObject = session.get("USER");
       
      if (booklookUserObject == null) {     
         return Action.LOGIN;
      }
      return actionInvocation.invoke();
   }
}

From the above LoginInterceptor.java source, the class has a single method implementation, intercept(). Using custom interceptors in your application is an elegant way to provide cross-cutting application features. The AbstractInterceptor class provides a default no-op implementation of both the destroy as well as the init method.

To allow a user to logout, we added a LogoutAction and links to it on each page of the application. When a user selects this, his USER session variable is deleted, which in turn forces the LoginInterceptor to return the user to the login.jsp. As the entire BackplaneUser object is present in the session, we can access it from inside each of the JSPs. We use this to add the user's login name to a welcome message at the top of each screen in the application.

The LoginInterceptor is configured to run by default on all actions except the login and logout actions. This is because if we were to force a check on the login during a login, we'd wind up in an infinite loop! We provide this login-free path for the login and logout by defining an alternate interceptor stack that does not contain the LoginInterceptor and assigning it as the path for LoginAction and LogoutAction.

Actions

Actions are a fundamental concept in most web application frameworks and they are the basic unit of work that can be associated with an HTTP request coming from a browser. The very basic usage of an action is to perform work with a single result always being returned.

package client.action;

import java.util.Map;

import server.beans.BooklookUser;
import server.services.PasswordEncrypter;
import server.services.Persistence;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;

@SuppressWarnings("serial")
public class LoginAction extends ActionSupport {
   private String username;
   private String password;

   public String getUsername(){
      return username;
   }
   public void setUsername(String username) {
      this.username = username;
   }

   public String getPassword() {
      return password;
   }

   public void setPassword(String password){
      this.password = password;
   }
    
   public String execute() throws Exception {
      Map<String, Object> session =ActionContext.getContext().getSession();
        
      String encryptedPassword = PasswordEncrypter.encrypt(password);
      BooklookUser user = Persistence.getInstance().getUser(username,encryptedPassword);
      if (user == null) {
         session.put("LOGINSUCCESS", "false");
         return ERROR;
      }
      session.put("LOGINSUCCESS", "true");
      session.put("USER", user);
      return SUCCESS;
   }
}

From the above code listing of the LoginAction, the execute method gets the session, retrieves the user with that username-password, and finally sets the session. Thus, you can imagine the Action doing a piece of work from the execute method and returning a string.

You can see from the above struts.xml listing, the action name is associated to the action class, which is responsible for the execute method. Optionally, the interceptor reference name is also mentioned, which takes care of the additional orthogonal functionality. The interesting thing here is, if the result is a success, then welcome.jsp is invoked; otherwise the user is served login.jsp. Remember that the result of the execute() method of LoginAction is a String.

    <action name="login" class="client.action.LoginAction">
      <interceptor-ref name="booklookDefaultStackNoAuth"/>
      <result name="success">welcome.jsp</result>
      <result name="error">login.jsp</result>
    </action>

PasswordEncrypter and Persistence are two services that are used in most of our Action classes. More importantly, the Persistence class provides several APIs for retrieving the sessions, looking up users, and maintaining the book data.

Admin

Admin is a simple utility module for administering the users and their access to the BookManager Application. It is an executable JAR with a main class that accepts command line input, and makes static calls to a UserAdmin class. This class handles building the necessary Hibernate objects and adding or retrieving them from the Derby database via Hibernate calls.

The Admin utility needs to be run before the application war is actually deployed on to the servlet container, to create and populate the Derby database schema and add users and administrators for application access.

Flow summary

We have seen how different aspects of the Interceptors, Actions, and Admin module work together. Now, we will see how a single request to log in from the browser translates to different actions:

  1. The browser requests the login.action.

  2. The Filter Dispatcher of the Struts 2 framework looks at the request and determines the appropriate Action—in this case LoginAction (defined in the struts.xml file).

  3. Next, the Interceptors are applied. In this case, the booklookDefaultStackNoAuth interceptor is applied to the action (defined in the struts.xml file).

  4. Next, the Action method is executed. In our example, the appropriate method on the action login is executed to authenticate the user from the database and a welcome page (welcome.jsp) is shown. If the authentication fails, the user is shown an error message and the login page (login.jsp).

In case of other action such as addbook, the Action is executed and redirected to the listbooks action. You can observe that Actions and redirections are mentioned declaratively in the struts.xml configuration file:

<action name="addbook" class="client.action.AddBookAction">
  <result name="success" type="redirectAction">
    listbooks
  </result>
</action>
lock icon The rest of the chapter is locked
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime