Struts - Uppsala University · Struts is based on a servlet that is con gured from an XML- le. ouY...

36

Transcript of Struts - Uppsala University · Struts is based on a servlet that is con gured from an XML- le. ouY...

Struts

Struts

Struts is an application framework, i. e. a reusable semicomplete

application that can be used to produce custom applications.

It is maintained by the Apache Jakarta project and it is freeware.

Technically it is a set of Custom Tags, a core servlet and some utility

classes.

(2009-10-29 � 1.1 )

Struts is based on a servlet that is con�gured from an XML-�le. You use

the same servlet for all applications, you just con�gure it di�erently.

In addition it works with ActionForms, Actions, ActionForward and

ActionMappings. We have a structure like:

Initial page Action servlet

Action

form

Action

JSP

config

Struts

JSP

Figure: Struts components

(2009-10-29 � 1.2 )

An ActionForm is a bean that is used to store data. It has

instancevariables, getters and setters. It is created and populated by the

ActionServlet at request.

An Action is a Java class that implements the functionality of the request

An ActionForward is a location that control can be transferred into.

An ActionMapping describes how Actions and ActionForm are related,

what ActionForwards are used. It also maps them into physical locations.

(2009-10-29 � 1.3 )

When using Struts, the servlet captures all requests of a certain pattern.

The initial request is usually mapped into such a request. Each request is

mapped into an Action using an ActionMapping. When the servlet receives

a request, it looks up the Action, creates and populates the required

ActionForms and transfers control to the proper Action.

When the Action completes it transferres control to an ActionForward, i. e.

a logical location that is mapped into some physical page in the con�g-�le.

(2009-10-29 � 1.4 )

As an example to understand how Struts is setup, handles requests and

produce results we will study a simple login application. It contains an

initial page, a login form and a welcome page.

There are one ActionForm, one Action and a number of ActionForwardsthat are described in ActionMappings.

(2009-10-29 � 1.5 )

Our application is built by the following �les:

WEB-INF dist index.jsp pages

./WEB-INF:

classes struts-bean.tld struts-template.tld *struts-config.xml struts.tld

struts-form.tld *web.xml lib struts-html.tld src struts-logic.tld

/WEB-INF/classes:

app resources

./WEB-INF/classes/app:

*Constants.class *LogonForm.class *LogoffAction.class *UserDirectory.class

*LogonAction.class *UserDirectoryException.class

./WEB-INF/classes/resources:

*application.properties *users.properties

./WEB-INF/lib:

struts-config_1_0.dtd web-app_2_2.dtd struts_1_0_2.jar web-app_2_3.dtd

./WEB-INF/src:

build.xml java

(2009-10-29 � 1.6 )

./WEB-INF/src/java:

app resources

./WEB-INF/src/java/app:

*Constants.java *LogonForm.java *LogoffAction.java *UserDirectory.java

*LogonAction.java *UserDirectoryException.java

./WEB-INF/src/java/resources:

*application.properties *users.properties

./dist:

./pages:

*Logon.jsp *Welcome.jsp *struts-power.gif

We should provide the �les marked with a "*�, the others are part of the

Struts framework.

(2009-10-29 � 1.7 )

As usual we need a web.xml as the central con�guration �le. In a Strutsenvironment it setups servlet mappings, points out the separate Strutscon�guration �le and de�nes the Struts taglibs.

<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"

"http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">

<web-app>

(2009-10-29 � 1.8 )

<!-- Standard Action Servlet Configuration (with debugging) -->

<servlet>

<servlet-name>action</servlet-name>

<servlet-class>

org.apache.struts.action.ActionServlet

</servlet-class>

<init-param>

<param-name>application</param-name>

<param-value>resources.application</param-value>

</init-param>

<init-param>

<param-name>config</param-name>

<param-value>/WEB-INF/struts-config.xml</param-value>

</init-param>

<init-param>

<param-name>debug</param-name>

<param-value>2</param-value>

</init-param>

<init-param>

<param-name>detail</param-name>

<param-value>2</param-value>

</init-param>

<load-on-startup>2</load-on-startup>

</servlet>

(2009-10-29 � 1.9 )

<!-- Action Servlet Mapping -->

<servlet-mapping>

<servlet-name>action</servlet-name>

<url-pattern>*.do</url-pattern>

</servlet-mapping>

<!-- The Usual Welcome File List -->

<welcome-file-list>

<welcome-file>index.jsp</welcome-file>

</welcome-file-list>

<!-- Struts Tag Library Descriptors -->

<taglib>

<taglib-uri>/tags/struts-bean</taglib-uri>

<taglib-location>/WEB-INF/struts-bean.tld</taglib-location>

</taglib>

<taglib>

<taglib-uri>/tags/struts-html</taglib-uri>

<taglib-location>/WEB-INF/struts-html.tld</taglib-location>

</taglib>

<taglib>

