X

Geertjan's Blog

  • November 13, 2005

Struts for the Complete Beginner (Part 3)

Geertjan Wielenga
Product Manager
In yesterday's instalment (and the one from the day before), all you ended up with was a (very simple) login form. But no application consists of only a login form. So, let's create something real with Struts—let's display some data from one of the PointBase databases that is bundled with the IDE. (By the way, the PointBase database server is also bundled with the IDE, together with the Tomcat Web Server that we'll be deploying to later.) And let's use a Struts datasource to make the connection. The nice thing about Struts datasources is that you can use them on any server—so, instead of having to create a new datasource per server (because a datasource is traditionally a server's resource and hence each one is different, depending on the server in question), we'll create one in struts-config.xml and then we'll be able to redeploy the application to a variety of servers, to show how portable Struts can make our application.

But, before we do all that, I've found out about a few other bits of functionality that Struts makes available. Now I know how to...

  • Localize the Buttons. So, until now, the buttons I created looked like this:

    <html:submit value="Login" />
    <html:cancel/>

    But it's better to do them like this:

    <html:submit><bean:message key="button.submit" /></html:submit>
    <html:cancel><bean:message key="button.cancel" /></html:cancel>

    And now add the display text to the ApplicationResource.properties file:

    button.submit=Login
    button.cancel=Cancel
  • Add Reset Functionality. A reset button is really simple to add. Here it is:

    <html:reset><bean:message key="button.reset" /></html:reset>

    And this is what loginForm.jsp now looks like:

    Now, whenever the Reset button is clicked, the value in the fields reverts to the values that they contained the last time the Login button was clicked.

  • Hide the Password Value. While typing in the password, you don't want someone looking over the user's shoulder and seeing the password. Hence, asterisks are used instead, as shown in the illustration above. No Java code is necessary for this in Struts. Just take a look at the JSP tags below and notice that while the name property is in a html:text tag, the password property is in a html:password tag:

  • Welcome the User. After providing a valid username and password, the user ends up in loginSuccessful.jsp. Currently, all it contains is a 'Logout' link (see yesterday's blog entry for details on this). However, you might want to spruce up this page by welcoming the user. Instead of providing a generic 'Login successful!' text, you might want to personalize and humanize things by saying 'Welcome, Ludwig van Beethoven!' (if, of course, the user's name happens to be Ludwig van Beethoven).

    To achieve this, you need to retrieve the user's name from the ActionForm Bean class. Again, this is the class that is the bridge between the JSP page and the Action class (it's really a 'backing bean'). To retrieve the user's name, you need to first add an attribute to the registered Action in struts-config.xml. You could have done this right at the start, when you were registering the action. When you look at the New Struts Action wizard in the IDE, there are a few odd-looking fields (click to enlarge):


    If some of my writer-colleagues were to see the above screenshot, they'd get really quite upset. They'd say things like: "How on earth is the user meant to know what 'Attribute' is for? And 'Parameter'? We should really provide more user-friendly texts for those!" While I agree with that, the unfortunate situation is that this is what they're called in the Struts world. That 'Attribute' field generates an attribute called attribute. Even though that's not very helpful, that's the way it is—and let's not confuse things by changing that in the IDE to something perfectly understandable yet totally unStrutslike.

    So, what's the point of the 'Attribute' field and the 'Parameter' field? I'm currently still ignorant about 'Parameter', but I do know what 'Attribute' is for. It pulls the ActionForm Bean into Scope (either Session or Request, depending on what you select in the dialog box above), so that we can do something useful with the values in Input Resource. In this case, the Input Resource is loginForm.jsp. We want to display 'Welcome, Ludwig van Beethoven!' (or whatever the user is called) in loginSuccessful.jsp. So, add an attribute to the /login path's action registration, as shown below (the addition is in bold below):

    <action input="/loginForm.jsp" 
    name="NewStrutsActionForm"
    path="/login"
    scope="request" attribute="validlogin"
    type="com.myapp.struts.NewStrutsAction">
    <forward name="success"
    path="/loginSuccessful.jsp"/>
    <forward name="cancel"
    path="/loginCancel.jsp"/>
    <exception key="message.java.lang.RuntimeException"
    path="/loginExceptions.jsp"
    type="java.lang.RuntimeException"/>
    </action>

    Now, in loginSuccessful.jsp we can add this line:

    <h1>Welcome, <bean:write name="validlogin" property="name"/>!</h1>

    So, when loginSuccessful.jsp opens, the ActionForm Bean class is called into scope (i.e., becomes accessible) via its name validLogin, and then its name property (i.e., the name typed into loginForm.jsp by the user) is displayed on the page. For example, if Ludwig van Beethoven's login was valid, this is what you'd see in loginSuccessful.jsp:

