MailReader for Commons Chain

About this presentation

Commons Chain is breathing new life and flexibility into the Struts Request Processor.

But how can you use it in your own applications?

Agenda

Outgrowing the Request Processor

Introducing Commons Chain

Yet Another MailReader Example

Outgrowing the Struts Request Processor

Struts: Trying to be the “C” in MVC

Web controller processes request

Struts 1.0: ActionServlet methods

Struts 1.1: RequestProcessor methods – pluggable class

Request Processor steps

processMultipart(request)

processPath(request, response)

processLocale(request, response)

processContent(request, response);

processNoCache(request, response);

processPreprocess(request, response)

+ nine others

Extension Contention

TilesRequestProcessor

Standard extension

Struts Workflow

Contributor extension

WorkflowRequestProcessor

TilesWorkflowRequestProcessor

Extension Contention

“Composable” RequestProcessor

Start with original request processing

Add new methods/handlers

Subtract methods/handlers

Replace methods/handlers

Configurable through XML

Action Chaining

Struts Action – adaptor between presentation and business tier

Action represents complete transaction

Often devolves into an operation/task

People wanted to create “MoveAction

CopyAction

DeleteAction

Chain of Responsibility

Catalog of “Commands”

Command == Class == Method

Chain = Command + Chain + Command

Callers retrieve commands by name

Not aware if Command or Chain

Catalog

<catalog>

<command name=”copy” className=”myApp.copy”>

<command name=”delete” className=”myApp.move”>

<chain name=”move”>

<command className=”myApp.copy” />

<command className=”myApp.delete” />

</chain>

Command Signature

boolean execute(Context context) throws Exception;

Command Signature

boolean execute(Context context) throws Exception;

boolean

True = stop

False = continue

Context

Context extends Map

Command Context

Several concrete implementations

WebContext (abstract)

FacesContext – JavaServer Faces

PortletContext – JSR 168 Portlets

ServletContext – Standard web apps

ContextBase

It's a Map! – No, it's a JavaBean!

JavaBean properties exposed through Map.get

get(“date”) -> getDate()

Map.put honors JavaBean properties

set(“date”,date) -> setDate(date)

“Type-safe” Map

Now What?

Sounds neat – But how do I use it with the application I already got?

MailReader for Commons Chain

Original proof of concept

MailReader for JSF

MailReader for Commons Chain

UserDatabase

Open()

Save()

Close()

CreateUser(username:String): User

FindUser(username:String): User

FindUsers(): User[]

RemoveUser(user:User)

User (properties)

Username: String (readonly)

Password: String

FullName: String

FromAddress: String

ReplyToAddress: String

DataBase: UserDatabase

Subscription: Subscription[]

User (methods)

CreateSubscription(Host:String): Subscription

FindSubscription(Host:String):Subscription

RemoveSubscription(Subscription:Subcription)

Subscription

AutoConnect: Boolean

Host: String (readonly)

Type: String

Username: String

Password: String

User: User (readonly)

database.xml

<user username="user" fromAddress="John.User@somewhere.com" fullName="John Q. User" password="pass">

<subscription host="mail.hotmail.com" autoConnect="false" password="bar" type="pop3" username="user1234"> </subscription>

<subscription host="mail.yahoo.com" autoConnect="false" password="foo" type="imap" username="jquser"> </subscription>

</user>

Getting Started

LocaleAction

Struts maintains Locale object in session

An Action can pass Locale to Command

Command can change locale

The Action can update Struts state

LocaleChangeTest