<taglib-uri>/tags/struts-logic</taglib-uri>

<taglib-location>/WEB-INF/struts-logic.tld</taglib-location>

</taglib>

</web-app>

(2009-10-29 � 1.10 )

Then you need the special struts con�guration �le.

(2009-10-29 � 1.11 )

<?xml version="1.0" encoding="ISO-8859-1" ?>

<!DOCTYPE struts-config PUBLIC

"-//Apache Software Foundation//DTD Struts Configuration 1.0//EN"

"http://jakarta.apache.org/struts/dtds/struts-config_1_0.dtd">

<struts-config>

<!-- ======================================== Form Bean Definitions -->

<form-beans>

<form-bean name="logonForm" type="app.LogonForm"/>

</form-beans>

<!-- =================================== Global Forward Definitions -->

<global-forwards>

<forward name="logoff" path="/Logoff.do"/>

<forward name="logon" path="/Logon.do"/>

<forward name="welcome" path="/Welcome.do"/>

</global-forwards>

(2009-10-29 � 1.12 )

<!-- =================================== Action Mapping Definitions -->

<action-mappings>

<action path="/Welcome" type="org.apache.struts.actions.ForwardAction"

parameter="/pages/Welcome.jsp"/>

<action path="/Logon" type="org.apache.struts.actions.ForwardAction"

parameter="/pages/Logon.jsp"/>

<action path="/LogonSubmit" type="app.LogonAction" name="logonForm"

scope="request" validate="true" input="/pages/Logon.jsp">

<forward name="success" path="/pages/Welcome.jsp"/>

</action>

<action path="/Logoff" type="app.LogoffAction">

<forward name="success" path="/pages/Welcome.jsp"/>

</action>

</action-mappings>

</struts-config>

(2009-10-29 � 1.13 )

We now have de�ned our application, the remaing work is to write the

JSP's, html's and Java classes. In addition some property �les are used.

Application.properties:

welcome.title=Struts Logon Example Application

welcome.heading=Welcome!

welcome.message=To get started on your own application, copy the

struts-blank.war to a new WAR file using the name for

your application. Place it in your container's "webapp"

folder (or equivalent), and let your container auto-deploy

the application. Edit the skeleton configuration files as

needed, reload Struts or restart your container, and you are

on your way! (You can find the application.properties file

with this message in the <B>/WEB-INF/src/java /resources</B> folder.)

errors.footer=</UL><HR>

errors.header=<H3><FONT color="red">Validation Error</FONT>

</H3>You must correct the following error(s) before proceeding:<UL>

error.username.required=<LI>Username is required</LI>

error.password.required=<LI>Password is required</LI>

error.logon.invalid=<LI>Username and password provided not found in user directory.

Password must match exactly, including any lower or upper case characters.</LI>

error.logon.connect=<LI>Could not connect to user directory.</LI>

(2009-10-29 � 1.14 )

and users.properties

TED=Husted

CEDRIC=Dumoulin

GEORGE=Franciscus

DAVID=Winterfeldt

CRAIG=McClanahan

(2009-10-29 � 1.15 )

Now we are ready for the rest, we start with our welcome page,

index.jsp:

<%@ taglib uri="/tags/struts-logic" prefix="logic" %>

<logic:forward name="welcome"/>

<%--

Redirect default requests to Welcome action.

--%>

(2009-10-29 � 1.16 )

This immediately forwards control using an ActionForward that is mapped

in the con�g �les. Since welcome is mapped into Welcome.do, the request

will end up in the server according to the servlet mapping.

This will activate the Action Welcome that will start the page

pages/Welcome.jsp . So the servlet starts this page.

(2009-10-29 � 1.17 )

It goes like

<%@ taglib uri="/tags/struts-bean" prefix="bean" %>

<%@ taglib uri="/tags/struts-html" prefix="html" %>

<%@ taglib uri="/tags/struts-logic" prefix="logic" %>

<HTML>

<HEAD>

<TITLE>Welcome!</TITLE>

<html:base/>

</HEAD>

<BODY>

<logic:present name="user">

<H3>Welcome <bean:write name="user" property="username"/>!</H3>

</logic:present>

<logic:notPresent scope="session" name="user">

<H3>Welcome World!</H3>

</logic:notPresent>

<html:errors/>

<UL>

<LI><html:link forward="logon">Sign in</html:link></LI>

<logic:present name="user">

<LI><html:link forward="logoff">Sign out</html:link></LI>

</logic:present>

</UL>

<IMG src='struts-power.gif' alt='Powered by Struts'>

</BODY>

</HTML> (2009-10-29 � 1.18 )

<%--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.

--%>

If I am not logged in I only have one option, to log in. If I am logged in I

can log in once more or log out. To login, the we use an ActionForward

logon. The link tag in the JSP will expand into

http://localhost:8080/logon/Logon.do

that will take us into the servlet with Action Logon, which will start