Now let's look at the data source. At the end of the following steps, you'll have a Struts datasource that accesses a PointBase sample database and displays the results in loginSuccessful.jsp. You'll use Struts tags to iterate through the results (in other words, there'll be no JSTL tags at all.) At that stage, your loginSuccessful.jsp will look like this:

So, to get the above result, do the following:

  1. Get the PointBase Database Driver. Each server that you want to use needs to have access to a PointBase database driver. For Tomcat, copy either pbclient.jar or pbembedded.jar from the PointBase installation directory to common/lib in the Tomcat installation directory. If you don't have PointBase, get the Sun Java System Application Server, because it includes PointBase. If you don't have the Sun Java System Application Server, get NetBeans IDE, because one of its bundled versions includes the Sun Java System Application Server which, in turn, includes PointBase.

  2. Add the Datasource to struts-config.xml. Here's my datasource, right at the top of struts-config.xml:

    <data-sources>
    <data-source type="org.apache.tomcat.dbcp.dbcp.BasicDataSource" key="empTable">
    <set-property property="driverClassName" value="com.pointbase.jdbc.jdbcUniversalDriver" />
    <set-property property="url" value="jdbc:pointbase://localhost:9092/sample" />
    <set-property property="username" value="pbpublic" />
    <set-property property="password" value="pbpublic" />
    <set-property property="validationQuery" value="SELECT \* FROM CUSTOMER_TBL ORDER BY upper(NAME)" />
    </data-source>
    </data-sources>

    By the way, when I tried to deploy my completed application to the Sun Java System Application Server and the JBoss Application Server, I found that the server didn't know how to handle the Struts datasource. That's because Tomcat comes with a bunch of libraries that the other servers don't include by default. Therefore, when using a different server, you need some other libraries, such as commons.logging on your classpath. (On the other hand, Tomcat is perfect for web applications, why would you want to use a heavy-duty application server for relatively simple Struts web applications?)

    From the datasource, you can see which sample database we'll be using. You can also have a look at it inside the IDE (click to enlarge):


  3. Create a JavaBean Class. Here's a simple POJO that represents the two fields ('name' and 'city') which we want to display:

    package com.myapp.struts;
    public class row {
    /\*\* Creates a new instance of Row \*/
    private String name;
    private String city;
    /\*\* Creates a new instance of Row \*/
    public row(String name, String city) {
    this.name = name;
    this.city = city;
    }
    public String getName() {
    return name;
    }
    public void setName(String name) {
    this.name = name;
    }
    public String getCity() {
    return city;
    }
    public void setCity(String city) {
    this.city = city;
    }
    }
  4. Create a DAO Action. Now we need to make a connection to the database via the datasource. This we do via a new action. All the code is below, most of it is standard database connection code, but where interesting things happen I've added a comment in bold.

    package com.myapp.struts;
    import javax.servlet.http.\*;
    import org.apache.struts.action.\*;
    import java.sql.\*;
    import java.util.ArrayList;
    import javax.sql.\*;
    import org.apache.struts.Globals;
    public class DatasourceConnectionAction extends Action {
    private DataSource dataSource;
    public ArrayList customerList = new ArrayList();
    private final static String SUCCESS = "success";
    public ActionForward execute(ActionMapping mapping, ActionForm form,
    HttpServletRequest request, HttpServletResponse response)
    throws Exception {
    HttpSession session = request.getSession();/\*\* Here the method that connects to the datasource is called: \*/
    customerList = getCustomers() ;/\*\* Here we put the customerList in scope, so that we can use it in the JSP page: \*/
    if(customerList != null){
    session.setAttribute("allMyCustomers" , customerList);
    }
    return (mapping.findForward(SUCCESS));
    }
    private ArrayList getCustomers(){
    Connection conn = null;
    Statement stmt = null;
    PreparedStatement prpStmt = null;
    ResultSet rs = null;
    StringBuffer resultString ;
    try{/\*\* Here 'empTable' maps to the datasource key defined in struts-config.xml: \*/
    dataSource = (DataSource)servlet.getServletContext().getAttribute("empTable");
    conn = dataSource.getConnection();
    String sqlQuery = "SELECT \* FROM CUSTOMER_TBL";
    prpStmt = conn.prepareStatement(sqlQuery);
    rs = prpStmt.executeQuery();
    /\*\* Here we put field 4 (the name) and field 7 (the city) in the customerList: \*/
    while (rs.next()) {
    customerList.add(new row(rs.getString(4), rs.getString(7)));
    }
    rs.close();
    } catch ( SQLException e ) {
    System.err.println("SQL Exception occured while accessing the table" );
    e.printStackTrace();
    return null;
    } catch ( Exception e ) {
    e.printStackTrace();
    return null;
    }
    return customerList;
    }
    }

    By the way, to test the above code, without having to go through loginForm.jsp each time, I added the following with the action-mapping in struts-config.xml:

    <action path="/MyDS" type="com.myapp.struts.DatasourceConnectionAction">
    <forward name="success" path="/mydatasourceSuccessful.jsp"/>
    </action>

    Then, when I deployed the application (after setting up the JSP page as shown in the next step), I was able to go directly to a new page called mydatasourceSuccessful.jsp. (Another way would be to change the scope variable to session.)

    Also, remember to change the action registration of the /login path in struts-config.xml so that your new action (the one above, which connects to the datasource) is called.

  5. Display the Result. At the top of the JSP page you need the following Struts taglib directives:

    <%@ taglib uri="http://jakarta.apache.org/struts/tags-logic" prefix="logic" %>
    <%@ taglib uri="http://jakarta.apache.org/struts/tags-bean" prefix="bean" %>
    <%@ taglib uri="http://jakarta.apache.org/struts/tags-html" prefix="html" %>

    Then, in the code, you need to use the logic prefix to iterate over the customers. The customers are exposed because of a very important part of DatasourceConnectionAction.java:

    if(customerList != null){
    session.setAttribute("allMyCustomers" , customerList);
    }

    Using the allMyCustomers attribute, you can now work in the JSP page with the returned data (one cool thing about Struts is that its tags are pretty self-explanatory):


