> Now we don't have a centralized location to specify such configuration (or
> I'm not aware about it, like struts-config.xml), is this a reason people
> plug it with Spring. Meanwhile I'll try to study some of trails.
On the larger projects I have worked on, the team often come into conflict
with using struts-config - most people seem to want it most of the time,
especially at the start of the project. With Tapestry developers can at
least deal with well decoupled units of work.
Having said that I did start work on an idea to drive the navigation using
Drools. This would operate like struts-config, but would also have potential
to create far more complex navigation experiences, ideal for the more
sophisticated web shops, who need to tailor for a clients individual needs.
Of course, it does mean you have some untestable loosly bounds script doing
a significant job! This is satisfying from an architectural perspective,
because it farms out business logic, and lets the page be really simple. Nav
rules are really part of the business logic, yet another reason why Struts
is pants IMHO. Ideally pages should not contain any logic for what comes
next.
I started by extending BasePage, then you can implement a next page method
and let Drools handle the actual value used. Here is an example of the
good'ol max login attempt handling. Rather too simple to justify the method,
but just an example. I'm conscious now that this should be handled by a
hivemind service, but this code was written for Tapestry 3 standalone, and
is rather yukky. A much nicer approach would be for a simple service that
returns the name of the next page. The unpleasantry is in marshalling the
required data for the working memory - so that the method is not fully
decoupled. It would be nice for something to do that as required by the
rules.
You could also write your own digester to implement a struts-config like
navigation service. You could even copy the relivant parts of the
struts-config dtd. And you could make the digester accomodate multiple
files, each containing its own nav rules, and merge them, thus avoiding file
sharing conflicts. The Drools approach gives you far more flexibility
though.
John
public class DRLBasePage extends BasePage {
static URL url;
static RuleBase ruleBase;
WorkingMemory workingMemory;
static {
try {
url = DRLBasePage.class.getResource("cycle.drl");
ruleBase = RuleBaseBuilder.buildFromUrl(url);
} catch (Exception e) {e.printStackTrace();}
}
public DRLBasePage() {
workingMemory = ruleBase.newWorkingMemory();
}
// method to obtain the next page for the given working memory and cycle
public void nextPage(IRequestCycle cycle) throws FactException {
workingMemory.assertObject(this); // pass in this page object
workingMemory.setApplicationData("cycle", cycle); // pass in the cycle to
allow Drools to perform the forward
// you may want to load additional data into working memory here which sadly
starts to create loose coupling!
// a suggestion is to ensure those data are page properties instead, so they
can be picked up with the assert object method
workingMemory.fireAllRules(); // this will implement the next page nav
}
}
/*
* If the user enters valid credentials they are stored in the visit
* and an attempt is made to go to the next page.
*
*/
package examples;
import org.apache.tapestry.IRequestCycle;
import html.DRLBasePage;
/**
* Login page.
*/
public abstract class Login extends DRLBasePage
{
public abstract String getUserName();
public abstract String getPassword();
public abstract void setMessage(String message);
public void login(IRequestCycle cycle)
{
if (isValidLogin(getUserName(), getPassword()))
{
// Get the Visit and cast it to our application-specific
// class.
((Visit) getVisit()).doLogin(getUserName(), getPassword());
// use Drools to obtain next page
try {
nextPage(cycle);
} catch (Exception e) {e.printStackTrace();}
return;
}
// this sets the message property for the page to display
setMessage("invalid user name or password - attempt "
+ ((Visit) getVisit()).getLoginAttempts());
}
private boolean isValidLogin(String userName, String password)
{
((examples.Visit) getVisit()).incLoginAttempts();
return "tapestry".equalsIgnoreCase(userName);
}
}
// a problem with this method is the need of the page to load any data that
the rules need, so that there is some yukky loose coupling
// any ideas on how to get that code out much appreciated, but initially it
is best to use page properties
<?xml version="1.0"?>
<rule-set name="Cycle" xmlns="http://drools.org/rules"
xmlns:java="http://drools.org/semantics/java">
<rule name="login_success">
<parameter identifier="page">
<java:class>html.DRLBasePage</java:class>
</parameter>
<java:condition>page instanceof examples.Login</java:condition>
<java:condition>((examples.Visit) page.getVisit()).getLoginAttempts() <
4</java:condition>
<java:consequence>
((examples.Visit) page.getVisit()).logAccess();
cycle.activate("Main");
</java:consequence>
</rule>
<rule name="login_failure">
<parameter identifier="page">
<java:class>html.DRLBasePage</java:class>
</parameter>
<java:condition>page instanceof examples.Login</java:condition>
<java:consequence>
((examples.Login) page).setMessage("Too many login attempts!");
</java:consequence>
</rule>
</rule-set>
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]