SE 452 Lecture Notes
Week 8: Introduction to Struts
Administrative [1/16]
Model View Controller [2/16]
Model 2 [3/16]
Struts [4/16]
Simple Struts App [5/16]
Setting up the app [6/16]
The ActionForm
[7/16]
The Action
[8/16]
struts-config.xml
[9/16]
Look how clean my view pages are :) [10/16]
Struts Pros and Cons [11/16]
Let's look at a more real-world example [12/16]
JSP code and configuration [13/16]
Java code [14/16]
build.xml
[15/16]
Homework and Reading [11/16]
Administrative [1/16]
- Still a lot of spots left open for the in class presentations
- Those will be graded with more leniency since you have to get up in front of your classmates
- Let's go over the homework 7
- Any questions before the quiz?
Model View Controller [2/16]
- What is it?
- GoF pattern
- Framework for building Smalltalk applications
- Seperates presentation from data
- Classic case: Say you wanted to display the same data as a pie chart and a bar chart
- The view is the chart, the model is the data
- GoF says the controller is how the user changes the data
- Use with Observer pattern, each view is an 'observer' to the model
- They are informed if the model changes
- Is this how HTTP works?
Model 2 [3/16]
- Sun described the pattern of using Servlets w/ JSP as Model 2
- Servlets handled the control flow, accessed data layers
- JSPs wrote the HTML
- Some pointed out this seemed close to MVC
- But, no Observer
Struts [4/16]
- An open source application framework
- Provides reusable solutions to problems that commonly occur in the presentation tier of J2EE applications
- An implementation of the MVC model 2 architecture
- But with a "pull" protocol, not a "push"
- Unfortunately, WebDev is an endless hack
- Why do we still use it?
- Additional features include:
- Extensive custom jsp tag libraries (reducing java code in jsp's)
- Built in support for internationalization
- Built in validation of user input
- xml based configuration
- Good API http://jakarta.apache.org/struts/api/index.html
Simple Struts App [5/16]
Struts from 30,000 feet would look like this:
- The Struts class
ActionServlet
controls navigation
- Another Struts class, the
Action
is used to access business classes
- When the
ActionServlet
receives a request from the container it uses the URI to determine which Action
it will use to handle the request
- An
Action
can validate input, access the BL, or query a data source
- All data in the request is given to the
Action
object in the form of a ActionForm
class which the ActionServlet
creates based on the attributes in the request object
- The
Action
class, upon completeing its business logic, returns the servlet an ActionForward
object, a logical representation of the path, to the next page
- All of these classes used by the servlet are bundled together in an
ActionMapping
object, The ActionServlet
knows which ActionMapping
to use based on the URI path of the request
- A
struts-config.xml
file is used to map all of these details
We'll look at a simple struts application, and then we'll see how each piece fits the MVC design pattern. Keep in the back of your mind this list of struts elements as they relate to MVC.
- ActionForward: A user gesture or view selection
- ActionForm: The data for a state change
- ActionMapping: The state change event
- ActionServlet: Configurable through xml.
The part of the controller that receives user gestures and state changes and issues view selections (dispatches all requests)
- Action classes: The part of the controller that interacts with the model to execute a state change or query and advises the ActionServlet of the next view to select
- ApplicationResources.properties: Stores localized messages and labels so that your application can be internationalized
- struts-config.xml: Stores the default configuration for the controller objects, which includes the user gestures, state changes, and state queries supported by your model
Setting up the app [6/16]
The files that are packaged in a war(web archive, click this link for more info)file. To open a war file simply place it in your webapps directory and the next time you start Tomcat it will expand. It's just a Jar file in a certain structure.
We'll do more on creating WARs through ant for our Struts apps in a bit. For now, let's just use this one register-complete.war
This WAR will also load the required Struts jar file to our lib and all the Struts TLDs. If you would rather install from scratch go here. http://jakarta.apache.org/struts/installation.html
Once deployed we can view our application here: http://localhost:8080/register-complete/register.jsp
The ActionForm
[7/16]
- We create forms by extending
org.apache.struts.action.ActionForm
- Our forms are just JavaBeans with a corresponding property for each field on an HTML form
- The ActionServlet will remove all parameters in the request object and calls the associated setter method
package app;
import org.apache.struts.action.*;
public class RegisterForm extends ActionForm {
protected String username;
protected String password1;
protected String password2;
public String getUsername () {return this.username;};
public String getPassword1() {return this.password1;};
public String getPassword2() {return this.password2;};
public void setUsername (String username) {this.username = username;};
public void setPassword1(String password) {this.password1 = password;};
public void setPassword2(String password) {this.password2 = password;};
}
The Action
[8/16]
- Extends
org.apache.struts.action.Action
- Gets incoming form from
ActionServlet
- Also gets
ActionMapping
(created by the container through the xml) so it can find appropriate forward
UserDirectory
we'll skip, but you have it in your WAR, assume this is any class accessing any data source or BL
package registerapp;
import org.apache.struts.action.*;
import javax.servlet.http.*;
import java.io.*;
public class RegisterAction extends Action {
public ActionForward perform (ActionMapping mapping,
ActionForm form,
HttpServletRequest req,
HttpServletResponse res) {
// (1) Cast the form to the RegisterForm
RegisterForm rf = (RegisterForm) form;
String username = rf.getUsername();
String password1 = rf.getPassword1();
String password2 = rf.getPassword2();
// (2) Apply business logic
if (password1.equals(password2)) {
try {
// (3) Return ActionForward for success
UserDirectory.getInstance().setUser(username,password1);
return mapping.findForward("success");
} catch (UserDirectoryException e) {
return mapping.findForward("failure");
}
}
// (4) Return ActionForward for failure
return mapping.findForward("failure");
}
}
struts-config.xml
[9/16]
- path: the container determines which action to use based on the path of the request
- the path will actually be register.do, but Struts sees and trims the .do and knows to go to the
ActionServlet
- This we define in web.xml
- name: matches the name of the form element with the one listed under
form-bean
- forward: your jsp's no longer need to keep track of where to go next, we can change how pages link together, and change there actual path w/o recompiling or changing any code
Look how clean my view pages are :) [10/16]
register.jsp
<%@ taglib uri="/WEB-INF/struts-form.tld" prefix="form" %>
UserName:
enter password:
re-enter password:
failure.html
FAILURE
Registration failed!
try again?
success.html
SUCCESS
Registration succeeded!
try another?
Struts Pros and Cons [11/16]
- Major Cons
- Requires an understanding of Struts components, not BlackBox
- Vendor does not offer support
- No data access support (could be a pro)
- JSP centric
- Major Pros
- Lightweigh, very fast, great object reuse
- Provides MVC style Controller and structure to otherwise klugey environment
- Great tag lib support
- Great documentation and Java doc
- Free and open source
- Strong community support
- Widely used and tested in industry
- Good i18n support
- Implementation detail collected in centralized configuration
- Model neutral (could be a con)
- Hides cumbersome implementation behind reusable logical names
- No longer have to do mindless, error prone things like parse values from a request object
- Strongly founded in design patterns
Let's look at a more real-world example [12/16]
- logon.war
- http://localhost:8080/logon/
- A valid login is username = "Ted" and password = "Husted" (Ted Husted provided this code)
- Uses Struts to:
- Writing links
- Writing forms
- Validating input
- Displaying error messages
- Repopulating forms
- Displaying alternative content
- Referencing images from dynamic pages
- Rewriting hyperlinks
JSP code and configuration [13/16]
- Looks up name in configuration, can redirect
index.jsp
<%@ taglib uri="/tags/struts-logic" prefix="logic" %>
<%--
Redirect default requests to Welcome action.
--%>
- html:base (regardless of where this was forwarded from, makes the base linking position the position of the jsp template)
- logic:present (looks for bean, works like if-else)
- html:link (works like key-value pair referenced in struts-config.xml)
Welcome.jsp
<%@ taglib uri="/tags/struts-bean" prefix="bean" %>
<%@ taglib uri="/tags/struts-html" prefix="html" %>
<%@ taglib uri="/tags/struts-logic" prefix="logic" %>
Welcome!
Welcome !
Welcome World!
<%--
If user is logged in, display "Welcome ${username}!"
Else display "Welcome World!"
Display link to log in page; maintain session id if needed.
If user is logged in, display a link to the sign-out page.
Note: Only the minimum required html or Struts custom tags
are used in this example.
--%>
struts-config.xml
- html:form can set the focus with JavaScript
- html:text maintains a reference to the bean, so on return can repopulate the field, if you don't want this feature you can set redisplay="false" as an attribute
- html:errors will display any
ActionErrors
objects returned
Logon.jsp
<%@ taglib uri="/tags/struts-html" prefix="html" %>
Sign in, Please!
<%--
Allow user to submit username and password to logon action.
--%>
Java code [14/16]
package app;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
public final class LogonForm extends ActionForm {
private String password = null;
private String username = null;
public String getPassword() {
return (this.password);
}
public void setPassword(String password) {
this.password = password;
}
public String getUsername() {
return (this.username);
}
public void setUsername(String username) {
this.username = username;
}
/*
* over-ridden from super class if, not required is scope is
* set to request in config, good for object re-use
*/
public void reset(ActionMapping mapping,
HttpServletRequest request) {
setPassword(null);
setUsername(null);
}
/*
* over-ridden from super class if, configuration in mapping is
* set to validate=true this method will be called
*
* sets to a key we have in a configuration file, html:errors will
* look up the key and print the error
*/
public ActionErrors validate(ActionMapping mapping,
HttpServletRequest request) {
ActionErrors errors = new ActionErrors();
if ((username == null) || (username.length() < 1))
errors.add("username",
new ActionError("error.username.required"));
if ((password == null) || (password.length() < 1))
errors.add("password",
new ActionError("error.password.required"));
return errors;
}
}
package app;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionServlet;
public final class LogonAction extends Action {
/**
* Validate credentials with business tier.
*
* @param username The username credential
* @param password The password credential
* @returns true if credentials can be validated
* @exception UserDirectoryException if cannot access directory
*/
public boolean isUserLogon(String username,
String password) throws UserDirectoryException {
return (UserDirectory.getInstance().isValidPassword(username,password));
// return true;
}
/**
* Login the user.
* The event is logged if the debug level is >= Constants.DEBUG.
*
* @param mapping The ActionMapping used to select this instance
* @param actionForm The ActionForm bean for this request (if any)
* @param request The HTTP request we are processing
* @param response The HTTP response we are creating
*
* @exception IOException if an input/output error occurs
* @exception ServletException if a servlet exception occurs
*/
public ActionForward perform(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
// Obtain username and password from web tier
String username = ((LogonForm) form).getUsername();
String password = ((LogonForm) form).getPassword();
// Validate credentials with business tier
boolean validated = false;
try {
validated = isUserLogon(username,password);
}
catch (UserDirectoryException ude) {
// couldn't connect to user directory
ActionErrors errors = new ActionErrors();
errors.add(ActionErrors.GLOBAL_ERROR,
new ActionError("error.logon.connect"));
saveErrors(request,errors);
// return to input page
return (new ActionForward(mapping.getInput()));
}
if (!validated) {
// credentials don't match
ActionErrors errors = new ActionErrors();
errors.add(ActionErrors.GLOBAL_ERROR,
new ActionError("error.logon.invalid"));
saveErrors(request,errors);
// return to input page
return (new ActionForward(mapping.getInput()));
}
// Save our logged-in user in the session,
// because we use it again later.
HttpSession session = request.getSession();
session.setAttribute(Constants.USER_KEY, form);
// Log this event, if appropriate
if (servlet.getDebug() >= Constants.DEBUG) {
StringBuffer message =
new StringBuffer("LogonAction: User '");
message.append(username);
message.append("' logged on in session ");
message.append(session.getId());
servlet.log(message.toString());
}
// Return success
return (mapping.findForward(Constants.SUCCESS));
}
} // End LogonAction
build.xml
[15/16]
- A build.xml you can run on ant on to compile and create a war file for the application we just went over
- Optional, you can just add the Struts jar and the TLDs and compile like you have been
Homework and Reading [16/16]
- You have 2 weeks for the homework given today (it is homework 8 and 9, so worth 200 points)
- We'll be going over more advanced features of Struts next week
- Plenty of resources, nothing required, but I highly recommend you take a look, perhaps go through a tutorial or two to get more comfortable http://jakarta.apache.org/struts/resources/index.html