And that's it! Now you can deploy the application and, when a correct user/password combination is entered (see SecurityManager.java for details on this, in yesterday's blog entry), the user is welcomed in loginSuccessful.jsp, and a table is shown with customers drawn from a sample PointBase database.

By the way, two of the best resources on Struts that I've found so far make use of Struts 1.1 and... NetBeans IDE 3.4! How cool is that? Have a look at part 1 here and part 2 here. Especially part 2 was very helpful while working on this blog entry.

Join the discussion

Comments ( 13 )
  • ab Monday, November 28, 2005
    i got this error as output after completing the code:
    HTTP Status 404 - Servlet action is not available
    --------------------------------------------------------------------------------
    type Status report
    message Servlet action is not available
    description The requested resource (Servlet action is not available) is not available.
  • Geertjan Monday, November 28, 2005
    Did you start the server? In NetBeans IDE, go to the Tools menu and start the server there...
  • Geertjan Monday, November 28, 2005
    I mean, the database server. PointBase.
  • Ed Wednesday, November 30, 2005
    I've set the session.setAttribute as
    ("com.sars.controller.action.select",arl);
    Where arl = my arraylist, which has items indeed.
    It goes wrong when trying to get the items displayed using my JSP page. It says: No getter method for property userID of bean com.sars.controller.action.select
    I know this means there is no getter method. Obviously I am executing a select which in turn is executed within a DAO.
    Here's a snippit <this is inside my class extends Action (Struts)>:
    StudentsDAO SDAO = new StudentsDAO();
    ArrayList arl = SDAO.getSelect();
    getSelect() is my method of getting the ArrayList result stored in the arl variable.
    My JSP contains:
    <logic:present name="com.sars.controller.action.select">
    <logic:iterate id="user" name="com.sars.controller.action.select">
    <tr>
    <td>
    <bean:write name="user" property="userID" />
    </td>
    <td>
    <bean:write name="user" property="password" />
    </td>
    </tr>
    </logic:iterate>
    </logic:present>
    I have no formbean, because I don't need a form. Consequence of this is, I don't have any setters/getters. I just want to call my jsp, which then invokes the action class which then calls my DAO and executes the select statement. Is it possible this way?
  • Ed Wednesday, November 30, 2005
    Euhm..
    My JSP code got lost..maybe its the HTML like tags??
  • John Himpel Tuesday, December 6, 2005
    Like ab, I get the same message in the browser.
    When I follow your instructions to start the PointBase server, I get a dialog box saying:
    No database instance registered.
    Register an instance of Sun Java Applicatioin Server 8.1
    I am using the builtin Tomcat server.
  • Geertjan Tuesday, December 6, 2005
    Yup. PointBase only works if you have a registered version of the Sun Java System Application Server (or if you have the PointBase driver in Tomcat's home directory). I suggest getting a version of NetBeans IDE 5.0 that includes the Sun Java System Application Server (they're bundled together, get it from the www.netbeans.org download page). Soon, when I've turned my Struts blogs into an 'official' NetBeans tutorial (i.e., one that is hosted on www.netbeans.org and that has the same look and feel as the other NetBeans tutorials), you'll get more detailed instructions on all of this -- including how to set up Tomcat to use PointBase with the Struts datasource.
  • james Thursday, December 15, 2005
    <data-sources>
    <data-source type="org.apache.tomcat.dbcp.dbcp.BasicDataSource" key="empTable">
    <set-property property="driverClassName" value="com.pointbase.jdbc.jdbcUniversalDriver" />
    <set-property property="url" value="jdbc:pointbase://localhost:9092/sample" />
    <set-property property="username" value="pbpublic" />
    <set-property property="password" value="pbpublic" />
    <set-property property="validationQuery" value="SELECT \* FROM CUSTOMER_TBL ORDER BY upper(NAME)" />
    </data-source>
    </data-sources>
    everytime i paste this at the top of my config file just as mentioned in the tutorial i got the error:
    TTP Status 404 - Servlet action is not available
    --------------------------------------------------------------------------------
    type Status report
    message Servlet action is not available
    description The requested resource (Servlet action is not available) is not available.
    i've started my pointbase database with no problem..so i really don't know what i'm doing wrong.
  • Sean Thursday, March 16, 2006
    I get the same problem as above, so what's the answer ? When validate in the stuts-config.xml I get
    Checking file:/C:/Documents%20and%20Settings/scoull/Desktop/New%20Folder/PasswordValidation/web/WEB-INF/struts-config.xml...
    The content of element type "action" must match "(icon?,display-name?,description?,set-property\*,exception\*,forward\*)". [50]
    XML validation finished.
    Why
  • Geertjan Thursday, March 16, 2006
    Which server are you using?
  • Geertjan Saturday, March 25, 2006
    From now on, if you encounter problems, please see this blog entry and the tutorial that is referred to there: Towards More Portable Web Applications.
  • gfg Monday, December 11, 2006
    fdhgfh
  • guest Tuesday, March 27, 2007
    222
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.