The Artima Developer Community
Sponsored Link

Java Buzz Forum
Jakarta Struts De-Mystified Part 2

0 replies on 1 page.

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 0 replies on 1 page
John Topley

Posts: 233
Nickname: johntopley
Registered: Jul, 2003

John Topley is embarking on a journey to become a J2EE master.
Jakarta Struts De-Mystified Part 2 Posted: Jul 18, 2004 9:23 AM
Reply to this message Reply

This post originated from an RSS feed registered with Java Buzz by John Topley.
Original Post: Jakarta Struts De-Mystified Part 2
Feed Title: John Topley's Weblog
Feed URL: http://johntopley.com/posts.atom
Feed Description: John Topley's Weblog - some articles on Ruby on Rails development.
Latest Java Buzz Posts
Latest Java Buzz Posts by John Topley
Latest Posts From John Topley's Weblog

Advertisement

Last time I introduced this series of articles and the Web Forum application, and I explained what would and would not be covered. This time I'm going to cover the persistence and business object layers, and we'll roll our sleeves up and cut some code.


Persistence Layer

The persistence layer in the Web Forum application is deliberately very simple. There are two database tables, one for posts and another for users. The posts table stores topics and their replies. The users table stores the details of the registered users of the application. These tables are accessed using data access objects (DAOs), which are just Java objects that perform simple object-relational mapping. In other words, they take a Java object and persist its contents into a table and vice-versa.

My DAOs have the appropriate SQL statements embedded in them, although this probably isn't good practice because database administrators like such things to be externalised so they can tune them. I have also created an abstract superclass for all my DAOs, which contains utility methods that obtain a JDBC connection from a data source and close database resources etc.

This is the schema for the posts table:

FieldTypeNullableKeyExtra
PostIDint(10) unsignedPrimaryAuto increment
SubjecttextYes
ReplyCountint(10) unsigned
UserIDint(10) unsigned
CreationDatedatetime
Messagetext
ParentIDint(10) unsigned

—The most interesting feature of this table is the ParentID column. This links replies back to their parent topic. The Subject column is null when a post is a reply and not a topic. I'm storing the number of replies to each topic in a ReplyCount column. Technically this is redundant because it could be calculated, but I'm storing it because MySQL doesn't currently support nested SQL SELECT statements.

The CreationDate column originally auto-updated, but I changed this because the date and time of the original topic were getting updated to the current date and time whenever a reply was added to that topic.

This is the schema for the users table:

FieldTypeNullableKeyExtra
UserIDint(10) unsignedPrimaryAuto increment
Usernamevarchar(8)
Passwordvarchar(16)
Forenamesvarchar(64)
Surnamevarchar(64)
CreationDatedate

The MySQL SQL script to create these tables is in src/sql/create_tables.sql. This script can be run from a query window in MySQL Control Centre. Note that it assumes the existence of a database named webforum. Also, because we're not creating the functionality to create new topics in this article, you'll have to manually insert some test data so that you have some topics to display.


Business Object Layer

I didn't have to do much thinking to come up with the business objects in the application, as they're pretty obvious. There are classes for individual posts and users, as well as collections of posts and users. The class model—excluding the Struts classes—is shown below:

A section of the Web Forum class model. Click to view the entire model.
A diagram of the Web Forum class model

—Some of these classes—such as UserCookie—will be covered later in the series. As can be seen from the class model, a Posts class contains a collection of Post objects at runtime, and features methods for retrieving this collection and for adding a new post. The Post class itself has attributes corresponding to the columns in the posts table, and overloaded constructors which are invoked depending upon whether the post is a topic or a reply. Accessors and mutators (getters and setters) are not shown on the class model.

The Users class contains a collection of User objects at runtime, and features methods for retrieving this collection and for adding a new user. In fact, I made the getUsers method deprecated after I discovered that I'd coded it but didn't actually call it from anywhere! The User class has attributes mirroring the columns in the users table, as well as a convenience getDisplayName method that returns the user's forenames and surname with a space character in the middle.