public class LocaleChangeTest extends TestCase {

private Command command;

public void setUp() {

command = new LocaleChange();

}

LocaleChangeTest

public void testCanadaFrench() {
Locale original = Locale.US;
Locale expected = Locale.CANADA_FRENCH;
Context input = new ContextBase();
input.put(MailReader.PN_COUNTRY, "CA");
input.put(MailReader.PN_LANGUAGE, "FR");
MailReader context = new MailReader(original, input);
try {
command.execute(context);
} catch (Exception e) {
fail(e.getMessage());
}
assertEquals("Unexpected Locale", expected, context.getLocale());
}

MailReader (Context)

public class MailReader extends ContextBase {

public static String PN_COUNTRY = "country";
public static String PN_LANGUAGE = "language";

private Locale locale;
public Locale getLocale() {return locale;}
public void setLocale(Locale locale) {this.locale = locale;}

private Context input;
public Context getContext() {return context;}
public MailReader(Locale locale, Context input) {
super();
this.locale = locale;
this.input = input;
}

LocaleChange

public boolean execute(Context context) throws Exception {

MailReader app = (MailReader) context;
Context input = app.getInput();

String country = (String) input.get(MailReader.PN_COUNTRY);
String language = (String) input.get(MailReader.PN_LANGUAGE);

LocaleChange (Command)

Locale locale = Locale.getDefault();

if ((!isBlank(language)) && (!isBlank(country))) {
locale = new Locale(language, country);
} else if (!isBlank(language)) {
locale = new Locale(language, "");
}

app.setLocale(locale); // MailReader context

return false;
}

Story so far ...

LocaleChangeTest

MailReader context

Locale and Input properties

LocaleChangeCommand

Alters Locale property based on Input

MailReaderAction

Extract relevant state from web layer

Pass state to a Command

Conform state to Command result

Select resource to render response

MailReaderAction

public ActionForward execute ( ... ) throws Exception {

MailReader mailReader = getContext(mapping,form,request);

boolean stop = getCatalogCommand(mapping,form,request).execute(mailReader);

ActionForward location;
location = postExecute(mapping,form,request,response,mailReader);
if (location != null) return location;

return findSuccess(mapping);

}

MailReaderAction

getContext – Create MailReader context based on ActionForm and servlet contexts

getCatalogCommand – Retrieve command object from catalog, based on ActionForm name

postExecute – Conform ActionForm and servlet contexts; validate outcome

getContext

public MailReader getContext( ... ) throws Exception {

Locale locale = getLocale(request);

Context input = getInput(form);

UserDatabase database = (UserDatabase)
request.getSession().getServletContext().getAttribute
(Constants.DATABASE_KEY);

return new MailReader(locale, input, database);

}

getCatalogCommand

public Command getCatalogCommand(''' throws Exception {

Catalog catalog = (Catalog) request.getSession().getServletContext().getAttribute("catalog");

String name = mapping.getName();

return catalog.getCommand(name);

}

postExecute

public ActionForward postExecute( ... ) throws Exception {

conformInput(mapping,form,request,mailReader);
conformState(mapping,form,request,mailReader);
return null;
}

public void conformInput( ... ) throws Exception {
BeanUtils.copyProperties(form,mailReader.getInput());

}

public void conformState( ... ) throws Exception {
setLocale(request,mailReader.getLocale());
}

Keeping it Going

LoginAction

Application maintains User object in session

Action can pass credentials to Command

Command can query database

Action updates state of web session

LogonAction

public ActionForward checkState( ... ) throws Exception {

Object user = request.getSession().getAttribute(Constants.USER_KEY);
if (user==null) {
ActionMessages errors = new ActionMessages()
errors.add(
ActionMessages.GLOBAL_MESSAGE
new ActionMessage("error.password.mismatch"));
saveErrors(request,errors);
return findInput(mapping);
}
return null;

}

ChainListener

<context-param>
<param-name>org.apache.commons.chain.CONFIG_CLASS_RESOURCE</param-name>
<param-value>resources/catalog.xml</param-value>
</context-param>

<listener>
<listener-class>org.apache.commons.chain.web.ChainListener</listener-class>
</listener>

For more ...

Commons Chain MailReader

Jakarta Commons Sandbox

http://jakarta.apache.org/commons/sandbox/chain/

apps folder

Summary

We covered:

Outgrowing the Request Processor

Introducing Commons Chain

Yet Another MailReader Example

MailReader for Commons Chain

Ted Husted

2003-02-14T05:12:57