Logon.jsp.

(2009-10-29 � 1.19 )

<%@ taglib uri="/tags/struts-html" prefix="html" %>

<HTML>

<HEAD>

<TITLE>Sign in, Please!</TITLE>

</HEAD>

<BODY>

<html:errors/>

<html:form action="/LogonSubmit" focus="username">

<TABLE border="0" width="100%">

<TR>

<TH align="right">Username:</TH>

<TD align="left"> <html:text property="username"/></TD>

</TR>

(2009-10-29 � 1.20 )

<TR>

<TH align="right">Password:</TH>

<TD align="left"><html:password property="password"/></TD>

</TR>

<TR>

<TD align="right"><html:submit/></TD>

<TD align="left"><html:reset/></TD>

</TR>

</TABLE>

</html:form>

</BODY>

</HTML>

<%--

Allow user to submit username and password to logon action.

--%>

(2009-10-29 � 1.21 )

This presents a form, when �lled in and submitted, the ActionForward

LogonSubmit . This is mapped into the Action LogonSubmit and requires

an ActionForm that should be created, populated and stored in the request

scope. My input should also be validated before it is used. This is done in

the bean.

It also de�nes an ActionForward success that can be used.

(2009-10-29 � 1.22 )

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;

/**

* Form bean for the user profile page.

* This form has the following fields,

* with default values in square brackets:

* <ul>

* <li><b>password</b> - Entered password value

* <li><b>username</b> - Entered username value

* </ul>

*

* @author Ted Husted

*/

(2009-10-29 � 1.23 )

public final class LogonForm extends ActionForm {

// ---------------------------------------Instance Variables

private String password = null;

private String username = null;

// ------------------------------------------ Properties

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;

}

//--------------------------------------- Public Methods

public void reset(ActionMapping mapping, HttpServletRequest request) {

setPassword(null);

setUsername(null);

}

(2009-10-29 � 1.24 )

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;

}

} // End LogonForm

(2009-10-29 � 1.25 )

The validation returns an ActionError (a HashMap). If that is neither null

nor empty, it will be saved by the controller in the reqeuest scope, and

control will be passed to the value of the input tag.

Otherwise we will enter the Action.

(2009-10-29 � 1.26 )

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;

/** * Implementation of <strong>Action</strong>

* that validates a user logon.

*

* @author Craig R. McClanahan

* @author Ted Husted

* @version $Revision: 1.1.1.1 $ $Date: 2002/08/15 15:50:55 $

*/

(2009-10-29 � 1.27 )

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));

}

(2009-10-29 � 1.28 )

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()));

}

(2009-10-29 � 1.29 )

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()));

}

(2009-10-29 � 1.30 )

// 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

(2009-10-29 � 1.31 )

Either we return to the input page or return success. The latter will take us

to /pages/Welcome.jsp that we have seen before. Since we are now logged

in, a logout option will also appear.

The last piece of this is the Logo� Action.

(2009-10-29 � 1.32 )

package app;

import java.io.IOException;

import java.util.Hashtable;

import java.util.Locale;

import java.util.Vector;

import javax.servlet.RequestDispatcher;

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.ActionForm;

import org.apache.struts.action.ActionForward;

import org.apache.struts.action.ActionMapping;

import org.apache.struts.action.ActionServlet;

import org.apache.struts.util.MessageResources;

/**

* Implementation of <strong>Action</strong> that processes a

* user logoff.

*j * @author Craig R. McClanahan

* @author Ted Husted

* @version $Revision: 1.1.1.1 $ $Date: 2002/08/15 15:50:55 $

*/

(2009-10-29 � 1.33 )

public final class LogoffAction extends Action {

public ActionForward perform(ActionMapping mapping, ActionForm form,

HttpServletRequest request, HttpServletResponse response)

throws IOException, ServletException {

// Extract attributes we will need

HttpSession session = request.getSession();

LogonForm user = (LogonForm) session.getAttribute(Constants.USER_KEY);

// Log this user logoff

if (user != null) {

if (servlet.getDebug() >= Constants.DEBUG) {

StringBuffer message = new StringBuffer("LogoffAction: User '");

message.append(user.getUsername());

message.append("' logged off in session ");

message.append(session.getId());

servlet.log(message.toString());

}

}

else {

if (servlet.getDebug() >= Constants.DEBUG) {

StringBuffer message = new StringBuffer("LogoffAction: User '");

message.append(session.getId());

servlet.log(message.toString());

}

}

(2009-10-29 � 1.34 )

// Remove user login

session.removeAttribute(Constants.USER_KEY);

// Return success

return (mapping.findForward(Constants.SUCCESS));

}

} // end LogoffAction

(2009-10-29 � 1.35 )

This removes the user and returns Success, which means Welcome.jsp. The

only parts missing now is the class that veri�es the login. See the course

homepage for the source of UserDirectory.

(2009-10-29 � 1.36 )