Project Structure

The organisation of the source code folder tree is shown on the left.

The source code folder tree.
A picture of the source code folder tree shown in Windows Explorer

Java source code files go under src, and the Java package hierarchy is rooted at com.johntopley.webforum. The public_html folder corresponds to the root of the web application.

I always put JSPs under pages because then they can be protected by the web container using J2EE declarative security. Anything in public_html and its sub-folders really should be regarded as public.

I also like to separate the Struts configuration files out into a config folder, although usually you'll see them stored directly under WEB-INF.

The application entry point is public_html/index.jsp, which is declared as a welcome file in the web.xml web application deployment descriptor. Let's take a look at index.jsp:

<%@ taglib uri="http://jakarta.apache.org/struts/tags-logic" prefix="logic" %>
<%@ page session="false" %>
<logic:redirect forward="ViewTopics" />

—All this page does is transfer control to the Struts framework, because if we're going to use a framework then we want to be using it as soon as possible. Struts ships with a number of JSP tag libraries (taglibs) and here we're using the logic taglib, which handles the conditional generation of output text, looping over object collections and application flow management. In this case the redirect tag performs an HTTP redirect for us to a Struts logical URL. More about that in a moment.

One thing to note about the Web Forum application is that I'm using the Servlet 2.3 specification syntax for referencing the taglibs using URIs, rather than referring to TLD files in the web.xml file. This is documented in section 5.4.3 of The Struts User's Guide.


The Heart Of Struts

The heart of Struts is the config/struts-config.xml file. This file defines the flow of the application and tells Struts which classes to use for what. The Struts ActionServlet is a controller class that reads this file and receives all incoming requests for the web application. The ActionServlet needs to be configured as a servlet in the web.xml file:

<servlet-name>action</servlet-name>
  <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
  <init-param>
    <param-name>config</param-name>
    <param-value>/WEB-INF/config/struts-config.xml</param-value>
  </init-param>
  .
  .
  .

The main part of the struts-config.xml file covered by this article is:

<struts-config>
  <global-forwards>
    <forward name="ViewTopics" path="/ViewTopics.do"/>
  </global-forwards>

  <action-mappings>
    <action
      path="/ViewTopics"
      type="com.johntopley.webforum.controller.__
        action.ViewTopicsAction"
      scope="request">
      <forward
        name="Topics"
        path="/WEB-INF/pages/topics.jsp"
      />
    </action>
  </action-mappings>
</struts-config>

—Struts introduces a layer of indirection into web applications because it uses logical URLs. This means that the address you see in the browser's address bar does not correspond to the physical location of that resource on the web server. This allows developers to easily move resources around without breaking things. The Struts name for the association of a logical name with a resource is an ActionForward, often just called a Forward. The Struts configuration file contains a global-forwards section that allows Forwards to be configured that are available throughout a Struts application. These are effectively the application's entry points.

Another key Struts concept is the Action class. Actions are simply Java servlets, so anything a servlet can do, an Action class can do. Actions are used to process requests for specific URLs. Generally they should act as a thin layer around the business objects layer, which does the real work.

Actions are referred to by Action Mappings, which again, are logical URLs. The Struts convention is that Action Mappings end with .do. The web.xml file needs to be configured so that the Struts ActionServlet is used to process any URL matching the pattern *.do, as shown here:

<servlet-mapping>
  <servlet-name>action</servlet-name>
  <url-pattern>*.do</url-pattern>
</servlet-mapping>

Flow Of Events

To recap where we've got to, the index.jsp page redirects to the ViewTopics global ActionForward, and this passes control to the /ViewTopics Action. Struts knows which Action class to invoke because the type attribute in the Action Mapping gives the fully-qualified name of the associated Java class, which must inherit from org.apache.struts.action.Action, and must have an execute method with the following signature:

public ActionForward execute(ActionMapping mapping,
          ActionForm form,
          HttpServletRequest request,
          HttpServletResponse response) throws Exception

—The mapping parameter is an ActionMapping object representing the ActionMapping that invoked this Action. The form parameter is used if an HTML form is associated with the request. This topic will be covered later in this series. The request and response parameters highlight the fact that an Action class is just a specialisation of a servlet.

The Web Forum application uses a BaseAction abstract class that inherits from the org.apache.struts.action.Action class mentioned earlier. The other Action classes within the application inherit from BaseAction, so it serves as an extension point where functionality common to all Action classes can be added if required. This should be considered good practice.

Important: Action classes should not use instance variables, as they are not thread-safe. State should be shared by being passed as parameters to methods.

This is the execute method in ViewTopicsAction.java:

public ActionForward execute(ActionMapping mapping,
          ActionForm form,
          HttpServletRequest request,
          HttpServletResponse response) throws Exception
{
  request.setAttribute(KeyConstants.POST_LIST_KEY,
    new PostsDAO().getTopics());

  return mapping.findForward(ForwardConstants.TOPICS_PAGE);
}

—A new PostsDAO object is instantiated and its getTopics method called. This method uses the following SQL statement to query the posts table:

SELECT  p.PostID, p.Subject, p.ReplyCount, p.UserID, p.CreationDate
FROM  Posts p
WHERE  p.ParentID = 0
ORDER BY  p.CreationDate DESC

—The SQL WHERE clause ensures that only topics are selected. The getTopics method returns an instance of the Posts class i.e. an ordered collection of Post objects. This instance is stored in the HTTP request under the key referred to by the KeyConstants.POST_LIST_KEY constant. The JSP that displays the list of topics will use this Posts object stored in the request. Finally, the findForward method of the ActionMapping class is invoked. This takes a String parameter that is the name of a Struts Forward to pass control to. The ForwardConstants class contains all of the Forward names used within the Web Forum.

As well as global ActionForwards, Struts also has local ActionForwards. These are simply Forwards that are local in scope to a single Action Mapping. In other words, they are not globally visible within the application.

Important: Struts gives precedence to local ActionForwards over global ActionForwards.

A local Forward is used within the /ViewTopics Action Mapping to hold a reference to the JSP that displays the list of topics:

<forward name="Topics" path="/WEB-INF/pages/topics.jsp"/>

—After lots of indirection, we finally have a physical path to a page! Although topics.jsp is included with the source code downloads so you can see the list of topics, I'll leave the explanation of how it works until next time.


Configuring The MySQL Data Source

Before the application can be run using JDeveloper, a data source for the MySQL database needs to be created, using the steps given below:

  1. Copy mysql-connector-java-3.0.11-stable-bin.jar to <JDEVHOME>/jdk/jre/lib/ext.
  2. Add a global data source named WebForumDS to <JDEVHOME>/jdev/systemX.X.X.X.XXXX/oc4j-config/data-sources.xml as shown below:
<data-source
  name="WebForumDS"
  location="jdbc/WebForumCoreDS"
  ejb-location="jdbc/WebForumDS"
  xa-location="jdbc/xa/WebForumDS"
  inactivity-timeout="30"
  class="com.evermind.sql.DriverManagerDataSource"
  connection-driver="com.mysql.jdbc.Driver"
  username="username"
  password="password"
  url="jdbc:mysql://localhost:3306/webforum?autoReconnect=true"
/>

Source Code Downloads

To reduce the download sizes, I've split the Struts JAR files that go into WEB-INF/lib into a separate download.


Next Time

This installment has been quite a lot longer than I'd anticipated, but we've covered a lot of important ground. Next time we'll take a look at how the Topics page works and we'll add the code that allows the user to click on a topic to view that topic and any replies to it.

Read: Jakarta Struts De-Mystified Part 2

Topic: Understanding the Java Portlet Specification Previous Topic   Next Topic Topic: [Jul 9, 2004 14:29 PDT] 4 Links

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use